@@ -5,19 +5,22 @@ import io.airbyte.cdk.AirbyteConnectorRunnable
5
5
import io.airbyte.cdk.AirbyteConnectorRunner
6
6
import io.airbyte.cdk.AirbyteDestinationRunner
7
7
import io.airbyte.cdk.AirbyteSourceRunner
8
- import io.airbyte.cdk.ClockFactory
9
8
import io.airbyte.cdk.output.BufferingOutputConsumer
10
9
import io.airbyte.cdk.util.Jsons
11
10
import io.airbyte.protocol.models.v0.AirbyteMessage
12
11
import io.airbyte.protocol.models.v0.AirbyteStateMessage
13
12
import io.airbyte.protocol.models.v0.ConfiguredAirbyteCatalog
13
+ import io.micronaut.context.RuntimeBeanDefinition
14
+ import java.io.ByteArrayInputStream
15
+ import java.io.ByteArrayOutputStream
16
+ import java.io.InputStream
14
17
import java.nio.file.Files
15
18
import java.nio.file.Path
16
19
import kotlin.io.path.deleteIfExists
17
20
18
21
data object CliRunner {
19
22
/* *
20
- * Runs a source connector with the given arguments and returns the results .
23
+ * Builds a [CliRunnable] which runs a source connector with the given arguments.
21
24
*
22
25
* This is useful for writing connector integration tests:
23
26
* - the [config], [catalog] and [state] get written to temporary files;
@@ -26,61 +29,88 @@ data object CliRunner {
26
29
* - that file name gets passed with the test-only `--output` CLI argument;
27
30
* - [AirbyteSourceRunner] takes the CLI arguments and runs them in a new Micronaut context;
28
31
* - after it's done, the output file contents are read and parsed into [AirbyteMessage]s.
29
- * - those are stored in a [BufferingOutputConsumer] which is returned.
32
+ * - those are stored in the [BufferingOutputConsumer] which is returned in the [CliRunnable] .
30
33
*/
31
- fun runSource (
34
+ fun source (
32
35
op : String ,
33
36
config : ConfigurationJsonObjectBase ? = null,
34
37
catalog : ConfiguredAirbyteCatalog ? = null,
35
38
state : List <AirbyteStateMessage >? = null,
36
- ): BufferingOutputConsumer =
37
- runConnector(op, config, catalog, state) { args: Array <String > ->
38
- AirbyteSourceRunner (args)
39
- }
39
+ ): CliRunnable {
40
+ val out = CliRunnerOutputStream ()
41
+ val runnable: Runnable =
42
+ makeRunnable(op, config, catalog, state) { args: Array <String > ->
43
+ AirbyteSourceRunner (args, out .beanDefinition)
44
+ }
45
+ return CliRunnable (runnable, out .results)
46
+ }
40
47
41
- /* * Same as [runSource ] but for destinations. */
42
- fun runDestination (
48
+ /* * Same as [source ] but for destinations. */
49
+ fun destination (
43
50
op : String ,
44
51
config : ConfigurationJsonObjectBase ? = null,
45
52
catalog : ConfiguredAirbyteCatalog ? = null,
46
53
state : List <AirbyteStateMessage >? = null,
47
- ): BufferingOutputConsumer =
48
- runConnector(op, config, catalog, state) { args: Array <String > ->
49
- AirbyteDestinationRunner (args)
50
- }
54
+ inputStream : InputStream ,
55
+ ): CliRunnable {
56
+ val inputBeanDefinition: RuntimeBeanDefinition <InputStream > =
57
+ RuntimeBeanDefinition .builder(InputStream ::class .java) { inputStream }
58
+ .singleton(true )
59
+ .build()
60
+ val out = CliRunnerOutputStream ()
61
+ val runnable: Runnable =
62
+ makeRunnable(op, config, catalog, state) { args: Array <String > ->
63
+ AirbyteDestinationRunner (args, inputBeanDefinition, out .beanDefinition)
64
+ }
65
+ return CliRunnable (runnable, out .results)
66
+ }
67
+
68
+ /* * Same as the other [destination] but with [AirbyteMessage] input. */
69
+ fun destination (
70
+ op : String ,
71
+ config : ConfigurationJsonObjectBase ? = null,
72
+ catalog : ConfiguredAirbyteCatalog ? = null,
73
+ state : List <AirbyteStateMessage >? = null,
74
+ vararg input : AirbyteMessage ,
75
+ ): CliRunnable {
76
+ val inputJsonBytes: ByteArray =
77
+ ByteArrayOutputStream ().use { baos ->
78
+ for (msg in input) {
79
+ Jsons .writeValue(baos, msg)
80
+ baos.write(' \n ' .code)
81
+ }
82
+ baos.toByteArray()
83
+ }
84
+ val inputStream: InputStream = ByteArrayInputStream (inputJsonBytes)
85
+ return destination(op, config, catalog, state, inputStream)
86
+ }
51
87
52
- private fun runConnector (
88
+ private fun makeRunnable (
53
89
op : String ,
54
90
config : ConfigurationJsonObjectBase ? ,
55
91
catalog : ConfiguredAirbyteCatalog ? ,
56
92
state : List <AirbyteStateMessage >? ,
57
93
connectorRunnerConstructor : (Array <String >) -> AirbyteConnectorRunner ,
58
- ): BufferingOutputConsumer {
59
- val result = BufferingOutputConsumer (ClockFactory ().fixed())
94
+ ): Runnable {
60
95
val configFile: Path ? = inputFile(config)
61
96
val catalogFile: Path ? = inputFile(catalog)
62
97
val stateFile: Path ? = inputFile(state)
63
- val outputFile: Path = Files .createTempFile(null , null )
64
98
val args: List <String > =
65
99
listOfNotNull(
66
100
" --$op " ,
67
101
configFile?.let { " --config=$it " },
68
102
catalogFile?.let { " --catalog=$it " },
69
103
stateFile?.let { " --state=$it " },
70
- " --output=$outputFile " ,
71
104
)
72
- try {
73
- connectorRunnerConstructor(args.toTypedArray()).run<AirbyteConnectorRunnable >()
74
- Files .readAllLines(outputFile)
75
- .filter { it.isNotBlank() }
76
- .map { Jsons .readValue(it, AirbyteMessage ::class .java) }
77
- .forEach { result.accept(it) }
78
- return result
79
- } finally {
80
- configFile?.deleteIfExists()
81
- catalogFile?.deleteIfExists()
82
- stateFile?.deleteIfExists()
83
- outputFile.deleteIfExists()
105
+ val runner: AirbyteConnectorRunner = connectorRunnerConstructor(args.toTypedArray())
106
+ return Runnable {
107
+ try {
108
+ runner.run<AirbyteConnectorRunnable >()
109
+ } finally {
110
+ configFile?.deleteIfExists()
111
+ catalogFile?.deleteIfExists()
112
+ stateFile?.deleteIfExists()
113
+ }
84
114
}
85
115
}
86
116
0 commit comments