@tinybirdco/sdk 0.0.41 → 0.0.43
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/LICENSE +7 -0
- package/README.md +29 -3
- package/dist/api/resources.d.ts +72 -1
- package/dist/api/resources.d.ts.map +1 -1
- package/dist/api/resources.js +197 -1
- package/dist/api/resources.js.map +1 -1
- package/dist/api/resources.test.js +82 -1
- package/dist/api/resources.test.js.map +1 -1
- package/dist/cli/commands/migrate.d.ts +11 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/dist/cli/commands/migrate.js +196 -0
- package/dist/cli/commands/migrate.js.map +1 -0
- package/dist/cli/commands/migrate.test.d.ts +2 -0
- package/dist/cli/commands/migrate.test.d.ts.map +1 -0
- package/dist/cli/commands/migrate.test.js +473 -0
- package/dist/cli/commands/migrate.test.js.map +1 -0
- package/dist/cli/commands/pull.d.ts +59 -0
- package/dist/cli/commands/pull.d.ts.map +1 -0
- package/dist/cli/commands/pull.js +104 -0
- package/dist/cli/commands/pull.js.map +1 -0
- package/dist/cli/commands/pull.test.d.ts +2 -0
- package/dist/cli/commands/pull.test.d.ts.map +1 -0
- package/dist/cli/commands/pull.test.js +140 -0
- package/dist/cli/commands/pull.test.js.map +1 -0
- package/dist/cli/config.d.ts +10 -0
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +22 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/index.js +77 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/generator/client.js +2 -2
- package/dist/generator/client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/migrate/discovery.d.ts +7 -0
- package/dist/migrate/discovery.d.ts.map +1 -0
- package/dist/migrate/discovery.js +125 -0
- package/dist/migrate/discovery.js.map +1 -0
- package/dist/migrate/emit-ts.d.ts +4 -0
- package/dist/migrate/emit-ts.d.ts.map +1 -0
- package/dist/migrate/emit-ts.js +387 -0
- package/dist/migrate/emit-ts.js.map +1 -0
- package/dist/migrate/parse-connection.d.ts +3 -0
- package/dist/migrate/parse-connection.d.ts.map +1 -0
- package/dist/migrate/parse-connection.js +74 -0
- package/dist/migrate/parse-connection.js.map +1 -0
- package/dist/migrate/parse-datasource.d.ts +3 -0
- package/dist/migrate/parse-datasource.d.ts.map +1 -0
- package/dist/migrate/parse-datasource.js +324 -0
- package/dist/migrate/parse-datasource.js.map +1 -0
- package/dist/migrate/parse-pipe.d.ts +3 -0
- package/dist/migrate/parse-pipe.d.ts.map +1 -0
- package/dist/migrate/parse-pipe.js +332 -0
- package/dist/migrate/parse-pipe.js.map +1 -0
- package/dist/migrate/parse.d.ts +3 -0
- package/dist/migrate/parse.d.ts.map +1 -0
- package/dist/migrate/parse.js +18 -0
- package/dist/migrate/parse.js.map +1 -0
- package/dist/migrate/parser-utils.d.ts +20 -0
- package/dist/migrate/parser-utils.d.ts.map +1 -0
- package/dist/migrate/parser-utils.js +130 -0
- package/dist/migrate/parser-utils.js.map +1 -0
- package/dist/migrate/types.d.ts +110 -0
- package/dist/migrate/types.d.ts.map +1 -0
- package/dist/migrate/types.js +2 -0
- package/dist/migrate/types.js.map +1 -0
- package/dist/schema/project.d.ts +20 -9
- package/dist/schema/project.d.ts.map +1 -1
- package/dist/schema/project.js +127 -136
- package/dist/schema/project.js.map +1 -1
- package/dist/schema/project.test.js +22 -0
- package/dist/schema/project.test.js.map +1 -1
- package/package.json +2 -1
- package/src/api/resources.test.ts +121 -0
- package/src/api/resources.ts +292 -1
- package/src/cli/commands/migrate.test.ts +564 -0
- package/src/cli/commands/migrate.ts +240 -0
- package/src/cli/commands/pull.test.ts +173 -0
- package/src/cli/commands/pull.ts +177 -0
- package/src/cli/config.ts +26 -0
- package/src/cli/index.ts +112 -0
- package/src/generator/client.ts +2 -2
- package/src/index.ts +1 -1
- package/src/migrate/discovery.ts +151 -0
- package/src/migrate/emit-ts.ts +469 -0
- package/src/migrate/parse-connection.ts +128 -0
- package/src/migrate/parse-datasource.ts +453 -0
- package/src/migrate/parse-pipe.ts +518 -0
- package/src/migrate/parse.ts +20 -0
- package/src/migrate/parser-utils.ts +160 -0
- package/src/migrate/types.ts +125 -0
- package/src/schema/project.test.ts +28 -0
- package/src/schema/project.ts +173 -181
package/src/api/resources.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tinybird Resources API client
|
|
3
|
-
* Functions to list and fetch
|
|
3
|
+
* Functions to list and fetch resources from a workspace
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { WorkspaceApiConfig } from "./workspaces.js";
|
|
@@ -248,6 +248,36 @@ interface PipeDetailResponse {
|
|
|
248
248
|
materialized_datasource?: string;
|
|
249
249
|
}
|
|
250
250
|
|
|
251
|
+
// ============ Connector/Datafile Types ============
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Resource file type returned by pull operations
|
|
255
|
+
*/
|
|
256
|
+
export type ResourceFileType = "datasource" | "pipe" | "connection";
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Raw Tinybird datafile pulled from API
|
|
260
|
+
*/
|
|
261
|
+
export interface ResourceFile {
|
|
262
|
+
/** Resource name (without extension) */
|
|
263
|
+
name: string;
|
|
264
|
+
/** Resource kind */
|
|
265
|
+
type: ResourceFileType;
|
|
266
|
+
/** Filename with extension */
|
|
267
|
+
filename: string;
|
|
268
|
+
/** Raw datafile content */
|
|
269
|
+
content: string;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Grouped resource files returned by pull operations
|
|
274
|
+
*/
|
|
275
|
+
export interface PulledResourceFiles {
|
|
276
|
+
datasources: ResourceFile[];
|
|
277
|
+
pipes: ResourceFile[];
|
|
278
|
+
connections: ResourceFile[];
|
|
279
|
+
}
|
|
280
|
+
|
|
251
281
|
// ============ API Helper ============
|
|
252
282
|
|
|
253
283
|
/**
|
|
@@ -290,6 +320,107 @@ async function handleApiResponse<T>(
|
|
|
290
320
|
return response.json() as Promise<T>;
|
|
291
321
|
}
|
|
292
322
|
|
|
323
|
+
/**
|
|
324
|
+
* Handle API text response and throw appropriate errors
|
|
325
|
+
*/
|
|
326
|
+
async function handleApiTextResponse(
|
|
327
|
+
response: Response,
|
|
328
|
+
endpoint: string
|
|
329
|
+
): Promise<string> {
|
|
330
|
+
if (response.status === 401) {
|
|
331
|
+
throw new ResourceApiError("Invalid or expired token", 401, endpoint);
|
|
332
|
+
}
|
|
333
|
+
if (response.status === 403) {
|
|
334
|
+
throw new ResourceApiError(
|
|
335
|
+
"Insufficient permissions to access resources",
|
|
336
|
+
403,
|
|
337
|
+
endpoint
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
if (response.status === 404) {
|
|
341
|
+
throw new ResourceApiError("Resource not found", 404, endpoint);
|
|
342
|
+
}
|
|
343
|
+
if (!response.ok) {
|
|
344
|
+
const body = await response.text();
|
|
345
|
+
throw new ResourceApiError(
|
|
346
|
+
`API request failed: ${response.status} ${response.statusText}`,
|
|
347
|
+
response.status,
|
|
348
|
+
endpoint,
|
|
349
|
+
body
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
return response.text();
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Extract resource names from API response arrays
|
|
357
|
+
*/
|
|
358
|
+
function extractNames(
|
|
359
|
+
data: Record<string, unknown>,
|
|
360
|
+
keys: string[]
|
|
361
|
+
): string[] {
|
|
362
|
+
for (const key of keys) {
|
|
363
|
+
const value = data[key];
|
|
364
|
+
if (!Array.isArray(value)) {
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const names = value
|
|
369
|
+
.map((item) => {
|
|
370
|
+
if (typeof item === "string") {
|
|
371
|
+
return item;
|
|
372
|
+
}
|
|
373
|
+
if (
|
|
374
|
+
typeof item === "object" &&
|
|
375
|
+
item !== null &&
|
|
376
|
+
"name" in item &&
|
|
377
|
+
typeof (item as { name: unknown }).name === "string"
|
|
378
|
+
) {
|
|
379
|
+
return (item as { name: string }).name;
|
|
380
|
+
}
|
|
381
|
+
return null;
|
|
382
|
+
})
|
|
383
|
+
.filter((name): name is string => name !== null);
|
|
384
|
+
|
|
385
|
+
return names;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return [];
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Fetch text resource from the first successful endpoint.
|
|
393
|
+
* Falls back on 404 responses.
|
|
394
|
+
*/
|
|
395
|
+
async function fetchTextFromAnyEndpoint(
|
|
396
|
+
config: WorkspaceApiConfig,
|
|
397
|
+
endpoints: string[]
|
|
398
|
+
): Promise<string> {
|
|
399
|
+
let lastNotFound: ResourceApiError | null = null;
|
|
400
|
+
|
|
401
|
+
for (const endpoint of endpoints) {
|
|
402
|
+
const url = new URL(endpoint, config.baseUrl);
|
|
403
|
+
const response = await tinybirdFetch(url.toString(), {
|
|
404
|
+
method: "GET",
|
|
405
|
+
headers: {
|
|
406
|
+
Authorization: `Bearer ${config.token}`,
|
|
407
|
+
},
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
if (response.status === 404) {
|
|
411
|
+
lastNotFound = new ResourceApiError("Resource not found", 404, endpoint);
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
return handleApiTextResponse(response, endpoint);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
throw (
|
|
419
|
+
lastNotFound ??
|
|
420
|
+
new ResourceApiError("Resource not found", 404, endpoints[0] ?? "unknown")
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
293
424
|
// ============ Datasource API ============
|
|
294
425
|
|
|
295
426
|
/**
|
|
@@ -424,6 +555,117 @@ export async function listPipes(
|
|
|
424
555
|
return data.pipes.map((p) => p.name);
|
|
425
556
|
}
|
|
426
557
|
|
|
558
|
+
/**
|
|
559
|
+
* List all pipes from the v1 endpoint.
|
|
560
|
+
* Falls back to v0 when v1 is unavailable.
|
|
561
|
+
*
|
|
562
|
+
* @param config - API configuration
|
|
563
|
+
* @returns Array of pipe names
|
|
564
|
+
*/
|
|
565
|
+
export async function listPipesV1(config: WorkspaceApiConfig): Promise<string[]> {
|
|
566
|
+
const endpoint = "/v1/pipes";
|
|
567
|
+
const url = new URL(endpoint, config.baseUrl);
|
|
568
|
+
|
|
569
|
+
const response = await tinybirdFetch(url.toString(), {
|
|
570
|
+
method: "GET",
|
|
571
|
+
headers: {
|
|
572
|
+
Authorization: `Bearer ${config.token}`,
|
|
573
|
+
},
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
// Older/self-hosted versions may not expose /v1/pipes.
|
|
577
|
+
if (response.status === 404) {
|
|
578
|
+
return listPipes(config);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
const data = await handleApiResponse<Record<string, unknown>>(response, endpoint);
|
|
582
|
+
return extractNames(data, ["pipes", "data"]);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Get a datasource as native .datasource text
|
|
587
|
+
*
|
|
588
|
+
* @param config - API configuration
|
|
589
|
+
* @param name - Datasource name
|
|
590
|
+
* @returns Raw .datasource content
|
|
591
|
+
*/
|
|
592
|
+
export async function getDatasourceFile(
|
|
593
|
+
config: WorkspaceApiConfig,
|
|
594
|
+
name: string
|
|
595
|
+
): Promise<string> {
|
|
596
|
+
const encoded = encodeURIComponent(name);
|
|
597
|
+
return fetchTextFromAnyEndpoint(config, [
|
|
598
|
+
`/v0/datasources/${encoded}.datasource`,
|
|
599
|
+
`/v0/datasources/${encoded}?format=datasource`,
|
|
600
|
+
]);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Get a pipe as native .pipe text
|
|
605
|
+
*
|
|
606
|
+
* @param config - API configuration
|
|
607
|
+
* @param name - Pipe name
|
|
608
|
+
* @returns Raw .pipe content
|
|
609
|
+
*/
|
|
610
|
+
export async function getPipeFile(
|
|
611
|
+
config: WorkspaceApiConfig,
|
|
612
|
+
name: string
|
|
613
|
+
): Promise<string> {
|
|
614
|
+
const encoded = encodeURIComponent(name);
|
|
615
|
+
return fetchTextFromAnyEndpoint(config, [
|
|
616
|
+
`/v1/pipes/${encoded}.pipe`,
|
|
617
|
+
`/v0/pipes/${encoded}.pipe`,
|
|
618
|
+
`/v1/pipes/${encoded}?format=pipe`,
|
|
619
|
+
`/v0/pipes/${encoded}?format=pipe`,
|
|
620
|
+
]);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* List all connectors in the workspace
|
|
625
|
+
*
|
|
626
|
+
* @param config - API configuration
|
|
627
|
+
* @returns Array of connector names
|
|
628
|
+
*/
|
|
629
|
+
export async function listConnectors(
|
|
630
|
+
config: WorkspaceApiConfig
|
|
631
|
+
): Promise<string[]> {
|
|
632
|
+
const endpoint = "/v0/connectors";
|
|
633
|
+
const url = new URL(endpoint, config.baseUrl);
|
|
634
|
+
|
|
635
|
+
const response = await tinybirdFetch(url.toString(), {
|
|
636
|
+
method: "GET",
|
|
637
|
+
headers: {
|
|
638
|
+
Authorization: `Bearer ${config.token}`,
|
|
639
|
+
},
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
// Not all workspaces expose connectors. Treat missing endpoint as no connectors.
|
|
643
|
+
if (response.status === 404) {
|
|
644
|
+
return [];
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const data = await handleApiResponse<Record<string, unknown>>(response, endpoint);
|
|
648
|
+
return extractNames(data, ["connectors", "connections"]);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Get a connector as native .connection text
|
|
653
|
+
*
|
|
654
|
+
* @param config - API configuration
|
|
655
|
+
* @param name - Connector name
|
|
656
|
+
* @returns Raw .connection content
|
|
657
|
+
*/
|
|
658
|
+
export async function getConnectorFile(
|
|
659
|
+
config: WorkspaceApiConfig,
|
|
660
|
+
name: string
|
|
661
|
+
): Promise<string> {
|
|
662
|
+
const encoded = encodeURIComponent(name);
|
|
663
|
+
return fetchTextFromAnyEndpoint(config, [
|
|
664
|
+
`/v0/connectors/${encoded}.connection`,
|
|
665
|
+
`/v0/connectors/${encoded}?format=connection`,
|
|
666
|
+
]);
|
|
667
|
+
}
|
|
668
|
+
|
|
427
669
|
/**
|
|
428
670
|
* Get detailed information about a specific pipe
|
|
429
671
|
*
|
|
@@ -541,6 +783,55 @@ export async function fetchAllResources(
|
|
|
541
783
|
return { datasources, pipes };
|
|
542
784
|
}
|
|
543
785
|
|
|
786
|
+
/**
|
|
787
|
+
* Pull all datasource/pipe/connector datafiles from a workspace
|
|
788
|
+
*
|
|
789
|
+
* @param config - API configuration
|
|
790
|
+
* @returns Raw resource files grouped by type
|
|
791
|
+
*/
|
|
792
|
+
export async function pullAllResourceFiles(
|
|
793
|
+
config: WorkspaceApiConfig
|
|
794
|
+
): Promise<PulledResourceFiles> {
|
|
795
|
+
const [datasourceNames, pipeNames, connectorNames] = await Promise.all([
|
|
796
|
+
listDatasources(config),
|
|
797
|
+
listPipesV1(config),
|
|
798
|
+
listConnectors(config),
|
|
799
|
+
]);
|
|
800
|
+
|
|
801
|
+
const [datasources, pipes, connections] = await Promise.all([
|
|
802
|
+
Promise.all(
|
|
803
|
+
datasourceNames.map(async (name): Promise<ResourceFile> => ({
|
|
804
|
+
name,
|
|
805
|
+
type: "datasource",
|
|
806
|
+
filename: `${name}.datasource`,
|
|
807
|
+
content: await getDatasourceFile(config, name),
|
|
808
|
+
}))
|
|
809
|
+
),
|
|
810
|
+
Promise.all(
|
|
811
|
+
pipeNames.map(async (name): Promise<ResourceFile> => ({
|
|
812
|
+
name,
|
|
813
|
+
type: "pipe",
|
|
814
|
+
filename: `${name}.pipe`,
|
|
815
|
+
content: await getPipeFile(config, name),
|
|
816
|
+
}))
|
|
817
|
+
),
|
|
818
|
+
Promise.all(
|
|
819
|
+
connectorNames.map(async (name): Promise<ResourceFile> => ({
|
|
820
|
+
name,
|
|
821
|
+
type: "connection",
|
|
822
|
+
filename: `${name}.connection`,
|
|
823
|
+
content: await getConnectorFile(config, name),
|
|
824
|
+
}))
|
|
825
|
+
),
|
|
826
|
+
]);
|
|
827
|
+
|
|
828
|
+
return {
|
|
829
|
+
datasources,
|
|
830
|
+
pipes,
|
|
831
|
+
connections,
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
|
|
544
835
|
/**
|
|
545
836
|
* Check if a workspace has any resources
|
|
546
837
|
*
|