@tinybirdco/sdk 0.0.1 → 0.0.3
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 +5 -4
- package/dist/api/build.d.ts +2 -0
- package/dist/api/build.d.ts.map +1 -1
- package/dist/api/build.js +13 -0
- package/dist/api/build.js.map +1 -1
- package/dist/api/build.test.js +1 -0
- package/dist/api/build.test.js.map +1 -1
- package/dist/api/deploy.d.ts.map +1 -1
- package/dist/api/deploy.js +3 -0
- package/dist/api/deploy.js.map +1 -1
- package/dist/api/deploy.test.js +1 -0
- package/dist/api/deploy.test.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +26 -0
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/init.test.js +43 -0
- package/dist/cli/commands/init.test.js.map +1 -1
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/generator/connection.d.ts +49 -0
- package/dist/generator/connection.d.ts.map +1 -0
- package/dist/generator/connection.js +78 -0
- package/dist/generator/connection.js.map +1 -0
- package/dist/generator/connection.test.d.ts +2 -0
- package/dist/generator/connection.test.d.ts.map +1 -0
- package/dist/generator/connection.test.js +106 -0
- package/dist/generator/connection.test.js.map +1 -0
- package/dist/generator/datasource.d.ts.map +1 -1
- package/dist/generator/datasource.js +20 -0
- package/dist/generator/datasource.js.map +1 -1
- package/dist/generator/datasource.test.js +92 -0
- package/dist/generator/datasource.test.js.map +1 -1
- package/dist/generator/index.d.ts +8 -2
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +10 -3
- package/dist/generator/index.js.map +1 -1
- package/dist/generator/loader.d.ts +8 -1
- package/dist/generator/loader.d.ts.map +1 -1
- package/dist/generator/loader.js +17 -2
- package/dist/generator/loader.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/schema/connection.d.ts +83 -0
- package/dist/schema/connection.d.ts.map +1 -0
- package/dist/schema/connection.js +61 -0
- package/dist/schema/connection.js.map +1 -0
- package/dist/schema/connection.test.d.ts +2 -0
- package/dist/schema/connection.test.d.ts.map +1 -0
- package/dist/schema/connection.test.js +117 -0
- package/dist/schema/connection.test.js.map +1 -0
- package/dist/schema/datasource.d.ts +16 -0
- package/dist/schema/datasource.d.ts.map +1 -1
- package/dist/schema/datasource.js.map +1 -1
- package/dist/schema/project.d.ts +12 -3
- package/dist/schema/project.d.ts.map +1 -1
- package/dist/schema/project.js +2 -0
- package/dist/schema/project.js.map +1 -1
- package/package.json +2 -1
- package/src/api/build.test.ts +1 -0
- package/src/api/build.ts +20 -0
- package/src/api/deploy.test.ts +1 -0
- package/src/api/deploy.ts +3 -0
- package/src/cli/commands/init.test.ts +72 -0
- package/src/cli/commands/init.ts +30 -0
- package/src/cli/index.ts +6 -0
- package/src/generator/connection.test.ts +135 -0
- package/src/generator/connection.ts +104 -0
- package/src/generator/datasource.test.ts +108 -0
- package/src/generator/datasource.ts +27 -1
- package/src/generator/index.ts +16 -4
- package/src/generator/loader.ts +21 -3
- package/src/index.ts +12 -0
- package/src/schema/connection.test.ts +149 -0
- package/src/schema/connection.ts +123 -0
- package/src/schema/datasource.ts +17 -0
- package/src/schema/project.ts +20 -5
package/src/generator/index.ts
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
import { loadSchema, loadEntities, entitiesToProject, type LoadedEntities } from "./loader.js";
|
|
7
7
|
import { generateAllDatasources, type GeneratedDatasource } from "./datasource.js";
|
|
8
8
|
import { generateAllPipes, type GeneratedPipe } from "./pipe.js";
|
|
9
|
-
import
|
|
9
|
+
import { generateAllConnections, type GeneratedConnection } from "./connection.js";
|
|
10
|
+
import type { ProjectDefinition, DatasourcesDefinition, PipesDefinition, ConnectionsDefinition } from "../schema/project.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Generated resources ready for API push
|
|
@@ -16,6 +17,8 @@ export interface GeneratedResources {
|
|
|
16
17
|
datasources: GeneratedDatasource[];
|
|
17
18
|
/** Generated pipe files */
|
|
18
19
|
pipes: GeneratedPipe[];
|
|
20
|
+
/** Generated connection files */
|
|
21
|
+
connections: GeneratedConnection[];
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
/**
|
|
@@ -34,6 +37,7 @@ export interface BuildResult {
|
|
|
34
37
|
stats: {
|
|
35
38
|
datasourceCount: number;
|
|
36
39
|
pipeCount: number;
|
|
40
|
+
connectionCount: number;
|
|
37
41
|
};
|
|
38
42
|
}
|
|
39
43
|
|
|
@@ -46,10 +50,12 @@ export interface BuildResult {
|
|
|
46
50
|
export function generateResources(project: ProjectDefinition): GeneratedResources {
|
|
47
51
|
const datasources = generateAllDatasources(project.datasources);
|
|
48
52
|
const pipes = generateAllPipes(project.pipes);
|
|
53
|
+
const connections = generateAllConnections(project.connections);
|
|
49
54
|
|
|
50
55
|
return {
|
|
51
56
|
datasources,
|
|
52
57
|
pipes,
|
|
58
|
+
connections,
|
|
53
59
|
};
|
|
54
60
|
}
|
|
55
61
|
|
|
@@ -107,6 +113,7 @@ export async function build(options: BuildOptions): Promise<BuildResult> {
|
|
|
107
113
|
stats: {
|
|
108
114
|
datasourceCount: resources.datasources.length,
|
|
109
115
|
pipeCount: resources.pipes.length,
|
|
116
|
+
connectionCount: resources.connections.length,
|
|
110
117
|
},
|
|
111
118
|
};
|
|
112
119
|
}
|
|
@@ -133,6 +140,7 @@ export interface BuildFromIncludeResult {
|
|
|
133
140
|
stats: {
|
|
134
141
|
datasourceCount: number;
|
|
135
142
|
pipeCount: number;
|
|
143
|
+
connectionCount: number;
|
|
136
144
|
};
|
|
137
145
|
}
|
|
138
146
|
|
|
@@ -141,11 +149,13 @@ export interface BuildFromIncludeResult {
|
|
|
141
149
|
*/
|
|
142
150
|
export function generateResourcesFromEntities(
|
|
143
151
|
datasources: DatasourcesDefinition,
|
|
144
|
-
pipes: PipesDefinition
|
|
152
|
+
pipes: PipesDefinition,
|
|
153
|
+
connections: ConnectionsDefinition = {}
|
|
145
154
|
): GeneratedResources {
|
|
146
155
|
return {
|
|
147
156
|
datasources: generateAllDatasources(datasources),
|
|
148
157
|
pipes: generateAllPipes(pipes),
|
|
158
|
+
connections: generateAllConnections(connections),
|
|
149
159
|
};
|
|
150
160
|
}
|
|
151
161
|
|
|
@@ -181,10 +191,10 @@ export async function buildFromInclude(
|
|
|
181
191
|
});
|
|
182
192
|
|
|
183
193
|
// Convert to format for generators
|
|
184
|
-
const { datasources, pipes } = entitiesToProject(entities);
|
|
194
|
+
const { datasources, pipes, connections } = entitiesToProject(entities);
|
|
185
195
|
|
|
186
196
|
// Generate resources
|
|
187
|
-
const resources = generateResourcesFromEntities(datasources, pipes);
|
|
197
|
+
const resources = generateResourcesFromEntities(datasources, pipes, connections);
|
|
188
198
|
|
|
189
199
|
return {
|
|
190
200
|
resources,
|
|
@@ -192,6 +202,7 @@ export async function buildFromInclude(
|
|
|
192
202
|
stats: {
|
|
193
203
|
datasourceCount: resources.datasources.length,
|
|
194
204
|
pipeCount: resources.pipes.length,
|
|
205
|
+
connectionCount: resources.connections.length,
|
|
195
206
|
},
|
|
196
207
|
};
|
|
197
208
|
}
|
|
@@ -200,4 +211,5 @@ export async function buildFromInclude(
|
|
|
200
211
|
export { loadSchema, loadEntities, entitiesToProject, type LoaderOptions, type LoadedSchema, type LoadedEntities, type LoadEntitiesOptions } from "./loader.js";
|
|
201
212
|
export { generateDatasource, generateAllDatasources, type GeneratedDatasource } from "./datasource.js";
|
|
202
213
|
export { generatePipe, generateAllPipes, type GeneratedPipe } from "./pipe.js";
|
|
214
|
+
export { generateConnection, generateAllConnections, type GeneratedConnection } from "./connection.js";
|
|
203
215
|
export { generateClientFile, type GenerateClientOptions, type GeneratedClient } from "./client.js";
|
package/src/generator/loader.ts
CHANGED
|
@@ -7,9 +7,10 @@ import * as esbuild from "esbuild";
|
|
|
7
7
|
import * as path from "path";
|
|
8
8
|
import * as fs from "fs";
|
|
9
9
|
import { watch as chokidarWatch, type FSWatcher } from "chokidar";
|
|
10
|
-
import { isProjectDefinition, type ProjectDefinition, type DatasourcesDefinition, type PipesDefinition } from "../schema/project.js";
|
|
10
|
+
import { isProjectDefinition, type ProjectDefinition, type DatasourcesDefinition, type PipesDefinition, type ConnectionsDefinition } from "../schema/project.js";
|
|
11
11
|
import { isDatasourceDefinition, type DatasourceDefinition } from "../schema/datasource.js";
|
|
12
12
|
import { isPipeDefinition, type PipeDefinition } from "../schema/pipe.js";
|
|
13
|
+
import { isConnectionDefinition, type ConnectionDefinition } from "../schema/connection.js";
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Result of loading a schema file
|
|
@@ -157,6 +158,8 @@ export interface LoadedEntities {
|
|
|
157
158
|
datasources: Record<string, { definition: DatasourceDefinition; info: EntityInfo }>;
|
|
158
159
|
/** Discovered pipes with their metadata */
|
|
159
160
|
pipes: Record<string, { definition: PipeDefinition; info: EntityInfo }>;
|
|
161
|
+
/** Discovered connections with their metadata */
|
|
162
|
+
connections: Record<string, { definition: ConnectionDefinition; info: EntityInfo }>;
|
|
160
163
|
/** All source files that were scanned */
|
|
161
164
|
sourceFiles: string[];
|
|
162
165
|
}
|
|
@@ -195,6 +198,7 @@ export async function loadEntities(options: LoadEntitiesOptions): Promise<Loaded
|
|
|
195
198
|
const result: LoadedEntities = {
|
|
196
199
|
datasources: {},
|
|
197
200
|
pipes: {},
|
|
201
|
+
connections: {},
|
|
198
202
|
sourceFiles: [],
|
|
199
203
|
};
|
|
200
204
|
|
|
@@ -237,7 +241,7 @@ export async function loadEntities(options: LoadEntitiesOptions): Promise<Loaded
|
|
|
237
241
|
const moduleUrl = `file://${outfile}`;
|
|
238
242
|
const module = await import(moduleUrl);
|
|
239
243
|
|
|
240
|
-
// Scan all exports for datasources and
|
|
244
|
+
// Scan all exports for datasources, pipes, and connections
|
|
241
245
|
for (const [exportName, value] of Object.entries(module)) {
|
|
242
246
|
if (isDatasourceDefinition(value)) {
|
|
243
247
|
result.datasources[exportName] = {
|
|
@@ -255,6 +259,14 @@ export async function loadEntities(options: LoadEntitiesOptions): Promise<Loaded
|
|
|
255
259
|
sourceFile: includePath,
|
|
256
260
|
},
|
|
257
261
|
};
|
|
262
|
+
} else if (isConnectionDefinition(value)) {
|
|
263
|
+
result.connections[exportName] = {
|
|
264
|
+
definition: value,
|
|
265
|
+
info: {
|
|
266
|
+
exportName,
|
|
267
|
+
sourceFile: includePath,
|
|
268
|
+
},
|
|
269
|
+
};
|
|
258
270
|
}
|
|
259
271
|
}
|
|
260
272
|
} finally {
|
|
@@ -282,9 +294,11 @@ export async function loadEntities(options: LoadEntitiesOptions): Promise<Loaded
|
|
|
282
294
|
export function entitiesToProject(entities: LoadedEntities): {
|
|
283
295
|
datasources: DatasourcesDefinition;
|
|
284
296
|
pipes: PipesDefinition;
|
|
297
|
+
connections: ConnectionsDefinition;
|
|
285
298
|
} {
|
|
286
299
|
const datasources: DatasourcesDefinition = {};
|
|
287
300
|
const pipes: PipesDefinition = {};
|
|
301
|
+
const connections: ConnectionsDefinition = {};
|
|
288
302
|
|
|
289
303
|
for (const [name, { definition }] of Object.entries(entities.datasources)) {
|
|
290
304
|
datasources[name] = definition;
|
|
@@ -294,7 +308,11 @@ export function entitiesToProject(entities: LoadedEntities): {
|
|
|
294
308
|
pipes[name] = definition;
|
|
295
309
|
}
|
|
296
310
|
|
|
297
|
-
|
|
311
|
+
for (const [name, { definition }] of Object.entries(entities.connections)) {
|
|
312
|
+
connections[name] = definition;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return { datasources, pipes, connections };
|
|
298
316
|
}
|
|
299
317
|
|
|
300
318
|
/**
|
package/src/index.ts
CHANGED
|
@@ -106,8 +106,19 @@ export type {
|
|
|
106
106
|
ColumnDefinition,
|
|
107
107
|
TokenConfig,
|
|
108
108
|
ExtractSchema,
|
|
109
|
+
KafkaConfig,
|
|
109
110
|
} from "./schema/datasource.js";
|
|
110
111
|
|
|
112
|
+
// ============ Connection ============
|
|
113
|
+
export { createKafkaConnection, isConnectionDefinition, isKafkaConnectionDefinition, getConnectionType } from "./schema/connection.js";
|
|
114
|
+
export type {
|
|
115
|
+
ConnectionDefinition,
|
|
116
|
+
KafkaConnectionDefinition,
|
|
117
|
+
KafkaConnectionOptions,
|
|
118
|
+
KafkaSecurityProtocol,
|
|
119
|
+
KafkaSaslMechanism,
|
|
120
|
+
} from "./schema/connection.js";
|
|
121
|
+
|
|
111
122
|
// ============ Pipe ============
|
|
112
123
|
export {
|
|
113
124
|
definePipe,
|
|
@@ -153,6 +164,7 @@ export type {
|
|
|
153
164
|
TinybirdClientConfig,
|
|
154
165
|
DatasourcesDefinition,
|
|
155
166
|
PipesDefinition,
|
|
167
|
+
ConnectionsDefinition,
|
|
156
168
|
ExtractDatasources,
|
|
157
169
|
ExtractPipes,
|
|
158
170
|
DataModel,
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
createKafkaConnection,
|
|
4
|
+
isConnectionDefinition,
|
|
5
|
+
isKafkaConnectionDefinition,
|
|
6
|
+
getConnectionType,
|
|
7
|
+
} from "./connection.js";
|
|
8
|
+
|
|
9
|
+
describe("Connection Schema", () => {
|
|
10
|
+
describe("createKafkaConnection", () => {
|
|
11
|
+
it("creates a Kafka connection with required fields", () => {
|
|
12
|
+
const conn = createKafkaConnection("my_kafka", {
|
|
13
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
expect(conn._name).toBe("my_kafka");
|
|
17
|
+
expect(conn._type).toBe("connection");
|
|
18
|
+
expect(conn._connectionType).toBe("kafka");
|
|
19
|
+
expect(conn.options.bootstrapServers).toBe("kafka.example.com:9092");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("creates a Kafka connection with all options", () => {
|
|
23
|
+
const conn = createKafkaConnection("my_kafka", {
|
|
24
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
25
|
+
securityProtocol: "SASL_SSL",
|
|
26
|
+
saslMechanism: "PLAIN",
|
|
27
|
+
key: '{{ tb_secret("KAFKA_KEY") }}',
|
|
28
|
+
secret: '{{ tb_secret("KAFKA_SECRET") }}',
|
|
29
|
+
sslCaPem: '{{ tb_secret("KAFKA_CA_CERT") }}',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(conn.options.securityProtocol).toBe("SASL_SSL");
|
|
33
|
+
expect(conn.options.saslMechanism).toBe("PLAIN");
|
|
34
|
+
expect(conn.options.key).toBe('{{ tb_secret("KAFKA_KEY") }}');
|
|
35
|
+
expect(conn.options.secret).toBe('{{ tb_secret("KAFKA_SECRET") }}');
|
|
36
|
+
expect(conn.options.sslCaPem).toBe('{{ tb_secret("KAFKA_CA_CERT") }}');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("supports different SASL mechanisms", () => {
|
|
40
|
+
const scramConn = createKafkaConnection("scram_kafka", {
|
|
41
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
42
|
+
saslMechanism: "SCRAM-SHA-256",
|
|
43
|
+
});
|
|
44
|
+
expect(scramConn.options.saslMechanism).toBe("SCRAM-SHA-256");
|
|
45
|
+
|
|
46
|
+
const scram512Conn = createKafkaConnection("scram512_kafka", {
|
|
47
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
48
|
+
saslMechanism: "SCRAM-SHA-512",
|
|
49
|
+
});
|
|
50
|
+
expect(scram512Conn.options.saslMechanism).toBe("SCRAM-SHA-512");
|
|
51
|
+
|
|
52
|
+
const oauthConn = createKafkaConnection("oauth_kafka", {
|
|
53
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
54
|
+
saslMechanism: "OAUTHBEARER",
|
|
55
|
+
});
|
|
56
|
+
expect(oauthConn.options.saslMechanism).toBe("OAUTHBEARER");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("supports different security protocols", () => {
|
|
60
|
+
const plaintext = createKafkaConnection("plaintext_kafka", {
|
|
61
|
+
bootstrapServers: "localhost:9092",
|
|
62
|
+
securityProtocol: "PLAINTEXT",
|
|
63
|
+
});
|
|
64
|
+
expect(plaintext.options.securityProtocol).toBe("PLAINTEXT");
|
|
65
|
+
|
|
66
|
+
const saslPlaintext = createKafkaConnection("sasl_plaintext_kafka", {
|
|
67
|
+
bootstrapServers: "localhost:9092",
|
|
68
|
+
securityProtocol: "SASL_PLAINTEXT",
|
|
69
|
+
});
|
|
70
|
+
expect(saslPlaintext.options.securityProtocol).toBe("SASL_PLAINTEXT");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("throws error for invalid connection name", () => {
|
|
74
|
+
expect(() =>
|
|
75
|
+
createKafkaConnection("123invalid", {
|
|
76
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
77
|
+
})
|
|
78
|
+
).toThrow("Invalid connection name");
|
|
79
|
+
|
|
80
|
+
expect(() =>
|
|
81
|
+
createKafkaConnection("my-connection", {
|
|
82
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
83
|
+
})
|
|
84
|
+
).toThrow("Invalid connection name");
|
|
85
|
+
|
|
86
|
+
expect(() =>
|
|
87
|
+
createKafkaConnection("", {
|
|
88
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
89
|
+
})
|
|
90
|
+
).toThrow("Invalid connection name");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("allows valid naming patterns", () => {
|
|
94
|
+
const conn1 = createKafkaConnection("_private_kafka", {
|
|
95
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
96
|
+
});
|
|
97
|
+
expect(conn1._name).toBe("_private_kafka");
|
|
98
|
+
|
|
99
|
+
const conn2 = createKafkaConnection("kafka_v2", {
|
|
100
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
101
|
+
});
|
|
102
|
+
expect(conn2._name).toBe("kafka_v2");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe("isConnectionDefinition", () => {
|
|
107
|
+
it("returns true for valid connection", () => {
|
|
108
|
+
const conn = createKafkaConnection("my_kafka", {
|
|
109
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(isConnectionDefinition(conn)).toBe(true);
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("returns false for non-connection objects", () => {
|
|
116
|
+
expect(isConnectionDefinition({})).toBe(false);
|
|
117
|
+
expect(isConnectionDefinition(null)).toBe(false);
|
|
118
|
+
expect(isConnectionDefinition(undefined)).toBe(false);
|
|
119
|
+
expect(isConnectionDefinition("string")).toBe(false);
|
|
120
|
+
expect(isConnectionDefinition(123)).toBe(false);
|
|
121
|
+
expect(isConnectionDefinition({ _name: "test" })).toBe(false);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe("isKafkaConnectionDefinition", () => {
|
|
126
|
+
it("returns true for Kafka connection", () => {
|
|
127
|
+
const conn = createKafkaConnection("my_kafka", {
|
|
128
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
expect(isKafkaConnectionDefinition(conn)).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("returns false for non-Kafka objects", () => {
|
|
135
|
+
expect(isKafkaConnectionDefinition({})).toBe(false);
|
|
136
|
+
expect(isKafkaConnectionDefinition(null)).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("getConnectionType", () => {
|
|
141
|
+
it("returns the connection type", () => {
|
|
142
|
+
const conn = createKafkaConnection("my_kafka", {
|
|
143
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
expect(getConnectionType(conn)).toBe("kafka");
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connection definition for Tinybird
|
|
3
|
+
* Define external connections (Kafka, etc.) as TypeScript with full type safety
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Symbol for brand typing
|
|
7
|
+
const CONNECTION_BRAND = Symbol("tinybird.connection");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Kafka security protocol options
|
|
11
|
+
*/
|
|
12
|
+
export type KafkaSecurityProtocol = "SASL_SSL" | "PLAINTEXT" | "SASL_PLAINTEXT";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Kafka SASL mechanism options
|
|
16
|
+
*/
|
|
17
|
+
export type KafkaSaslMechanism = "PLAIN" | "SCRAM-SHA-256" | "SCRAM-SHA-512" | "OAUTHBEARER";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Options for creating a Kafka connection
|
|
21
|
+
*/
|
|
22
|
+
export interface KafkaConnectionOptions {
|
|
23
|
+
/** Kafka bootstrap servers (host:port) */
|
|
24
|
+
bootstrapServers: string;
|
|
25
|
+
/** Security protocol (default: 'SASL_SSL') */
|
|
26
|
+
securityProtocol?: KafkaSecurityProtocol;
|
|
27
|
+
/** SASL mechanism for authentication */
|
|
28
|
+
saslMechanism?: KafkaSaslMechanism;
|
|
29
|
+
/** Kafka key/username - can use {{ tb_secret(...) }} */
|
|
30
|
+
key?: string;
|
|
31
|
+
/** Kafka secret/password - can use {{ tb_secret(...) }} */
|
|
32
|
+
secret?: string;
|
|
33
|
+
/** SSL CA certificate PEM - for private CA certs */
|
|
34
|
+
sslCaPem?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Kafka-specific connection definition
|
|
39
|
+
*/
|
|
40
|
+
export interface KafkaConnectionDefinition {
|
|
41
|
+
readonly [CONNECTION_BRAND]: true;
|
|
42
|
+
/** Connection name */
|
|
43
|
+
readonly _name: string;
|
|
44
|
+
/** Type marker for inference */
|
|
45
|
+
readonly _type: "connection";
|
|
46
|
+
/** Connection type */
|
|
47
|
+
readonly _connectionType: "kafka";
|
|
48
|
+
/** Kafka options */
|
|
49
|
+
readonly options: KafkaConnectionOptions;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A connection definition - union of all connection types
|
|
54
|
+
*/
|
|
55
|
+
export type ConnectionDefinition = KafkaConnectionDefinition;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Create a Kafka connection
|
|
59
|
+
*
|
|
60
|
+
* @param name - The connection name (must be valid identifier)
|
|
61
|
+
* @param options - Kafka connection configuration
|
|
62
|
+
* @returns A connection definition that can be used in a project
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* import { createKafkaConnection } from '@tinybirdco/sdk';
|
|
67
|
+
*
|
|
68
|
+
* export const myKafka = createKafkaConnection('my_kafka', {
|
|
69
|
+
* bootstrapServers: 'kafka.example.com:9092',
|
|
70
|
+
* securityProtocol: 'SASL_SSL',
|
|
71
|
+
* saslMechanism: 'PLAIN',
|
|
72
|
+
* key: '{{ tb_secret("KAFKA_KEY") }}',
|
|
73
|
+
* secret: '{{ tb_secret("KAFKA_SECRET") }}',
|
|
74
|
+
* });
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export function createKafkaConnection(
|
|
78
|
+
name: string,
|
|
79
|
+
options: KafkaConnectionOptions
|
|
80
|
+
): KafkaConnectionDefinition {
|
|
81
|
+
// Validate name is a valid identifier
|
|
82
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`Invalid connection name: "${name}". Must start with a letter or underscore and contain only alphanumeric characters and underscores.`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
[CONNECTION_BRAND]: true,
|
|
90
|
+
_name: name,
|
|
91
|
+
_type: "connection",
|
|
92
|
+
_connectionType: "kafka",
|
|
93
|
+
options,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Check if a value is a connection definition
|
|
99
|
+
*/
|
|
100
|
+
export function isConnectionDefinition(value: unknown): value is ConnectionDefinition {
|
|
101
|
+
return (
|
|
102
|
+
typeof value === "object" &&
|
|
103
|
+
value !== null &&
|
|
104
|
+
CONNECTION_BRAND in value &&
|
|
105
|
+
(value as Record<symbol, unknown>)[CONNECTION_BRAND] === true
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check if a value is a Kafka connection definition
|
|
111
|
+
*/
|
|
112
|
+
export function isKafkaConnectionDefinition(value: unknown): value is KafkaConnectionDefinition {
|
|
113
|
+
return isConnectionDefinition(value) && value._connectionType === "kafka";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Get the connection type from a connection definition
|
|
118
|
+
*/
|
|
119
|
+
export function getConnectionType<T extends ConnectionDefinition>(
|
|
120
|
+
connection: T
|
|
121
|
+
): T["_connectionType"] {
|
|
122
|
+
return connection._connectionType;
|
|
123
|
+
}
|
package/src/schema/datasource.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { AnyTypeValidator } from "./types.js";
|
|
7
7
|
import type { EngineConfig } from "./engines.js";
|
|
8
|
+
import type { KafkaConnectionDefinition } from "./connection.js";
|
|
8
9
|
|
|
9
10
|
// Symbol for brand typing
|
|
10
11
|
const DATASOURCE_BRAND = Symbol("tinybird.datasource");
|
|
@@ -35,6 +36,20 @@ export interface TokenConfig {
|
|
|
35
36
|
permissions: readonly ("READ" | "APPEND")[];
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Kafka ingestion configuration for a datasource
|
|
41
|
+
*/
|
|
42
|
+
export interface KafkaConfig {
|
|
43
|
+
/** Kafka connection to use */
|
|
44
|
+
connection: KafkaConnectionDefinition;
|
|
45
|
+
/** Kafka topic to consume from */
|
|
46
|
+
topic: string;
|
|
47
|
+
/** Consumer group ID (optional) */
|
|
48
|
+
groupId?: string;
|
|
49
|
+
/** Where to start reading: 'earliest' or 'latest' (default: 'latest') */
|
|
50
|
+
autoOffsetReset?: "earliest" | "latest";
|
|
51
|
+
}
|
|
52
|
+
|
|
38
53
|
/**
|
|
39
54
|
* Options for defining a datasource
|
|
40
55
|
*/
|
|
@@ -55,6 +70,8 @@ export interface DatasourceOptions<TSchema extends SchemaDefinition> {
|
|
|
55
70
|
* Defaults to true.
|
|
56
71
|
*/
|
|
57
72
|
jsonPaths?: boolean;
|
|
73
|
+
/** Kafka ingestion configuration */
|
|
74
|
+
kafka?: KafkaConfig;
|
|
58
75
|
}
|
|
59
76
|
|
|
60
77
|
/**
|
package/src/schema/project.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import type { DatasourceDefinition, SchemaDefinition } from "./datasource.js";
|
|
7
7
|
import type { PipeDefinition, ParamsDefinition, OutputDefinition } from "./pipe.js";
|
|
8
|
+
import type { ConnectionDefinition } from "./connection.js";
|
|
8
9
|
import { getEndpointConfig } from "./pipe.js";
|
|
9
10
|
import type { TinybirdClient } from "../client/base.js";
|
|
10
11
|
import type { QueryResult } from "../client/types.js";
|
|
@@ -23,6 +24,11 @@ export type DatasourcesDefinition = Record<string, DatasourceDefinition<SchemaDe
|
|
|
23
24
|
*/
|
|
24
25
|
export type PipesDefinition = Record<string, PipeDefinition<ParamsDefinition, OutputDefinition>>;
|
|
25
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Collection of connection definitions
|
|
29
|
+
*/
|
|
30
|
+
export type ConnectionsDefinition = Record<string, ConnectionDefinition>;
|
|
31
|
+
|
|
26
32
|
/**
|
|
27
33
|
* Type for a single query method
|
|
28
34
|
*/
|
|
@@ -101,12 +107,15 @@ export interface TinybirdClientConfig<
|
|
|
101
107
|
*/
|
|
102
108
|
export interface ProjectConfig<
|
|
103
109
|
TDatasources extends DatasourcesDefinition = DatasourcesDefinition,
|
|
104
|
-
TPipes extends PipesDefinition = PipesDefinition
|
|
110
|
+
TPipes extends PipesDefinition = PipesDefinition,
|
|
111
|
+
TConnections extends ConnectionsDefinition = ConnectionsDefinition
|
|
105
112
|
> {
|
|
106
113
|
/** All datasources in this project */
|
|
107
114
|
datasources?: TDatasources;
|
|
108
115
|
/** All pipes in this project */
|
|
109
116
|
pipes?: TPipes;
|
|
117
|
+
/** All connections in this project */
|
|
118
|
+
connections?: TConnections;
|
|
110
119
|
}
|
|
111
120
|
|
|
112
121
|
/**
|
|
@@ -114,7 +123,8 @@ export interface ProjectConfig<
|
|
|
114
123
|
*/
|
|
115
124
|
export interface ProjectDefinition<
|
|
116
125
|
TDatasources extends DatasourcesDefinition = DatasourcesDefinition,
|
|
117
|
-
TPipes extends PipesDefinition = PipesDefinition
|
|
126
|
+
TPipes extends PipesDefinition = PipesDefinition,
|
|
127
|
+
TConnections extends ConnectionsDefinition = ConnectionsDefinition
|
|
118
128
|
> {
|
|
119
129
|
readonly [PROJECT_BRAND]: true;
|
|
120
130
|
/** Type marker for inference */
|
|
@@ -123,6 +133,8 @@ export interface ProjectDefinition<
|
|
|
123
133
|
readonly datasources: TDatasources;
|
|
124
134
|
/** All pipes */
|
|
125
135
|
readonly pipes: TPipes;
|
|
136
|
+
/** All connections */
|
|
137
|
+
readonly connections: TConnections;
|
|
126
138
|
/** Typed Tinybird client */
|
|
127
139
|
readonly tinybird: ProjectClient<TDatasources, TPipes>;
|
|
128
140
|
}
|
|
@@ -157,12 +169,14 @@ export interface ProjectDefinition<
|
|
|
157
169
|
*/
|
|
158
170
|
export function defineProject<
|
|
159
171
|
TDatasources extends DatasourcesDefinition,
|
|
160
|
-
TPipes extends PipesDefinition
|
|
172
|
+
TPipes extends PipesDefinition,
|
|
173
|
+
TConnections extends ConnectionsDefinition
|
|
161
174
|
>(
|
|
162
|
-
config: ProjectConfig<TDatasources, TPipes>
|
|
163
|
-
): ProjectDefinition<TDatasources, TPipes> {
|
|
175
|
+
config: ProjectConfig<TDatasources, TPipes, TConnections>
|
|
176
|
+
): ProjectDefinition<TDatasources, TPipes, TConnections> {
|
|
164
177
|
const datasources = (config.datasources ?? {}) as TDatasources;
|
|
165
178
|
const pipes = (config.pipes ?? {}) as TPipes;
|
|
179
|
+
const connections = (config.connections ?? {}) as TConnections;
|
|
166
180
|
|
|
167
181
|
// Use the shared client builder
|
|
168
182
|
const tinybird = buildProjectClient(datasources, pipes);
|
|
@@ -172,6 +186,7 @@ export function defineProject<
|
|
|
172
186
|
_type: "project",
|
|
173
187
|
datasources,
|
|
174
188
|
pipes,
|
|
189
|
+
connections,
|
|
175
190
|
tinybird,
|
|
176
191
|
};
|
|
177
192
|
}
|