Skip to content

Commit cd7c0aa

Browse files
frifriSF59octavia-squidington-iiiedgao
authored
Update CDK to pass DestinationRecordRaw around (#55737)
Co-authored-by: Octavia Squidington III <[email protected]> Co-authored-by: Edward Gao <[email protected]>
1 parent ae6db5a commit cd7c0aa

File tree

20 files changed

+176
-131
lines changed

20 files changed

+176
-131
lines changed

airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/config/SyncBeanFactory.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import io.airbyte.cdk.load.command.DestinationConfiguration
99
import io.airbyte.cdk.load.command.DestinationStream
1010
import io.airbyte.cdk.load.message.BatchEnvelope
1111
import io.airbyte.cdk.load.message.ChannelMessageQueue
12-
import io.airbyte.cdk.load.message.DestinationRecordAirbyteValue
12+
import io.airbyte.cdk.load.message.DestinationRecordRaw
1313
import io.airbyte.cdk.load.message.MultiProducerChannel
1414
import io.airbyte.cdk.load.message.PartitionedQueue
1515
import io.airbyte.cdk.load.message.PipelineEvent
@@ -120,7 +120,7 @@ class SyncBeanFactory {
120120
@Named("recordQueue")
121121
fun recordQueue(
122122
loadStrategy: LoadStrategy? = null,
123-
): PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordAirbyteValue>>> {
123+
): PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordRaw>>> {
124124
return PartitionedQueue(
125125
Array(loadStrategy?.inputPartitions ?: 1) {
126126
ChannelMessageQueue(Channel(Channel.UNLIMITED))

airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/message/DestinationMessage.kt

+41
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import io.airbyte.cdk.load.command.DestinationStream
1111
import io.airbyte.cdk.load.data.AirbyteType
1212
import io.airbyte.cdk.load.data.AirbyteValue
1313
import io.airbyte.cdk.load.data.AirbyteValueDeepCoercingMapper
14+
import io.airbyte.cdk.load.data.EnrichedAirbyteValue
1415
import io.airbyte.cdk.load.data.IntegerValue
1516
import io.airbyte.cdk.load.data.StringValue
1617
import io.airbyte.cdk.load.data.TimestampWithTimezoneValue
@@ -143,6 +144,9 @@ data class DestinationRecord(
143144
serialized.length.toLong()
144145
)
145146
}
147+
fun asDestinationRecordRaw(): DestinationRecordRaw {
148+
return DestinationRecordRaw(stream, message, serialized)
149+
}
146150
}
147151

148152
/**
@@ -163,6 +167,43 @@ data class DestinationRecordAirbyteValue(
163167
val serializedSizeBytes: Long = 0L
164168
)
165169

170+
data class EnrichedDestinationRecordAirbyteValue(
171+
val stream: DestinationStream.Descriptor,
172+
val declaredFields: Map<String, EnrichedAirbyteValue>,
173+
val airbyteMetaFields: Map<String, EnrichedAirbyteValue>,
174+
val undeclaredFields: Map<String, JsonNode>,
175+
val emittedAtMs: Long,
176+
val meta: Meta?,
177+
val serializedSizeBytes: Long = 0L
178+
)
179+
180+
data class DestinationRecordRaw(
181+
val stream: DestinationStream.Descriptor,
182+
private val rawData: AirbyteMessage,
183+
private val serialized: String
184+
) {
185+
fun asRawJson(): JsonNode {
186+
return rawData.record.data
187+
}
188+
189+
fun asDestinationRecordAirbyteValue(): DestinationRecordAirbyteValue {
190+
return DestinationRecordAirbyteValue(
191+
stream,
192+
rawData.record.data.toAirbyteValue(),
193+
rawData.record.emittedAt,
194+
Meta(
195+
rawData.record.meta?.changes?.map { Meta.Change(it.field, it.change, it.reason) }
196+
?: emptyList()
197+
),
198+
serialized.length.toLong()
199+
)
200+
}
201+
202+
fun asEnrichedDestinationRecordAirbyteValue(): EnrichedDestinationRecordAirbyteValue {
203+
TODO()
204+
}
205+
}
206+
166207
data class DestinationFile(
167208
override val stream: DestinationStream.Descriptor,
168209
val emittedAtMs: Long,

airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/pipeline/DirectLoadPipelineStep.kt

+2-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
package io.airbyte.cdk.load.pipeline
66

7-
import io.airbyte.cdk.load.message.DestinationRecordAirbyteValue
7+
import io.airbyte.cdk.load.message.DestinationRecordRaw
88
import io.airbyte.cdk.load.message.PartitionedQueue
99
import io.airbyte.cdk.load.message.PipelineEvent
1010
import io.airbyte.cdk.load.message.QueueWriter
@@ -24,8 +24,7 @@ import jakarta.inject.Singleton
2424
class DirectLoadPipelineStep<S : DirectLoader>(
2525
val accumulator: DirectLoadRecordAccumulator<S, StreamKey>,
2626
@Named("recordQueue")
27-
val inputQueue:
28-
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordAirbyteValue>>>,
27+
val inputQueue: PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordRaw>>>,
2928
@Named("batchStateUpdateQueue") val batchQueue: QueueWriter<BatchUpdate>,
3029
@Value("\${airbyte.destination.core.record-batch-size-override:null}")
3130
val batchSizeOverride: Long? = null,

airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/pipeline/DirectLoadRecordAccumulator.kt

+3-6
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
package io.airbyte.cdk.load.pipeline
66

77
import io.airbyte.cdk.load.message.Batch
8-
import io.airbyte.cdk.load.message.DestinationRecordAirbyteValue
8+
import io.airbyte.cdk.load.message.DestinationRecordRaw
99
import io.airbyte.cdk.load.message.WithBatchState
1010
import io.airbyte.cdk.load.message.WithStream
1111
import io.airbyte.cdk.load.write.DirectLoader
@@ -25,15 +25,12 @@ data class DirectLoadAccResult(override val state: Batch.State) : WithBatchState
2525
@Requires(bean = DirectLoaderFactory::class)
2626
class DirectLoadRecordAccumulator<S : DirectLoader, K : WithStream>(
2727
val directLoaderFactory: DirectLoaderFactory<S>
28-
) : BatchAccumulator<S, K, DestinationRecordAirbyteValue, DirectLoadAccResult> {
28+
) : BatchAccumulator<S, K, DestinationRecordRaw, DirectLoadAccResult> {
2929
override fun start(key: K, part: Int): S {
3030
return directLoaderFactory.create(key.stream, part)
3131
}
3232

33-
override fun accept(
34-
record: DestinationRecordAirbyteValue,
35-
state: S
36-
): Pair<S, DirectLoadAccResult?> {
33+
override fun accept(record: DestinationRecordRaw, state: S): Pair<S, DirectLoadAccResult?> {
3734
state.accept(record).let {
3835
return when (it) {
3936
is Incomplete -> Pair(state, null)

airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/pipeline/InputPartitioner.kt

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
package io.airbyte.cdk.load.pipeline
66

7-
import io.airbyte.cdk.load.message.DestinationRecordAirbyteValue
7+
import io.airbyte.cdk.load.message.DestinationRecordRaw
88
import io.micronaut.context.annotation.Secondary
99
import jakarta.inject.Singleton
1010
import kotlin.math.abs
@@ -14,13 +14,13 @@ import kotlin.math.abs
1414
* partitioned by a hash of the stream name and namespace.
1515
*/
1616
interface InputPartitioner {
17-
fun getPartition(record: DestinationRecordAirbyteValue, numParts: Int): Int
17+
fun getPartition(record: DestinationRecordRaw, numParts: Int): Int
1818
}
1919

2020
@Singleton
2121
@Secondary
2222
class ByStreamInputPartitioner : InputPartitioner {
23-
override fun getPartition(record: DestinationRecordAirbyteValue, numParts: Int): Int {
23+
override fun getPartition(record: DestinationRecordRaw, numParts: Int): Int {
2424
return abs(record.stream.hashCode()) % numParts
2525
}
2626
}

airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/task/DestinationTaskLauncher.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import io.airbyte.cdk.load.command.DestinationStream
1111
import io.airbyte.cdk.load.message.BatchEnvelope
1212
import io.airbyte.cdk.load.message.ChannelMessageQueue
1313
import io.airbyte.cdk.load.message.CheckpointMessageWrapped
14-
import io.airbyte.cdk.load.message.DestinationRecordAirbyteValue
14+
import io.airbyte.cdk.load.message.DestinationRecordRaw
1515
import io.airbyte.cdk.load.message.DestinationStreamEvent
1616
import io.airbyte.cdk.load.message.MessageQueue
1717
import io.airbyte.cdk.load.message.MessageQueueSupplier
@@ -145,7 +145,7 @@ class DefaultDestinationTaskLauncher<K : WithStream>(
145145
// New interface shim
146146
@Named("recordQueue")
147147
private val recordQueueForPipeline:
148-
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordAirbyteValue>>>,
148+
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordRaw>>>,
149149
@Named("batchStateUpdateQueue") private val batchUpdateQueue: ChannelMessageQueue<BatchUpdate>,
150150
private val loadPipeline: LoadPipeline?,
151151
private val partitioner: InputPartitioner,

airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/task/internal/InputConsumerTask.kt

+5-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import io.airbyte.cdk.load.message.DestinationFile
1515
import io.airbyte.cdk.load.message.DestinationFileStreamComplete
1616
import io.airbyte.cdk.load.message.DestinationFileStreamIncomplete
1717
import io.airbyte.cdk.load.message.DestinationRecord
18-
import io.airbyte.cdk.load.message.DestinationRecordAirbyteValue
18+
import io.airbyte.cdk.load.message.DestinationRecordRaw
1919
import io.airbyte.cdk.load.message.DestinationRecordStreamComplete
2020
import io.airbyte.cdk.load.message.DestinationRecordStreamIncomplete
2121
import io.airbyte.cdk.load.message.DestinationStreamAffinedMessage
@@ -80,7 +80,7 @@ class DefaultInputConsumerTask(
8080
// Required by new interface
8181
@Named("recordQueue")
8282
private val recordQueueForPipeline:
83-
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordAirbyteValue>>>,
83+
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordRaw>>>,
8484
private val loadPipeline: LoadPipeline? = null,
8585
private val partitioner: InputPartitioner,
8686
private val openStreamQueue: QueueWriter<DestinationStream>
@@ -158,7 +158,7 @@ class DefaultInputConsumerTask(
158158
val manager = syncManager.getStreamManager(stream)
159159
when (val message = reserved.value) {
160160
is DestinationRecord -> {
161-
val record = message.asRecordMarshaledToAirbyteValue()
161+
val record = message.asDestinationRecordRaw()
162162
manager.incrementReadCount()
163163
val pipelineMessage =
164164
PipelineMessage(
@@ -310,7 +310,7 @@ interface InputConsumerTaskFactory {
310310

311311
// Required by new interface
312312
recordQueueForPipeline:
313-
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordAirbyteValue>>>,
313+
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordRaw>>>,
314314
loadPipeline: LoadPipeline?,
315315
partitioner: InputPartitioner,
316316
openStreamQueue: QueueWriter<DestinationStream>,
@@ -333,7 +333,7 @@ class DefaultInputConsumerTaskFactory(
333333

334334
// Required by new interface
335335
recordQueueForPipeline:
336-
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordAirbyteValue>>>,
336+
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordRaw>>>,
337337
loadPipeline: LoadPipeline?,
338338
partitioner: InputPartitioner,
339339
openStreamQueue: QueueWriter<DestinationStream>,

airbyte-cdk/bulk/core/load/src/main/kotlin/io/airbyte/cdk/load/write/DirectLoader.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
package io.airbyte.cdk.load.write
66

77
import io.airbyte.cdk.load.command.DestinationStream
8-
import io.airbyte.cdk.load.message.DestinationRecordAirbyteValue
8+
import io.airbyte.cdk.load.message.DestinationRecordRaw
99

1010
/**
1111
* [DirectLoader] is for the use case where records are loaded directly into the destination or
@@ -56,7 +56,7 @@ interface DirectLoader : AutoCloseable {
5656
* Called once per record until it returns [Complete], after which [close] is called, the loader
5757
* is discarded, and the records are considered processed by the platform.
5858
*/
59-
fun accept(record: DestinationRecordAirbyteValue): DirectLoadResult
59+
fun accept(record: DestinationRecordRaw): DirectLoadResult
6060

6161
/**
6262
* Called by the CDK to force work to finish. It will only be called if the last call to

airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/task/DestinationTaskLauncherTest.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import io.airbyte.cdk.load.command.MockDestinationConfiguration
1313
import io.airbyte.cdk.load.message.Batch
1414
import io.airbyte.cdk.load.message.BatchEnvelope
1515
import io.airbyte.cdk.load.message.CheckpointMessageWrapped
16-
import io.airbyte.cdk.load.message.DestinationRecordAirbyteValue
16+
import io.airbyte.cdk.load.message.DestinationRecordRaw
1717
import io.airbyte.cdk.load.message.DestinationStreamEvent
1818
import io.airbyte.cdk.load.message.MessageQueue
1919
import io.airbyte.cdk.load.message.MessageQueueSupplier
@@ -159,7 +159,7 @@ class DestinationTaskLauncherTest {
159159
destinationTaskLauncher: DestinationTaskLauncher,
160160
fileTransferQueue: MessageQueue<FileTransferQueueMessage>,
161161
recordQueueForPipeline:
162-
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordAirbyteValue>>>,
162+
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordRaw>>>,
163163
loadPipeline: LoadPipeline?,
164164
partitioner: InputPartitioner,
165165
openStreamQueue: QueueWriter<DestinationStream>,

airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/task/DestinationTaskLauncherUTest.kt

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import io.airbyte.cdk.load.message.Batch
1111
import io.airbyte.cdk.load.message.BatchEnvelope
1212
import io.airbyte.cdk.load.message.ChannelMessageQueue
1313
import io.airbyte.cdk.load.message.CheckpointMessageWrapped
14-
import io.airbyte.cdk.load.message.DestinationRecordAirbyteValue
14+
import io.airbyte.cdk.load.message.DestinationRecordRaw
1515
import io.airbyte.cdk.load.message.DestinationStreamEvent
1616
import io.airbyte.cdk.load.message.MessageQueue
1717
import io.airbyte.cdk.load.message.MessageQueueSupplier
@@ -95,14 +95,14 @@ class DestinationTaskLauncherUTest {
9595
private val openStreamQueue: MessageQueue<DestinationStream> = mockk(relaxed = true)
9696

9797
private val recordQueueForPipeline:
98-
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordAirbyteValue>>> =
98+
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordRaw>>> =
9999
mockk(relaxed = true)
100100
private val batchUpdateQueue: ChannelMessageQueue<BatchUpdate> = mockk(relaxed = true)
101101
private val partitioner: InputPartitioner = mockk(relaxed = true)
102102
private val updateBatchTaskFactory: UpdateBatchStateTaskFactory = mockk(relaxed = true)
103103

104104
private fun getDefaultDestinationTaskLauncher(
105-
useFileTranfer: Boolean,
105+
useFileTransfer: Boolean,
106106
loadPipeline: LoadPipeline? = null
107107
): DefaultDestinationTaskLauncher<Nothing> {
108108
return DefaultDestinationTaskLauncher(
@@ -124,7 +124,7 @@ class DestinationTaskLauncherUTest {
124124
updateCheckpointsTask,
125125
failStreamTaskFactory,
126126
failSyncTaskFactory,
127-
useFileTranfer,
127+
useFileTransfer,
128128
inputFlow,
129129
recordQueueSupplier,
130130
checkpointQueue,

airbyte-cdk/bulk/core/load/src/test/kotlin/io/airbyte/cdk/load/task/internal/InputConsumerTaskUTest.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import io.airbyte.cdk.load.data.ObjectTypeWithoutSchema
1010
import io.airbyte.cdk.load.message.CheckpointMessageWrapped
1111
import io.airbyte.cdk.load.message.DestinationMessage
1212
import io.airbyte.cdk.load.message.DestinationRecord
13-
import io.airbyte.cdk.load.message.DestinationRecordAirbyteValue
13+
import io.airbyte.cdk.load.message.DestinationRecordRaw
1414
import io.airbyte.cdk.load.message.DestinationStreamEvent
1515
import io.airbyte.cdk.load.message.MessageQueue
1616
import io.airbyte.cdk.load.message.MessageQueueSupplier
@@ -47,7 +47,7 @@ class InputConsumerTaskUTest {
4747
@MockK lateinit var fileTransferQueue: MessageQueue<FileTransferQueueMessage>
4848
@MockK
4949
lateinit var recordQueueForPipeline:
50-
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordAirbyteValue>>>
50+
PartitionedQueue<Reserved<PipelineEvent<StreamKey, DestinationRecordRaw>>>
5151
@MockK lateinit var partitioner: InputPartitioner
5252
@MockK lateinit var openStreamQueue: QueueWriter<DestinationStream>
5353

airbyte-integrations/connectors/destination-dev-null/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ plugins {
66
airbyteBulkConnector {
77
core = 'load'
88
toolkits = []
9-
cdk = 'local'
9+
cdk = '0.344'
1010
}
1111

1212
application {

airbyte-integrations/connectors/destination-dev-null/metadata.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ data:
1010
- suite: integrationTests
1111
connectorType: destination
1212
definitionId: a7bcc9d8-13b3-4e49-b80d-d020b90045e3
13-
dockerImageTag: 0.7.18
13+
dockerImageTag: 0.7.19
1414
dockerRepository: airbyte/destination-dev-null
1515
documentationUrl: https://docs.airbyte.com/integrations/destinations/dev-null
1616
githubIssueLabel: destination-dev-null

airbyte-integrations/connectors/destination-dev-null/src/main/kotlin/io/airbyte/integrations/destination/dev_null/DevNullDirectLoader.kt

+7-7
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
package io.airbyte.integrations.destination.dev_null
66

77
import io.airbyte.cdk.load.command.DestinationStream
8-
import io.airbyte.cdk.load.message.DestinationRecordAirbyteValue
8+
import io.airbyte.cdk.load.message.DestinationRecordRaw
99
import io.airbyte.cdk.load.write.DirectLoader
1010
import io.airbyte.cdk.load.write.DirectLoaderFactory
1111
import io.github.oshai.kotlinlogging.KotlinLogging
@@ -18,9 +18,9 @@ abstract class DevNullDirectLoader(
1818
) : DirectLoader {
1919
private var recordCount: Long = 0L
2020

21-
abstract fun acceptInner(record: DestinationRecordAirbyteValue)
21+
abstract fun acceptInner(record: DestinationRecordRaw)
2222

23-
override fun accept(record: DestinationRecordAirbyteValue): DirectLoader.DirectLoadResult {
23+
override fun accept(record: DestinationRecordRaw): DirectLoader.DirectLoadResult {
2424
acceptInner(record)
2525
return if (++recordCount % config.ackRatePerRecord == 0L) {
2626
DirectLoader.Complete
@@ -87,7 +87,7 @@ class LoggingDirectLoader(
8787
private var logCount: Long = 0L
8888
private val prng: Random = loggingConfig.seed?.let { Random(it) } ?: Random.Default
8989

90-
override fun acceptInner(record: DestinationRecordAirbyteValue) {
90+
override fun acceptInner(record: DestinationRecordRaw) {
9191
if (recordCount++ % loggingConfig.logEvery == 0L) {
9292
if (loggingConfig.sampleRate == 1.0 || prng.nextDouble() < loggingConfig.sampleRate) {
9393
if (++logCount < loggingConfig.maxEntryCount) {
@@ -101,15 +101,15 @@ class LoggingDirectLoader(
101101
}
102102

103103
class SilentDirectLoader(config: DevNullConfiguration) : DevNullDirectLoader(config) {
104-
override fun acceptInner(record: DestinationRecordAirbyteValue) {
104+
override fun acceptInner(record: DestinationRecordRaw) {
105105
/* Do nothing */
106106
}
107107
}
108108

109109
class ThrottledDirectLoader(config: DevNullConfiguration, private val millisPerRecord: Long) :
110110
DevNullDirectLoader(config) {
111111

112-
override fun acceptInner(record: DestinationRecordAirbyteValue) {
112+
override fun acceptInner(record: DestinationRecordRaw) {
113113
Thread.sleep(millisPerRecord)
114114
}
115115
}
@@ -123,7 +123,7 @@ class FailingDirectLoader(
123123

124124
private var messageCount: Long = 0L
125125

126-
override fun acceptInner(record: DestinationRecordAirbyteValue) {
126+
override fun acceptInner(record: DestinationRecordRaw) {
127127
if (messageCount++ > numMessages) {
128128
val message =
129129
"Failing Destination(stream=$stream, numMessages=$numMessages: failing at $record)"

airbyte-integrations/connectors/destination-s3-data-lake/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ plugins {
66
airbyteBulkConnector {
77
core = 'load'
88
toolkits = ['load-iceberg-parquet', 'load-aws']
9-
cdk = '0.343'
9+
cdk = '0.344'
1010
}
1111

1212
application {

airbyte-integrations/connectors/destination-s3-data-lake/metadata.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ data:
2626
alias: airbyte-connector-testing-secret-store
2727
connectorType: destination
2828
definitionId: 716ca874-520b-4902-9f80-9fad66754b89
29-
dockerImageTag: 0.3.16
29+
dockerImageTag: 0.3.17
3030
dockerRepository: airbyte/destination-s3-data-lake
3131
documentationUrl: https://docs.airbyte.com/integrations/destinations/s3-data-lake
3232
githubIssueLabel: destination-s3-data-lake

0 commit comments

Comments
 (0)