@tinybirdco/sdk 0.0.48 → 0.0.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -3
- package/dist/cli/commands/migrate.d.ts.map +1 -1
- package/dist/cli/commands/migrate.js +32 -0
- package/dist/cli/commands/migrate.js.map +1 -1
- package/dist/cli/commands/migrate.test.js +398 -1
- package/dist/cli/commands/migrate.test.js.map +1 -1
- package/dist/generator/pipe.d.ts.map +1 -1
- package/dist/generator/pipe.js +31 -1
- package/dist/generator/pipe.js.map +1 -1
- package/dist/generator/pipe.test.js +50 -1
- package/dist/generator/pipe.test.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/migrate/emit-ts.d.ts.map +1 -1
- package/dist/migrate/emit-ts.js +50 -9
- package/dist/migrate/emit-ts.js.map +1 -1
- package/dist/migrate/parse-datasource.d.ts.map +1 -1
- package/dist/migrate/parse-datasource.js +77 -49
- package/dist/migrate/parse-datasource.js.map +1 -1
- package/dist/migrate/parse-pipe.d.ts.map +1 -1
- package/dist/migrate/parse-pipe.js +254 -44
- package/dist/migrate/parse-pipe.js.map +1 -1
- package/dist/migrate/parser-utils.d.ts +5 -0
- package/dist/migrate/parser-utils.d.ts.map +1 -1
- package/dist/migrate/parser-utils.js +22 -0
- package/dist/migrate/parser-utils.js.map +1 -1
- package/dist/migrate/types.d.ts +22 -3
- package/dist/migrate/types.d.ts.map +1 -1
- package/dist/schema/datasource.test.js +1 -0
- package/dist/schema/datasource.test.js.map +1 -1
- package/dist/schema/pipe.d.ts +90 -3
- package/dist/schema/pipe.d.ts.map +1 -1
- package/dist/schema/pipe.js +84 -0
- package/dist/schema/pipe.js.map +1 -1
- package/dist/schema/pipe.test.js +70 -1
- package/dist/schema/pipe.test.js.map +1 -1
- package/package.json +1 -1
- package/src/cli/commands/migrate.test.ts +580 -1
- package/src/cli/commands/migrate.ts +35 -0
- package/src/generator/pipe.test.ts +56 -1
- package/src/generator/pipe.ts +41 -1
- package/src/index.ts +9 -0
- package/src/migrate/emit-ts.ts +52 -10
- package/src/migrate/parse-datasource.ts +82 -68
- package/src/migrate/parse-pipe.ts +359 -66
- package/src/migrate/parser-utils.ts +36 -1
- package/src/migrate/types.ts +25 -3
- package/src/schema/datasource.test.ts +1 -0
- package/src/schema/pipe.test.ts +89 -0
- package/src/schema/pipe.ts +188 -4
package/README.md
CHANGED
|
@@ -358,6 +358,7 @@ Behavior:
|
|
|
358
358
|
- Processes files, directories, and glob patterns.
|
|
359
359
|
- Continues through all matches and reports migratable resources plus per-file errors.
|
|
360
360
|
- In strict mode (default), exits with non-zero status if any errors are found.
|
|
361
|
+
- Infers parameter defaults from scalar SQL placeholders (for example `{{String(env, 'prod')}}`) while treating `{{Array(ids, 'String')}}` second arguments as element types, not defaults.
|
|
361
362
|
- Writes one output file by default: `./tinybird.migration.ts`.
|
|
362
363
|
|
|
363
364
|
### `tinybird dev`
|
|
@@ -529,14 +530,14 @@ In local mode:
|
|
|
529
530
|
### Connections
|
|
530
531
|
|
|
531
532
|
```typescript
|
|
532
|
-
import { defineKafkaConnection, defineS3Connection } from "@tinybirdco/sdk";
|
|
533
|
+
import { defineKafkaConnection, defineS3Connection, secret } from "@tinybirdco/sdk";
|
|
533
534
|
|
|
534
535
|
export const eventsKafka = defineKafkaConnection("events_kafka", {
|
|
535
536
|
bootstrapServers: "kafka.example.com:9092",
|
|
536
537
|
securityProtocol: "SASL_SSL",
|
|
537
538
|
saslMechanism: "PLAIN",
|
|
538
|
-
key:
|
|
539
|
-
secret:
|
|
539
|
+
key: secret("KAFKA_KEY"),
|
|
540
|
+
secret: secret("KAFKA_SECRET"),
|
|
540
541
|
});
|
|
541
542
|
|
|
542
543
|
export const landingS3 = defineS3Connection("landing_s3", {
|
|
@@ -741,6 +742,55 @@ export const manualReport = defineCopyPipe("manual_report", {
|
|
|
741
742
|
});
|
|
742
743
|
```
|
|
743
744
|
|
|
745
|
+
### Sink Pipes
|
|
746
|
+
|
|
747
|
+
Use sink pipes to publish query results to external systems. The SDK supports only Kafka and S3 sinks.
|
|
748
|
+
|
|
749
|
+
```typescript
|
|
750
|
+
import { defineSinkPipe, node } from "@tinybirdco/sdk";
|
|
751
|
+
import { eventsKafka, landingS3 } from "./connections";
|
|
752
|
+
|
|
753
|
+
// Kafka sink
|
|
754
|
+
export const kafkaEventsSink = defineSinkPipe("kafka_events_sink", {
|
|
755
|
+
sink: {
|
|
756
|
+
connection: eventsKafka,
|
|
757
|
+
topic: "events_export",
|
|
758
|
+
schedule: "@on-demand",
|
|
759
|
+
},
|
|
760
|
+
nodes: [
|
|
761
|
+
node({
|
|
762
|
+
name: "publish",
|
|
763
|
+
sql: `
|
|
764
|
+
SELECT timestamp, payload
|
|
765
|
+
FROM kafka_events
|
|
766
|
+
`,
|
|
767
|
+
}),
|
|
768
|
+
],
|
|
769
|
+
});
|
|
770
|
+
|
|
771
|
+
// S3 sink
|
|
772
|
+
export const s3EventsSink = defineSinkPipe("s3_events_sink", {
|
|
773
|
+
sink: {
|
|
774
|
+
connection: landingS3,
|
|
775
|
+
bucketUri: "s3://my-bucket/exports/",
|
|
776
|
+
fileTemplate: "events_{date}",
|
|
777
|
+
format: "csv",
|
|
778
|
+
schedule: "@once",
|
|
779
|
+
strategy: "create_new",
|
|
780
|
+
compression: "gzip",
|
|
781
|
+
},
|
|
782
|
+
nodes: [
|
|
783
|
+
node({
|
|
784
|
+
name: "export",
|
|
785
|
+
sql: `
|
|
786
|
+
SELECT timestamp, session_id
|
|
787
|
+
FROM s3_landing
|
|
788
|
+
`,
|
|
789
|
+
}),
|
|
790
|
+
],
|
|
791
|
+
});
|
|
792
|
+
```
|
|
793
|
+
|
|
744
794
|
### Static Tokens
|
|
745
795
|
|
|
746
796
|
Define reusable tokens for resource access control:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/migrate.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,eAAe,EAGhB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA2BD,wBAAsB,UAAU,CAC9B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,
|
|
1
|
+
{"version":3,"file":"migrate.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/migrate.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAEV,eAAe,EAGhB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,qBAAqB;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA2BD,wBAAsB,UAAU,CAC9B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,CAAC,eAAe,CAAC,CAmO1B"}
|
|
@@ -79,6 +79,7 @@ export async function runMigrate(options) {
|
|
|
79
79
|
const migrated = [];
|
|
80
80
|
const migratedConnectionNames = new Set();
|
|
81
81
|
const migratedDatasourceNames = new Set();
|
|
82
|
+
const parsedConnectionTypeByName = new Map(parsedConnections.map((connection) => [connection.name, connection.connectionType]));
|
|
82
83
|
for (const connection of parsedConnections) {
|
|
83
84
|
try {
|
|
84
85
|
validateResourceForEmission(connection);
|
|
@@ -121,6 +122,37 @@ export async function runMigrate(options) {
|
|
|
121
122
|
}
|
|
122
123
|
}
|
|
123
124
|
for (const pipe of parsedPipes) {
|
|
125
|
+
if (pipe.type === "sink") {
|
|
126
|
+
const sinkConnectionName = pipe.sink?.connectionName;
|
|
127
|
+
if (!sinkConnectionName || !migratedConnectionNames.has(sinkConnectionName)) {
|
|
128
|
+
errors.push({
|
|
129
|
+
filePath: pipe.filePath,
|
|
130
|
+
resourceName: pipe.name,
|
|
131
|
+
resourceKind: pipe.kind,
|
|
132
|
+
message: `Sink pipe references missing/unmigrated connection "${sinkConnectionName ?? "(none)"}".`,
|
|
133
|
+
});
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const sinkConnectionType = parsedConnectionTypeByName.get(sinkConnectionName);
|
|
137
|
+
if (!sinkConnectionType) {
|
|
138
|
+
errors.push({
|
|
139
|
+
filePath: pipe.filePath,
|
|
140
|
+
resourceName: pipe.name,
|
|
141
|
+
resourceKind: pipe.kind,
|
|
142
|
+
message: `Sink pipe connection "${sinkConnectionName}" could not be resolved.`,
|
|
143
|
+
});
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (sinkConnectionType !== pipe.sink?.service) {
|
|
147
|
+
errors.push({
|
|
148
|
+
filePath: pipe.filePath,
|
|
149
|
+
resourceName: pipe.name,
|
|
150
|
+
resourceKind: pipe.kind,
|
|
151
|
+
message: `Sink pipe service "${pipe.sink?.service}" is incompatible with connection "${sinkConnectionName}" type "${sinkConnectionType}".`,
|
|
152
|
+
});
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
124
156
|
if (pipe.type === "materialized" &&
|
|
125
157
|
(!pipe.materializedDatasource ||
|
|
126
158
|
!migratedDatasourceNames.has(pipe.materializedDatasource))) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../../src/cli/commands/migrate.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"migrate.js","sourceRoot":"","sources":["../../../src/cli/commands/migrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -105,7 +105,7 @@ export const eventsEndpoint = definePipe("events_endpoint", {
|
|
|
105
105
|
SELECT event_id, user_id, payload
|
|
106
106
|
FROM events
|
|
107
107
|
WHERE user_id = {{UInt64(user_id)}}
|
|
108
|
-
|
|
108
|
+
AND env = {{String(env, 'prod')}}
|
|
109
109
|
\`,
|
|
110
110
|
}),
|
|
111
111
|
node({
|
|
@@ -586,6 +586,260 @@ DATASOURCE events_state
|
|
|
586
586
|
expect(output).toContain("storeRawValue: true");
|
|
587
587
|
expect(output).toContain('engine: engine.replacingMergeTree({ sortingKey: "event_id", ver: "version_ts", isDeleted: "_is_deleted" })');
|
|
588
588
|
});
|
|
589
|
+
it("migrates TYPE copy in lowercase", async () => {
|
|
590
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
591
|
+
tempDirs.push(tempDir);
|
|
592
|
+
writeFile(tempDir, "copy_target.datasource", `SCHEMA >
|
|
593
|
+
id String
|
|
594
|
+
|
|
595
|
+
ENGINE "MergeTree"
|
|
596
|
+
ENGINE_SORTING_KEY "id"
|
|
597
|
+
`);
|
|
598
|
+
writeFile(tempDir, "copy_lower.pipe", `NODE copy_node
|
|
599
|
+
SQL >
|
|
600
|
+
SELECT id
|
|
601
|
+
FROM copy_target
|
|
602
|
+
TYPE copy
|
|
603
|
+
TARGET_DATASOURCE copy_target
|
|
604
|
+
`);
|
|
605
|
+
const result = await runMigrate({
|
|
606
|
+
cwd: tempDir,
|
|
607
|
+
patterns: ["."],
|
|
608
|
+
strict: true,
|
|
609
|
+
});
|
|
610
|
+
expect(result.success).toBe(true);
|
|
611
|
+
expect(result.errors).toHaveLength(0);
|
|
612
|
+
const output = fs.readFileSync(result.outputPath, "utf-8");
|
|
613
|
+
expect(output).toContain('export const copyLower = defineCopyPipe("copy_lower", {');
|
|
614
|
+
expect(output).toContain("datasource: copyTarget,");
|
|
615
|
+
});
|
|
616
|
+
it("migrates TYPE ENDPOINT in uppercase", async () => {
|
|
617
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
618
|
+
tempDirs.push(tempDir);
|
|
619
|
+
writeFile(tempDir, "endpoint_upper.pipe", `NODE endpoint
|
|
620
|
+
SQL >
|
|
621
|
+
SELECT 1 AS id
|
|
622
|
+
FROM system.numbers
|
|
623
|
+
LIMIT 1
|
|
624
|
+
TYPE ENDPOINT
|
|
625
|
+
`);
|
|
626
|
+
const result = await runMigrate({
|
|
627
|
+
cwd: tempDir,
|
|
628
|
+
patterns: ["."],
|
|
629
|
+
strict: true,
|
|
630
|
+
});
|
|
631
|
+
expect(result.success).toBe(true);
|
|
632
|
+
expect(result.errors).toHaveLength(0);
|
|
633
|
+
const output = fs.readFileSync(result.outputPath, "utf-8");
|
|
634
|
+
expect(output).toContain('export const endpointUpper = definePipe("endpoint_upper", {');
|
|
635
|
+
expect(output).toContain("endpoint: true,");
|
|
636
|
+
});
|
|
637
|
+
it("parses multiline datasource blocks with flexible indentation", async () => {
|
|
638
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
639
|
+
tempDirs.push(tempDir);
|
|
640
|
+
writeFile(tempDir, "flex.datasource", `DESCRIPTION >
|
|
641
|
+
Flexible indentation description
|
|
642
|
+
|
|
643
|
+
SCHEMA >
|
|
644
|
+
id String,
|
|
645
|
+
env String
|
|
646
|
+
|
|
647
|
+
ENGINE "MergeTree"
|
|
648
|
+
ENGINE_SORTING_KEY "id"
|
|
649
|
+
FORWARD_QUERY >
|
|
650
|
+
SELECT id, env
|
|
651
|
+
FROM upstream_ds
|
|
652
|
+
SHARED_WITH >
|
|
653
|
+
workspace_a,
|
|
654
|
+
workspace_b
|
|
655
|
+
`);
|
|
656
|
+
const result = await runMigrate({
|
|
657
|
+
cwd: tempDir,
|
|
658
|
+
patterns: ["."],
|
|
659
|
+
strict: true,
|
|
660
|
+
});
|
|
661
|
+
expect(result.success).toBe(true);
|
|
662
|
+
expect(result.errors).toHaveLength(0);
|
|
663
|
+
const output = fs.readFileSync(result.outputPath, "utf-8");
|
|
664
|
+
expect(output).toContain('description: "Flexible indentation description"');
|
|
665
|
+
expect(output).toContain("forwardQuery: `");
|
|
666
|
+
expect(output).toContain("SELECT id, env");
|
|
667
|
+
expect(output).toContain("FROM upstream_ds");
|
|
668
|
+
expect(output).toContain('sharedWith: ["workspace_a", "workspace_b"],');
|
|
669
|
+
});
|
|
670
|
+
it("parses node SQL blocks with flexible indentation", async () => {
|
|
671
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
672
|
+
tempDirs.push(tempDir);
|
|
673
|
+
writeFile(tempDir, "flex_pipe.pipe", `NODE endpoint_node
|
|
674
|
+
DESCRIPTION >
|
|
675
|
+
Endpoint node description
|
|
676
|
+
SQL >
|
|
677
|
+
%
|
|
678
|
+
SELECT 1 AS id
|
|
679
|
+
FROM system.numbers
|
|
680
|
+
TYPE endpoint
|
|
681
|
+
`);
|
|
682
|
+
const result = await runMigrate({
|
|
683
|
+
cwd: tempDir,
|
|
684
|
+
patterns: ["."],
|
|
685
|
+
strict: true,
|
|
686
|
+
});
|
|
687
|
+
expect(result.success).toBe(true);
|
|
688
|
+
expect(result.errors).toHaveLength(0);
|
|
689
|
+
const output = fs.readFileSync(result.outputPath, "utf-8");
|
|
690
|
+
expect(output).toContain('description: "Endpoint node description"');
|
|
691
|
+
expect(output).toContain("SELECT 1 AS id");
|
|
692
|
+
expect(output).toContain("FROM system.numbers");
|
|
693
|
+
expect(output).toContain("endpoint: true,");
|
|
694
|
+
});
|
|
695
|
+
it("migrates datasource without engine directives", async () => {
|
|
696
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
697
|
+
tempDirs.push(tempDir);
|
|
698
|
+
writeFile(tempDir, "no_engine.datasource", `SCHEMA >
|
|
699
|
+
id String,
|
|
700
|
+
name String
|
|
701
|
+
`);
|
|
702
|
+
const result = await runMigrate({
|
|
703
|
+
cwd: tempDir,
|
|
704
|
+
patterns: ["."],
|
|
705
|
+
strict: true,
|
|
706
|
+
});
|
|
707
|
+
expect(result.success).toBe(true);
|
|
708
|
+
expect(result.errors).toHaveLength(0);
|
|
709
|
+
const output = fs.readFileSync(result.outputPath, "utf-8");
|
|
710
|
+
expect(output).toContain('export const noEngine = defineDatasource("no_engine", {');
|
|
711
|
+
expect(output).not.toContain("engine:");
|
|
712
|
+
expect(output).not.toContain(", engine,");
|
|
713
|
+
});
|
|
714
|
+
it("infers MergeTree when engine options exist without ENGINE directive", async () => {
|
|
715
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
716
|
+
tempDirs.push(tempDir);
|
|
717
|
+
writeFile(tempDir, "implicit_engine.datasource", `SCHEMA >
|
|
718
|
+
id String,
|
|
719
|
+
event_date Date
|
|
720
|
+
|
|
721
|
+
ENGINE_SORTING_KEY "id"
|
|
722
|
+
ENGINE_PARTITION_KEY "toYYYYMM(event_date)"
|
|
723
|
+
`);
|
|
724
|
+
const result = await runMigrate({
|
|
725
|
+
cwd: tempDir,
|
|
726
|
+
patterns: ["."],
|
|
727
|
+
strict: true,
|
|
728
|
+
});
|
|
729
|
+
expect(result.success).toBe(true);
|
|
730
|
+
expect(result.errors).toHaveLength(0);
|
|
731
|
+
const output = fs.readFileSync(result.outputPath, "utf-8");
|
|
732
|
+
expect(output).toContain('export const implicitEngine = defineDatasource("implicit_engine", {');
|
|
733
|
+
expect(output).toContain('engine: engine.mergeTree({ sortingKey: "id", partitionKey: "toYYYYMM(event_date)" }),');
|
|
734
|
+
});
|
|
735
|
+
it("supports quoted datasource token names with spaces", async () => {
|
|
736
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
737
|
+
tempDirs.push(tempDir);
|
|
738
|
+
writeFile(tempDir, "token_spaces.datasource", `TOKEN "ingestion (Data Source append)" APPEND
|
|
739
|
+
|
|
740
|
+
SCHEMA >
|
|
741
|
+
id String
|
|
742
|
+
|
|
743
|
+
ENGINE "MergeTree"
|
|
744
|
+
ENGINE_SORTING_KEY "id"
|
|
745
|
+
`);
|
|
746
|
+
const result = await runMigrate({
|
|
747
|
+
cwd: tempDir,
|
|
748
|
+
patterns: ["."],
|
|
749
|
+
strict: true,
|
|
750
|
+
});
|
|
751
|
+
expect(result.success).toBe(true);
|
|
752
|
+
expect(result.errors).toHaveLength(0);
|
|
753
|
+
const output = fs.readFileSync(result.outputPath, "utf-8");
|
|
754
|
+
expect(output).toContain('{ name: "ingestion (Data Source append)", permissions: ["APPEND"] },');
|
|
755
|
+
});
|
|
756
|
+
it("rejects unquoted datasource token names with spaces", async () => {
|
|
757
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
758
|
+
tempDirs.push(tempDir);
|
|
759
|
+
writeFile(tempDir, "bad_token.datasource", `TOKEN ingestion data APPEND
|
|
760
|
+
|
|
761
|
+
SCHEMA >
|
|
762
|
+
id String
|
|
763
|
+
|
|
764
|
+
ENGINE "MergeTree"
|
|
765
|
+
ENGINE_SORTING_KEY "id"
|
|
766
|
+
`);
|
|
767
|
+
const result = await runMigrate({
|
|
768
|
+
cwd: tempDir,
|
|
769
|
+
patterns: ["."],
|
|
770
|
+
strict: true,
|
|
771
|
+
});
|
|
772
|
+
expect(result.success).toBe(false);
|
|
773
|
+
expect(result.errors).toHaveLength(1);
|
|
774
|
+
expect(result.errors[0]?.message).toContain("Unsupported TOKEN syntax in strict mode");
|
|
775
|
+
});
|
|
776
|
+
it("allows empty datasource DESCRIPTION block", async () => {
|
|
777
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
778
|
+
tempDirs.push(tempDir);
|
|
779
|
+
writeFile(tempDir, "empty_ds_desc.datasource", `DESCRIPTION >
|
|
780
|
+
|
|
781
|
+
SCHEMA >
|
|
782
|
+
id String
|
|
783
|
+
|
|
784
|
+
ENGINE "MergeTree"
|
|
785
|
+
ENGINE_SORTING_KEY "id"
|
|
786
|
+
`);
|
|
787
|
+
const result = await runMigrate({
|
|
788
|
+
cwd: tempDir,
|
|
789
|
+
patterns: ["."],
|
|
790
|
+
strict: true,
|
|
791
|
+
});
|
|
792
|
+
expect(result.success).toBe(true);
|
|
793
|
+
expect(result.errors).toHaveLength(0);
|
|
794
|
+
const output = fs.readFileSync(result.outputPath, "utf-8");
|
|
795
|
+
expect(output).toContain('export const emptyDsDesc = defineDatasource("empty_ds_desc", {');
|
|
796
|
+
expect(output).toContain('description: "",');
|
|
797
|
+
});
|
|
798
|
+
it("allows empty node DESCRIPTION block", async () => {
|
|
799
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
800
|
+
tempDirs.push(tempDir);
|
|
801
|
+
writeFile(tempDir, "empty_node_desc.pipe", `NODE helper
|
|
802
|
+
DESCRIPTION >
|
|
803
|
+
|
|
804
|
+
SQL >
|
|
805
|
+
SELECT 1 AS id
|
|
806
|
+
TYPE endpoint
|
|
807
|
+
`);
|
|
808
|
+
const result = await runMigrate({
|
|
809
|
+
cwd: tempDir,
|
|
810
|
+
patterns: ["."],
|
|
811
|
+
strict: true,
|
|
812
|
+
});
|
|
813
|
+
expect(result.success).toBe(true);
|
|
814
|
+
expect(result.errors).toHaveLength(0);
|
|
815
|
+
const output = fs.readFileSync(result.outputPath, "utf-8");
|
|
816
|
+
expect(output).toContain('export const emptyNodeDesc = definePipe("empty_node_desc", {');
|
|
817
|
+
expect(output).toContain('description: "",');
|
|
818
|
+
});
|
|
819
|
+
it("supports keyword-style pipe param arguments", async () => {
|
|
820
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
821
|
+
tempDirs.push(tempDir);
|
|
822
|
+
writeFile(tempDir, "keyword_params.pipe", `NODE endpoint
|
|
823
|
+
SQL >
|
|
824
|
+
SELECT 1 AS id
|
|
825
|
+
WHERE tenant_id = {{ String(tenant_id, description="Tenant ID to filter", default="") }}
|
|
826
|
+
AND event_date >= {{ Date(from_date, description="Starting date", required=False) }}
|
|
827
|
+
AND days >= {{ Int32(days, 1, description="Days to include", required=True) }}
|
|
828
|
+
TYPE endpoint
|
|
829
|
+
`);
|
|
830
|
+
const result = await runMigrate({
|
|
831
|
+
cwd: tempDir,
|
|
832
|
+
patterns: ["."],
|
|
833
|
+
strict: true,
|
|
834
|
+
});
|
|
835
|
+
expect(result.success).toBe(true);
|
|
836
|
+
expect(result.errors).toHaveLength(0);
|
|
837
|
+
const output = fs.readFileSync(result.outputPath, "utf-8");
|
|
838
|
+
expect(output).toContain('export const keywordParams = definePipe("keyword_params", {');
|
|
839
|
+
expect(output).toContain('tenant_id: p.string().optional("").describe("Tenant ID to filter"),');
|
|
840
|
+
expect(output).toContain('from_date: p.date().optional().describe("Starting date"),');
|
|
841
|
+
expect(output).toContain('days: p.int32().optional(1).describe("Days to include"),');
|
|
842
|
+
});
|
|
589
843
|
it("migrates datasource with mixed explicit and default json paths", async () => {
|
|
590
844
|
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
591
845
|
tempDirs.push(tempDir);
|
|
@@ -687,5 +941,148 @@ KAFKA_GROUP_ID events-group
|
|
|
687
941
|
expect(output).not.toContain("const secret = (name: string, defaultValue?: string) =>");
|
|
688
942
|
expect(output).toContain('groupId: "events-group",');
|
|
689
943
|
});
|
|
944
|
+
it("migrates Kafka sink pipes and emits sink config in TypeScript", async () => {
|
|
945
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
946
|
+
tempDirs.push(tempDir);
|
|
947
|
+
writeFile(tempDir, "events_kafka.connection", `TYPE kafka
|
|
948
|
+
KAFKA_BOOTSTRAP_SERVERS localhost:9092
|
|
949
|
+
`);
|
|
950
|
+
writeFile(tempDir, "events_sink.pipe", `NODE publish
|
|
951
|
+
SQL >
|
|
952
|
+
SELECT *
|
|
953
|
+
FROM events
|
|
954
|
+
WHERE env = {{String(env, 'prod')}}
|
|
955
|
+
TYPE sink
|
|
956
|
+
EXPORT_CONNECTION_NAME events_kafka
|
|
957
|
+
EXPORT_KAFKA_TOPIC events_out
|
|
958
|
+
EXPORT_SCHEDULE @on-demand
|
|
959
|
+
`);
|
|
960
|
+
const result = await runMigrate({
|
|
961
|
+
cwd: tempDir,
|
|
962
|
+
patterns: ["."],
|
|
963
|
+
strict: true,
|
|
964
|
+
});
|
|
965
|
+
expect(result.success).toBe(true);
|
|
966
|
+
expect(result.errors).toHaveLength(0);
|
|
967
|
+
expect(result.migrated.filter((resource) => resource.kind === "connection")).toHaveLength(1);
|
|
968
|
+
expect(result.migrated.filter((resource) => resource.kind === "pipe")).toHaveLength(1);
|
|
969
|
+
const output = fs.readFileSync(result.outputPath, "utf-8");
|
|
970
|
+
expect(output).toContain('export const eventsSink = defineSinkPipe("events_sink", {');
|
|
971
|
+
expect(output).toContain("params: {");
|
|
972
|
+
expect(output).toContain('env: p.string().optional("prod"),');
|
|
973
|
+
expect(output).toContain("sink: {");
|
|
974
|
+
expect(output).toContain("connection: eventsKafka");
|
|
975
|
+
expect(output).toContain('topic: "events_out"');
|
|
976
|
+
expect(output).toContain('schedule: "@on-demand"');
|
|
977
|
+
expect(output).not.toContain('strategy:');
|
|
978
|
+
});
|
|
979
|
+
it("migrates S3 sink pipes and emits compression/strategy config in TypeScript", async () => {
|
|
980
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
981
|
+
tempDirs.push(tempDir);
|
|
982
|
+
writeFile(tempDir, "exports_s3.connection", `TYPE s3
|
|
983
|
+
S3_REGION "us-east-1"
|
|
984
|
+
S3_ARN "arn:aws:iam::123456789012:role/tinybird-s3-access"
|
|
985
|
+
`);
|
|
986
|
+
writeFile(tempDir, "events_s3_sink.pipe", `NODE export
|
|
987
|
+
SQL >
|
|
988
|
+
SELECT * FROM events
|
|
989
|
+
TYPE sink
|
|
990
|
+
EXPORT_CONNECTION_NAME exports_s3
|
|
991
|
+
EXPORT_BUCKET_URI s3://exports/events/
|
|
992
|
+
EXPORT_FILE_TEMPLATE events_{date}
|
|
993
|
+
EXPORT_SCHEDULE @once
|
|
994
|
+
EXPORT_FORMAT ndjson
|
|
995
|
+
EXPORT_STRATEGY create_new
|
|
996
|
+
EXPORT_COMPRESSION gzip
|
|
997
|
+
`);
|
|
998
|
+
const result = await runMigrate({
|
|
999
|
+
cwd: tempDir,
|
|
1000
|
+
patterns: ["."],
|
|
1001
|
+
strict: true,
|
|
1002
|
+
});
|
|
1003
|
+
expect(result.success).toBe(true);
|
|
1004
|
+
expect(result.errors).toHaveLength(0);
|
|
1005
|
+
const output = fs.readFileSync(result.outputPath, "utf-8");
|
|
1006
|
+
expect(output).toContain('export const eventsS3Sink = defineSinkPipe("events_s3_sink", {');
|
|
1007
|
+
expect(output).toContain("sink: {");
|
|
1008
|
+
expect(output).toContain("connection: exportsS3");
|
|
1009
|
+
expect(output).toContain('bucketUri: "s3://exports/events/"');
|
|
1010
|
+
expect(output).toContain('fileTemplate: "events_{date}"');
|
|
1011
|
+
expect(output).toContain('schedule: "@once"');
|
|
1012
|
+
expect(output).toContain('format: "ndjson"');
|
|
1013
|
+
expect(output).toContain('strategy: "create_new"');
|
|
1014
|
+
expect(output).toContain('compression: "gzip"');
|
|
1015
|
+
});
|
|
1016
|
+
it("reports an error when sink pipe references a missing connection", async () => {
|
|
1017
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
1018
|
+
tempDirs.push(tempDir);
|
|
1019
|
+
writeFile(tempDir, "events_sink.pipe", `NODE publish
|
|
1020
|
+
SQL >
|
|
1021
|
+
SELECT * FROM events
|
|
1022
|
+
TYPE sink
|
|
1023
|
+
EXPORT_CONNECTION_NAME missing_connection
|
|
1024
|
+
EXPORT_KAFKA_TOPIC events_out
|
|
1025
|
+
EXPORT_SCHEDULE @on-demand
|
|
1026
|
+
`);
|
|
1027
|
+
const result = await runMigrate({
|
|
1028
|
+
cwd: tempDir,
|
|
1029
|
+
patterns: ["."],
|
|
1030
|
+
strict: true,
|
|
1031
|
+
});
|
|
1032
|
+
expect(result.success).toBe(false);
|
|
1033
|
+
expect(result.errors.map((error) => error.message)).toEqual(expect.arrayContaining([
|
|
1034
|
+
'Sink pipe references missing/unmigrated connection "missing_connection".',
|
|
1035
|
+
]));
|
|
1036
|
+
});
|
|
1037
|
+
it("reports an error when sink connection type does not match sink service", async () => {
|
|
1038
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
1039
|
+
tempDirs.push(tempDir);
|
|
1040
|
+
writeFile(tempDir, "events_kafka.connection", `TYPE kafka
|
|
1041
|
+
KAFKA_BOOTSTRAP_SERVERS localhost:9092
|
|
1042
|
+
`);
|
|
1043
|
+
writeFile(tempDir, "events_s3_sink.pipe", `NODE export
|
|
1044
|
+
SQL >
|
|
1045
|
+
SELECT * FROM events
|
|
1046
|
+
TYPE sink
|
|
1047
|
+
EXPORT_CONNECTION_NAME events_kafka
|
|
1048
|
+
EXPORT_BUCKET_URI s3://exports/events/
|
|
1049
|
+
EXPORT_FILE_TEMPLATE events_{date}
|
|
1050
|
+
EXPORT_SCHEDULE @once
|
|
1051
|
+
EXPORT_FORMAT csv
|
|
1052
|
+
`);
|
|
1053
|
+
const result = await runMigrate({
|
|
1054
|
+
cwd: tempDir,
|
|
1055
|
+
patterns: ["."],
|
|
1056
|
+
strict: true,
|
|
1057
|
+
});
|
|
1058
|
+
expect(result.success).toBe(false);
|
|
1059
|
+
expect(result.errors.map((error) => error.message)).toEqual(expect.arrayContaining([
|
|
1060
|
+
'Sink pipe service "s3" is incompatible with connection "events_kafka" type "kafka".',
|
|
1061
|
+
]));
|
|
1062
|
+
});
|
|
1063
|
+
it("supports comments in sink pipe files", async () => {
|
|
1064
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "tinybird-migrate-"));
|
|
1065
|
+
tempDirs.push(tempDir);
|
|
1066
|
+
writeFile(tempDir, "events_kafka.connection", `TYPE kafka
|
|
1067
|
+
KAFKA_BOOTSTRAP_SERVERS localhost:9092
|
|
1068
|
+
`);
|
|
1069
|
+
writeFile(tempDir, "events_sink.pipe", `# this pipe publishes records
|
|
1070
|
+
NODE publish
|
|
1071
|
+
SQL >
|
|
1072
|
+
SELECT * FROM events
|
|
1073
|
+
TYPE sink
|
|
1074
|
+
# Kafka target
|
|
1075
|
+
EXPORT_CONNECTION_NAME events_kafka
|
|
1076
|
+
EXPORT_KAFKA_TOPIC events_out
|
|
1077
|
+
EXPORT_SCHEDULE @on-demand
|
|
1078
|
+
`);
|
|
1079
|
+
const result = await runMigrate({
|
|
1080
|
+
cwd: tempDir,
|
|
1081
|
+
patterns: ["."],
|
|
1082
|
+
strict: true,
|
|
1083
|
+
});
|
|
1084
|
+
expect(result.success).toBe(true);
|
|
1085
|
+
expect(result.errors).toHaveLength(0);
|
|
1086
|
+
});
|
|
690
1087
|
});
|
|
691
1088
|
//# sourceMappingURL=migrate.test.js.map
|