@tinybirdco/sdk 0.0.44 → 0.0.46
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 +61 -8
- package/dist/api/api.d.ts.map +1 -1
- package/dist/api/api.js +8 -3
- package/dist/api/api.js.map +1 -1
- package/dist/api/api.test.js +24 -6
- package/dist/api/api.test.js.map +1 -1
- package/dist/cli/commands/migrate.d.ts.map +1 -1
- package/dist/cli/commands/migrate.js +4 -3
- package/dist/cli/commands/migrate.js.map +1 -1
- package/dist/cli/commands/migrate.test.js +42 -4
- package/dist/cli/commands/migrate.test.js.map +1 -1
- package/dist/client/base.d.ts +1 -1
- package/dist/client/base.js +1 -1
- package/dist/generator/connection.d.ts +1 -1
- package/dist/generator/connection.d.ts.map +1 -1
- package/dist/generator/connection.js +25 -2
- package/dist/generator/connection.js.map +1 -1
- package/dist/generator/connection.test.js +37 -14
- package/dist/generator/connection.test.js.map +1 -1
- package/dist/generator/datasource.d.ts.map +1 -1
- package/dist/generator/datasource.js +23 -0
- package/dist/generator/datasource.js.map +1 -1
- package/dist/generator/datasource.test.js +53 -9
- package/dist/generator/datasource.test.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/infer/index.d.ts +3 -3
- package/dist/migrate/emit-ts.d.ts.map +1 -1
- package/dist/migrate/emit-ts.js +69 -13
- package/dist/migrate/emit-ts.js.map +1 -1
- package/dist/migrate/parse-connection.d.ts +2 -2
- package/dist/migrate/parse-connection.d.ts.map +1 -1
- package/dist/migrate/parse-connection.js +61 -18
- package/dist/migrate/parse-connection.js.map +1 -1
- package/dist/migrate/parse-datasource.d.ts.map +1 -1
- package/dist/migrate/parse-datasource.js +31 -0
- package/dist/migrate/parse-datasource.js.map +1 -1
- package/dist/migrate/types.d.ts +18 -1
- package/dist/migrate/types.d.ts.map +1 -1
- package/dist/schema/connection.d.ts +49 -6
- package/dist/schema/connection.d.ts.map +1 -1
- package/dist/schema/connection.js +44 -9
- package/dist/schema/connection.js.map +1 -1
- package/dist/schema/connection.test.js +72 -17
- package/dist/schema/connection.test.js.map +1 -1
- package/dist/schema/datasource.d.ts +16 -1
- package/dist/schema/datasource.d.ts.map +1 -1
- package/dist/schema/datasource.js +3 -0
- package/dist/schema/datasource.js.map +1 -1
- package/dist/schema/datasource.test.js +21 -0
- package/dist/schema/datasource.test.js.map +1 -1
- package/dist/schema/params.d.ts +3 -3
- package/dist/schema/params.d.ts.map +1 -1
- package/dist/schema/params.js +3 -3
- package/dist/schema/params.js.map +1 -1
- package/dist/schema/project.d.ts +3 -3
- package/dist/schema/project.js +3 -3
- package/dist/schema/types.d.ts +8 -8
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/schema/types.js +4 -4
- package/dist/schema/types.js.map +1 -1
- package/package.json +1 -1
- package/src/api/api.test.ts +32 -6
- package/src/api/api.ts +14 -3
- package/src/cli/commands/migrate.test.ts +58 -4
- package/src/cli/commands/migrate.ts +6 -4
- package/src/client/base.ts +1 -1
- package/src/generator/connection.test.ts +45 -14
- package/src/generator/connection.ts +30 -2
- package/src/generator/datasource.test.ts +61 -9
- package/src/generator/datasource.ts +38 -1
- package/src/index.ts +12 -1
- package/src/infer/index.ts +3 -3
- package/src/migrate/emit-ts.ts +80 -16
- package/src/migrate/parse-connection.ts +108 -30
- package/src/migrate/parse-datasource.ts +46 -1
- package/src/migrate/types.ts +24 -2
- package/src/schema/connection.test.ts +92 -17
- package/src/schema/connection.ts +86 -10
- package/src/schema/datasource.test.ts +25 -0
- package/src/schema/datasource.ts +21 -1
- package/src/schema/params.ts +3 -3
- package/src/schema/project.ts +3 -3
- package/src/schema/types.ts +10 -10
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
defineKafkaConnection,
|
|
4
|
+
defineS3Connection,
|
|
4
5
|
isConnectionDefinition,
|
|
5
6
|
isKafkaConnectionDefinition,
|
|
7
|
+
isS3ConnectionDefinition,
|
|
6
8
|
getConnectionType,
|
|
7
9
|
} from "./connection.js";
|
|
8
10
|
|
|
9
11
|
describe("Connection Schema", () => {
|
|
10
|
-
describe("
|
|
12
|
+
describe("defineKafkaConnection", () => {
|
|
11
13
|
it("creates a Kafka connection with required fields", () => {
|
|
12
|
-
const conn =
|
|
14
|
+
const conn = defineKafkaConnection("my_kafka", {
|
|
13
15
|
bootstrapServers: "kafka.example.com:9092",
|
|
14
16
|
});
|
|
15
17
|
|
|
@@ -20,7 +22,7 @@ describe("Connection Schema", () => {
|
|
|
20
22
|
});
|
|
21
23
|
|
|
22
24
|
it("creates a Kafka connection with all options", () => {
|
|
23
|
-
const conn =
|
|
25
|
+
const conn = defineKafkaConnection("my_kafka", {
|
|
24
26
|
bootstrapServers: "kafka.example.com:9092",
|
|
25
27
|
securityProtocol: "SASL_SSL",
|
|
26
28
|
saslMechanism: "PLAIN",
|
|
@@ -37,19 +39,19 @@ describe("Connection Schema", () => {
|
|
|
37
39
|
});
|
|
38
40
|
|
|
39
41
|
it("supports different SASL mechanisms", () => {
|
|
40
|
-
const scramConn =
|
|
42
|
+
const scramConn = defineKafkaConnection("scram_kafka", {
|
|
41
43
|
bootstrapServers: "kafka.example.com:9092",
|
|
42
44
|
saslMechanism: "SCRAM-SHA-256",
|
|
43
45
|
});
|
|
44
46
|
expect(scramConn.options.saslMechanism).toBe("SCRAM-SHA-256");
|
|
45
47
|
|
|
46
|
-
const scram512Conn =
|
|
48
|
+
const scram512Conn = defineKafkaConnection("scram512_kafka", {
|
|
47
49
|
bootstrapServers: "kafka.example.com:9092",
|
|
48
50
|
saslMechanism: "SCRAM-SHA-512",
|
|
49
51
|
});
|
|
50
52
|
expect(scram512Conn.options.saslMechanism).toBe("SCRAM-SHA-512");
|
|
51
53
|
|
|
52
|
-
const oauthConn =
|
|
54
|
+
const oauthConn = defineKafkaConnection("oauth_kafka", {
|
|
53
55
|
bootstrapServers: "kafka.example.com:9092",
|
|
54
56
|
saslMechanism: "OAUTHBEARER",
|
|
55
57
|
});
|
|
@@ -57,13 +59,13 @@ describe("Connection Schema", () => {
|
|
|
57
59
|
});
|
|
58
60
|
|
|
59
61
|
it("supports different security protocols", () => {
|
|
60
|
-
const plaintext =
|
|
62
|
+
const plaintext = defineKafkaConnection("plaintext_kafka", {
|
|
61
63
|
bootstrapServers: "localhost:9092",
|
|
62
64
|
securityProtocol: "PLAINTEXT",
|
|
63
65
|
});
|
|
64
66
|
expect(plaintext.options.securityProtocol).toBe("PLAINTEXT");
|
|
65
67
|
|
|
66
|
-
const saslPlaintext =
|
|
68
|
+
const saslPlaintext = defineKafkaConnection("sasl_plaintext_kafka", {
|
|
67
69
|
bootstrapServers: "localhost:9092",
|
|
68
70
|
securityProtocol: "SASL_PLAINTEXT",
|
|
69
71
|
});
|
|
@@ -72,40 +74,88 @@ describe("Connection Schema", () => {
|
|
|
72
74
|
|
|
73
75
|
it("throws error for invalid connection name", () => {
|
|
74
76
|
expect(() =>
|
|
75
|
-
|
|
77
|
+
defineKafkaConnection("123invalid", {
|
|
76
78
|
bootstrapServers: "kafka.example.com:9092",
|
|
77
79
|
})
|
|
78
80
|
).toThrow("Invalid connection name");
|
|
79
81
|
|
|
80
82
|
expect(() =>
|
|
81
|
-
|
|
83
|
+
defineKafkaConnection("my-connection", {
|
|
82
84
|
bootstrapServers: "kafka.example.com:9092",
|
|
83
85
|
})
|
|
84
86
|
).toThrow("Invalid connection name");
|
|
85
87
|
|
|
86
88
|
expect(() =>
|
|
87
|
-
|
|
89
|
+
defineKafkaConnection("", {
|
|
88
90
|
bootstrapServers: "kafka.example.com:9092",
|
|
89
91
|
})
|
|
90
92
|
).toThrow("Invalid connection name");
|
|
91
93
|
});
|
|
92
94
|
|
|
93
95
|
it("allows valid naming patterns", () => {
|
|
94
|
-
const conn1 =
|
|
96
|
+
const conn1 = defineKafkaConnection("_private_kafka", {
|
|
95
97
|
bootstrapServers: "kafka.example.com:9092",
|
|
96
98
|
});
|
|
97
99
|
expect(conn1._name).toBe("_private_kafka");
|
|
98
100
|
|
|
99
|
-
const conn2 =
|
|
101
|
+
const conn2 = defineKafkaConnection("kafka_v2", {
|
|
100
102
|
bootstrapServers: "kafka.example.com:9092",
|
|
101
103
|
});
|
|
102
104
|
expect(conn2._name).toBe("kafka_v2");
|
|
103
105
|
});
|
|
104
106
|
});
|
|
105
107
|
|
|
108
|
+
describe("defineS3Connection", () => {
|
|
109
|
+
it("creates an S3 connection with IAM role auth", () => {
|
|
110
|
+
const conn = defineS3Connection("my_s3", {
|
|
111
|
+
region: "us-east-1",
|
|
112
|
+
arn: "arn:aws:iam::123456789012:role/tinybird-s3-access",
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
expect(conn._name).toBe("my_s3");
|
|
116
|
+
expect(conn._type).toBe("connection");
|
|
117
|
+
expect(conn._connectionType).toBe("s3");
|
|
118
|
+
expect(conn.options.region).toBe("us-east-1");
|
|
119
|
+
expect(conn.options.arn).toBe("arn:aws:iam::123456789012:role/tinybird-s3-access");
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("creates an S3 connection with access key auth", () => {
|
|
123
|
+
const conn = defineS3Connection("my_s3", {
|
|
124
|
+
region: "us-east-1",
|
|
125
|
+
accessKey: '{{ tb_secret("S3_ACCESS_KEY") }}',
|
|
126
|
+
secret: '{{ tb_secret("S3_SECRET") }}',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
expect(conn.options.accessKey).toBe('{{ tb_secret("S3_ACCESS_KEY") }}');
|
|
130
|
+
expect(conn.options.secret).toBe('{{ tb_secret("S3_SECRET") }}');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("throws when auth config is incomplete", () => {
|
|
134
|
+
expect(() =>
|
|
135
|
+
defineS3Connection("my_s3", {
|
|
136
|
+
region: "us-east-1",
|
|
137
|
+
})
|
|
138
|
+
).toThrow("S3 connection requires either `arn` or both `accessKey` and `secret`.");
|
|
139
|
+
|
|
140
|
+
expect(() =>
|
|
141
|
+
defineS3Connection("my_s3", {
|
|
142
|
+
region: "us-east-1",
|
|
143
|
+
accessKey: "key-only",
|
|
144
|
+
})
|
|
145
|
+
).toThrow("S3 connection requires either `arn` or both `accessKey` and `secret`.");
|
|
146
|
+
|
|
147
|
+
expect(() =>
|
|
148
|
+
defineS3Connection("my_s3", {
|
|
149
|
+
region: "us-east-1",
|
|
150
|
+
secret: "secret-only",
|
|
151
|
+
})
|
|
152
|
+
).toThrow("S3 connection requires either `arn` or both `accessKey` and `secret`.");
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
106
156
|
describe("isConnectionDefinition", () => {
|
|
107
157
|
it("returns true for valid connection", () => {
|
|
108
|
-
const conn =
|
|
158
|
+
const conn = defineKafkaConnection("my_kafka", {
|
|
109
159
|
bootstrapServers: "kafka.example.com:9092",
|
|
110
160
|
});
|
|
111
161
|
|
|
@@ -124,7 +174,7 @@ describe("Connection Schema", () => {
|
|
|
124
174
|
|
|
125
175
|
describe("isKafkaConnectionDefinition", () => {
|
|
126
176
|
it("returns true for Kafka connection", () => {
|
|
127
|
-
const conn =
|
|
177
|
+
const conn = defineKafkaConnection("my_kafka", {
|
|
128
178
|
bootstrapServers: "kafka.example.com:9092",
|
|
129
179
|
});
|
|
130
180
|
|
|
@@ -137,13 +187,38 @@ describe("Connection Schema", () => {
|
|
|
137
187
|
});
|
|
138
188
|
});
|
|
139
189
|
|
|
190
|
+
describe("isS3ConnectionDefinition", () => {
|
|
191
|
+
it("returns true for S3 connection", () => {
|
|
192
|
+
const conn = defineS3Connection("my_s3", {
|
|
193
|
+
region: "us-east-1",
|
|
194
|
+
arn: "arn:aws:iam::123456789012:role/tinybird-s3-access",
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
expect(isS3ConnectionDefinition(conn)).toBe(true);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("returns false for non-S3 objects", () => {
|
|
201
|
+
expect(isS3ConnectionDefinition({})).toBe(false);
|
|
202
|
+
expect(isS3ConnectionDefinition(null)).toBe(false);
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
140
206
|
describe("getConnectionType", () => {
|
|
141
207
|
it("returns the connection type", () => {
|
|
142
|
-
const conn =
|
|
208
|
+
const conn = defineKafkaConnection("my_kafka", {
|
|
143
209
|
bootstrapServers: "kafka.example.com:9092",
|
|
144
210
|
});
|
|
145
211
|
|
|
146
212
|
expect(getConnectionType(conn)).toBe("kafka");
|
|
147
213
|
});
|
|
214
|
+
|
|
215
|
+
it("returns the s3 connection type", () => {
|
|
216
|
+
const conn = defineS3Connection("my_s3", {
|
|
217
|
+
region: "us-east-1",
|
|
218
|
+
arn: "arn:aws:iam::123456789012:role/tinybird-s3-access",
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
expect(getConnectionType(conn)).toBe("s3");
|
|
222
|
+
});
|
|
148
223
|
});
|
|
149
224
|
});
|
package/src/schema/connection.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Connection definition for Tinybird
|
|
3
|
-
* Define external connections (Kafka, etc.) as TypeScript with full type safety
|
|
3
|
+
* Define external connections (Kafka, S3, etc.) as TypeScript with full type safety
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
// Symbol for brand typing - use Symbol.for() for global registry
|
|
@@ -50,13 +50,50 @@ export interface KafkaConnectionDefinition {
|
|
|
50
50
|
readonly options: KafkaConnectionOptions;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
/**
|
|
54
|
+
* Options for defining an S3 connection
|
|
55
|
+
*/
|
|
56
|
+
export interface S3ConnectionOptions {
|
|
57
|
+
/** S3 bucket region (for example: us-east-1) */
|
|
58
|
+
region: string;
|
|
59
|
+
/** IAM role ARN used by Tinybird to access the bucket */
|
|
60
|
+
arn?: string;
|
|
61
|
+
/** S3 access key for key/secret auth */
|
|
62
|
+
accessKey?: string;
|
|
63
|
+
/** S3 secret key for key/secret auth */
|
|
64
|
+
secret?: string;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* S3-specific connection definition
|
|
69
|
+
*/
|
|
70
|
+
export interface S3ConnectionDefinition {
|
|
71
|
+
readonly [CONNECTION_BRAND]: true;
|
|
72
|
+
/** Connection name */
|
|
73
|
+
readonly _name: string;
|
|
74
|
+
/** Type marker for inference */
|
|
75
|
+
readonly _type: "connection";
|
|
76
|
+
/** Connection type */
|
|
77
|
+
readonly _connectionType: "s3";
|
|
78
|
+
/** S3 options */
|
|
79
|
+
readonly options: S3ConnectionOptions;
|
|
80
|
+
}
|
|
81
|
+
|
|
53
82
|
/**
|
|
54
83
|
* A connection definition - union of all connection types
|
|
55
84
|
*/
|
|
56
|
-
export type ConnectionDefinition = KafkaConnectionDefinition;
|
|
85
|
+
export type ConnectionDefinition = KafkaConnectionDefinition | S3ConnectionDefinition;
|
|
86
|
+
|
|
87
|
+
function validateConnectionName(name: string): void {
|
|
88
|
+
if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`Invalid connection name: "${name}". Must start with a letter or underscore and contain only alphanumeric characters and underscores.`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
57
94
|
|
|
58
95
|
/**
|
|
59
|
-
*
|
|
96
|
+
* Define a Kafka connection
|
|
60
97
|
*
|
|
61
98
|
* @param name - The connection name (must be valid identifier)
|
|
62
99
|
* @param options - Kafka connection configuration
|
|
@@ -64,9 +101,9 @@ export type ConnectionDefinition = KafkaConnectionDefinition;
|
|
|
64
101
|
*
|
|
65
102
|
* @example
|
|
66
103
|
* ```ts
|
|
67
|
-
* import {
|
|
104
|
+
* import { defineKafkaConnection } from '@tinybirdco/sdk';
|
|
68
105
|
*
|
|
69
|
-
* export const myKafka =
|
|
106
|
+
* export const myKafka = defineKafkaConnection('my_kafka', {
|
|
70
107
|
* bootstrapServers: 'kafka.example.com:9092',
|
|
71
108
|
* securityProtocol: 'SASL_SSL',
|
|
72
109
|
* saslMechanism: 'PLAIN',
|
|
@@ -75,22 +112,54 @@ export type ConnectionDefinition = KafkaConnectionDefinition;
|
|
|
75
112
|
* });
|
|
76
113
|
* ```
|
|
77
114
|
*/
|
|
78
|
-
export function
|
|
115
|
+
export function defineKafkaConnection(
|
|
79
116
|
name: string,
|
|
80
117
|
options: KafkaConnectionOptions
|
|
81
118
|
): KafkaConnectionDefinition {
|
|
82
|
-
|
|
83
|
-
|
|
119
|
+
validateConnectionName(name);
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
[CONNECTION_BRAND]: true,
|
|
123
|
+
_name: name,
|
|
124
|
+
_type: "connection",
|
|
125
|
+
_connectionType: "kafka",
|
|
126
|
+
options,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @deprecated Use defineKafkaConnection instead.
|
|
132
|
+
*/
|
|
133
|
+
export const createKafkaConnection = defineKafkaConnection;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Define an S3 connection
|
|
137
|
+
*
|
|
138
|
+
* @param name - The connection name (must be valid identifier)
|
|
139
|
+
* @param options - S3 connection configuration
|
|
140
|
+
* @returns A connection definition that can be used in a project
|
|
141
|
+
*/
|
|
142
|
+
export function defineS3Connection(
|
|
143
|
+
name: string,
|
|
144
|
+
options: S3ConnectionOptions
|
|
145
|
+
): S3ConnectionDefinition {
|
|
146
|
+
validateConnectionName(name);
|
|
147
|
+
|
|
148
|
+
if (!options.arn && !(options.accessKey && options.secret)) {
|
|
84
149
|
throw new Error(
|
|
85
|
-
|
|
150
|
+
"S3 connection requires either `arn` or both `accessKey` and `secret`."
|
|
86
151
|
);
|
|
87
152
|
}
|
|
88
153
|
|
|
154
|
+
if ((options.accessKey && !options.secret) || (!options.accessKey && options.secret)) {
|
|
155
|
+
throw new Error("S3 connection `accessKey` and `secret` must be provided together.");
|
|
156
|
+
}
|
|
157
|
+
|
|
89
158
|
return {
|
|
90
159
|
[CONNECTION_BRAND]: true,
|
|
91
160
|
_name: name,
|
|
92
161
|
_type: "connection",
|
|
93
|
-
_connectionType: "
|
|
162
|
+
_connectionType: "s3",
|
|
94
163
|
options,
|
|
95
164
|
};
|
|
96
165
|
}
|
|
@@ -114,6 +183,13 @@ export function isKafkaConnectionDefinition(value: unknown): value is KafkaConne
|
|
|
114
183
|
return isConnectionDefinition(value) && value._connectionType === "kafka";
|
|
115
184
|
}
|
|
116
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Check if a value is an S3 connection definition
|
|
188
|
+
*/
|
|
189
|
+
export function isS3ConnectionDefinition(value: unknown): value is S3ConnectionDefinition {
|
|
190
|
+
return isConnectionDefinition(value) && value._connectionType === "s3";
|
|
191
|
+
}
|
|
192
|
+
|
|
117
193
|
/**
|
|
118
194
|
* Get the connection type from a connection definition
|
|
119
195
|
*/
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "./datasource.js";
|
|
10
10
|
import { t } from "./types.js";
|
|
11
11
|
import { engine } from "./engines.js";
|
|
12
|
+
import { defineKafkaConnection, defineS3Connection } from "./connection.js";
|
|
12
13
|
|
|
13
14
|
describe("Datasource Schema", () => {
|
|
14
15
|
describe("defineDatasource", () => {
|
|
@@ -85,6 +86,30 @@ describe("Datasource Schema", () => {
|
|
|
85
86
|
});
|
|
86
87
|
expect(ds2._name).toBe("events_v2");
|
|
87
88
|
});
|
|
89
|
+
|
|
90
|
+
it("throws when both kafka and s3 ingestion are configured", () => {
|
|
91
|
+
const kafkaConn = defineKafkaConnection("my_kafka", {
|
|
92
|
+
bootstrapServers: "kafka.example.com:9092",
|
|
93
|
+
});
|
|
94
|
+
const s3Conn = defineS3Connection("my_s3", {
|
|
95
|
+
region: "us-east-1",
|
|
96
|
+
arn: "arn:aws:iam::123456789012:role/tinybird-s3-access",
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(() =>
|
|
100
|
+
defineDatasource("events", {
|
|
101
|
+
schema: { id: t.string() },
|
|
102
|
+
kafka: {
|
|
103
|
+
connection: kafkaConn,
|
|
104
|
+
topic: "events",
|
|
105
|
+
},
|
|
106
|
+
s3: {
|
|
107
|
+
connection: s3Conn,
|
|
108
|
+
bucketUri: "s3://my-bucket/events/*.csv",
|
|
109
|
+
},
|
|
110
|
+
})
|
|
111
|
+
).toThrow("Datasource cannot define both `kafka` and `s3` ingestion options.");
|
|
112
|
+
});
|
|
88
113
|
});
|
|
89
114
|
|
|
90
115
|
describe("isDatasourceDefinition", () => {
|
package/src/schema/datasource.ts
CHANGED
|
@@ -5,7 +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
|
+
import type { KafkaConnectionDefinition, S3ConnectionDefinition } from "./connection.js";
|
|
9
9
|
import type { TokenDefinition, DatasourceTokenScope } from "./token.js";
|
|
10
10
|
|
|
11
11
|
// Symbol for brand typing - use Symbol.for() for global registry
|
|
@@ -68,6 +68,20 @@ export interface KafkaConfig {
|
|
|
68
68
|
autoOffsetReset?: "earliest" | "latest";
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* S3 import configuration for a datasource
|
|
73
|
+
*/
|
|
74
|
+
export interface S3Config {
|
|
75
|
+
/** S3 connection to use */
|
|
76
|
+
connection: S3ConnectionDefinition;
|
|
77
|
+
/** S3 bucket URI, for example: s3://my-bucket/path/*.csv */
|
|
78
|
+
bucketUri: string;
|
|
79
|
+
/** Import schedule, for example: @auto or @once */
|
|
80
|
+
schedule?: string;
|
|
81
|
+
/** Incremental import lower bound timestamp expression */
|
|
82
|
+
fromTimestamp?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
71
85
|
/**
|
|
72
86
|
* Options for defining a datasource
|
|
73
87
|
*/
|
|
@@ -95,6 +109,8 @@ export interface DatasourceOptions<TSchema extends SchemaDefinition> {
|
|
|
95
109
|
forwardQuery?: string;
|
|
96
110
|
/** Kafka ingestion configuration */
|
|
97
111
|
kafka?: KafkaConfig;
|
|
112
|
+
/** S3 ingestion configuration */
|
|
113
|
+
s3?: S3Config;
|
|
98
114
|
}
|
|
99
115
|
|
|
100
116
|
/**
|
|
@@ -152,6 +168,10 @@ export function defineDatasource<TSchema extends SchemaDefinition>(
|
|
|
152
168
|
);
|
|
153
169
|
}
|
|
154
170
|
|
|
171
|
+
if (options.kafka && options.s3) {
|
|
172
|
+
throw new Error("Datasource cannot define both `kafka` and `s3` ingestion options.");
|
|
173
|
+
}
|
|
174
|
+
|
|
155
175
|
return {
|
|
156
176
|
[DATASOURCE_BRAND]: true,
|
|
157
177
|
_name: name,
|
package/src/schema/params.ts
CHANGED
|
@@ -173,13 +173,13 @@ export const p = {
|
|
|
173
173
|
|
|
174
174
|
// ============ Date/Time Types ============
|
|
175
175
|
|
|
176
|
-
/** Date parameter (YYYY-MM-DD format) */
|
|
176
|
+
/** Date parameter (YYYY-MM-DD format, e.g. 2024-01-15) */
|
|
177
177
|
date: () => createParamValidator<string, "Date">("Date"),
|
|
178
178
|
|
|
179
|
-
/** DateTime parameter (YYYY-MM-DD HH:MM:SS format) */
|
|
179
|
+
/** DateTime parameter (YYYY-MM-DD HH:MM:SS format, e.g. 2024-01-15 10:30:00) */
|
|
180
180
|
dateTime: () => createParamValidator<string, "DateTime">("DateTime"),
|
|
181
181
|
|
|
182
|
-
/** DateTime64 parameter
|
|
182
|
+
/** DateTime64 parameter (YYYY-MM-DD HH:MM:SS[.fraction] format, e.g. 2024-01-15 10:30:00.123) */
|
|
183
183
|
dateTime64: () => createParamValidator<string, "DateTime64">("DateTime64"),
|
|
184
184
|
|
|
185
185
|
// ============ Array Types ============
|
package/src/schema/project.ts
CHANGED
|
@@ -272,13 +272,13 @@ interface TinybirdConstructor {
|
|
|
272
272
|
*
|
|
273
273
|
* // Query a pipe (fully typed)
|
|
274
274
|
* const result = await tinybird.topPages.query({
|
|
275
|
-
* start_date:
|
|
276
|
-
* end_date:
|
|
275
|
+
* start_date: '2024-01-01 00:00:00',
|
|
276
|
+
* end_date: '2024-01-31 23:59:59',
|
|
277
277
|
* });
|
|
278
278
|
*
|
|
279
279
|
* // Ingest an event (fully typed)
|
|
280
280
|
* await tinybird.pageViews.ingest({
|
|
281
|
-
* timestamp:
|
|
281
|
+
* timestamp: '2024-01-15 10:30:00',
|
|
282
282
|
* pathname: '/home',
|
|
283
283
|
* session_id: 'abc123',
|
|
284
284
|
* });
|
package/src/schema/types.ts
CHANGED
|
@@ -203,25 +203,25 @@ export const t = {
|
|
|
203
203
|
|
|
204
204
|
// ============ Date/Time Types ============
|
|
205
205
|
|
|
206
|
-
/** Date -
|
|
207
|
-
date: () => createValidator<
|
|
206
|
+
/** Date - string in YYYY-MM-DD format (e.g. 2024-01-15) */
|
|
207
|
+
date: () => createValidator<string, "Date">("Date"),
|
|
208
208
|
|
|
209
|
-
/** Date32 - extended date range */
|
|
210
|
-
date32: () => createValidator<
|
|
209
|
+
/** Date32 - string in YYYY-MM-DD format (e.g. 2024-01-15, extended date range) */
|
|
210
|
+
date32: () => createValidator<string, "Date32">("Date32"),
|
|
211
211
|
|
|
212
|
-
/** DateTime -
|
|
212
|
+
/** DateTime - string in YYYY-MM-DD HH:MM:SS format (e.g. 2024-01-15 10:30:00) */
|
|
213
213
|
dateTime: (timezone?: string) =>
|
|
214
214
|
timezone
|
|
215
|
-
? createValidator<
|
|
216
|
-
: createValidator<
|
|
215
|
+
? createValidator<string, `DateTime('${string}')`>(`DateTime('${timezone}')`)
|
|
216
|
+
: createValidator<string, "DateTime">("DateTime"),
|
|
217
217
|
|
|
218
|
-
/** DateTime64 -
|
|
218
|
+
/** DateTime64 - string in YYYY-MM-DD HH:MM:SS[.fraction] format (e.g. 2024-01-15 10:30:00.123) */
|
|
219
219
|
dateTime64: (precision: 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 = 3, timezone?: string) =>
|
|
220
220
|
timezone
|
|
221
|
-
? createValidator<
|
|
221
|
+
? createValidator<string, `DateTime64(${number}, '${string}')`>(
|
|
222
222
|
`DateTime64(${precision}, '${timezone}')`
|
|
223
223
|
)
|
|
224
|
-
: createValidator<
|
|
224
|
+
: createValidator<string, `DateTime64(${number})`>(`DateTime64(${precision})`),
|
|
225
225
|
|
|
226
226
|
// ============ Complex Types ============
|
|
227
227
|
|