@tinybirdco/sdk 0.0.4 → 0.0.7

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.
Files changed (165) hide show
  1. package/README.md +52 -13
  2. package/dist/api/branches.d.ts.map +1 -1
  3. package/dist/api/branches.js +6 -5
  4. package/dist/api/branches.js.map +1 -1
  5. package/dist/api/branches.test.js +32 -6
  6. package/dist/api/branches.test.js.map +1 -1
  7. package/dist/api/build.d.ts.map +1 -1
  8. package/dist/api/build.js +2 -1
  9. package/dist/api/build.js.map +1 -1
  10. package/dist/api/deploy.d.ts +42 -3
  11. package/dist/api/deploy.d.ts.map +1 -1
  12. package/dist/api/deploy.js +162 -19
  13. package/dist/api/deploy.js.map +1 -1
  14. package/dist/api/deploy.test.js +83 -31
  15. package/dist/api/deploy.test.js.map +1 -1
  16. package/dist/api/fetcher.d.ts +6 -0
  17. package/dist/api/fetcher.d.ts.map +1 -0
  18. package/dist/api/fetcher.js +13 -0
  19. package/dist/api/fetcher.js.map +1 -0
  20. package/dist/api/local.d.ts.map +1 -1
  21. package/dist/api/local.js +5 -4
  22. package/dist/api/local.js.map +1 -1
  23. package/dist/api/local.test.js.map +1 -1
  24. package/dist/api/resources.d.ts +178 -0
  25. package/dist/api/resources.d.ts.map +1 -0
  26. package/dist/api/resources.js +245 -0
  27. package/dist/api/resources.js.map +1 -0
  28. package/dist/api/resources.test.d.ts +2 -0
  29. package/dist/api/resources.test.d.ts.map +1 -0
  30. package/dist/api/resources.test.js +255 -0
  31. package/dist/api/resources.test.js.map +1 -0
  32. package/dist/api/workspaces.d.ts.map +1 -1
  33. package/dist/api/workspaces.js +2 -1
  34. package/dist/api/workspaces.js.map +1 -1
  35. package/dist/api/workspaces.test.js +9 -1
  36. package/dist/api/workspaces.test.js.map +1 -1
  37. package/dist/cli/auth.d.ts.map +1 -1
  38. package/dist/cli/auth.js +2 -1
  39. package/dist/cli/auth.js.map +1 -1
  40. package/dist/cli/commands/build.d.ts +3 -4
  41. package/dist/cli/commands/build.d.ts.map +1 -1
  42. package/dist/cli/commands/build.js +23 -25
  43. package/dist/cli/commands/build.js.map +1 -1
  44. package/dist/cli/commands/deploy.d.ts +41 -0
  45. package/dist/cli/commands/deploy.d.ts.map +1 -0
  46. package/dist/cli/commands/deploy.js +92 -0
  47. package/dist/cli/commands/deploy.js.map +1 -0
  48. package/dist/cli/commands/dev.d.ts.map +1 -1
  49. package/dist/cli/commands/dev.js +7 -3
  50. package/dist/cli/commands/dev.js.map +1 -1
  51. package/dist/cli/commands/init.d.ts +38 -1
  52. package/dist/cli/commands/init.d.ts.map +1 -1
  53. package/dist/cli/commands/init.js +434 -23
  54. package/dist/cli/commands/init.js.map +1 -1
  55. package/dist/cli/commands/init.test.js +190 -30
  56. package/dist/cli/commands/init.test.js.map +1 -1
  57. package/dist/cli/index.js +80 -15
  58. package/dist/cli/index.js.map +1 -1
  59. package/dist/cli/utils/package-manager.d.ts +8 -0
  60. package/dist/cli/utils/package-manager.d.ts.map +1 -0
  61. package/dist/cli/utils/package-manager.js +45 -0
  62. package/dist/cli/utils/package-manager.js.map +1 -0
  63. package/dist/cli/utils/package-manager.test.d.ts +2 -0
  64. package/dist/cli/utils/package-manager.test.d.ts.map +1 -0
  65. package/dist/cli/utils/package-manager.test.js +85 -0
  66. package/dist/cli/utils/package-manager.test.js.map +1 -0
  67. package/dist/client/base.d.ts.map +1 -1
  68. package/dist/client/base.js +2 -1
  69. package/dist/client/base.js.map +1 -1
  70. package/dist/codegen/index.d.ts +39 -0
  71. package/dist/codegen/index.d.ts.map +1 -0
  72. package/dist/codegen/index.js +300 -0
  73. package/dist/codegen/index.js.map +1 -0
  74. package/dist/codegen/index.test.d.ts +2 -0
  75. package/dist/codegen/index.test.d.ts.map +1 -0
  76. package/dist/codegen/index.test.js +310 -0
  77. package/dist/codegen/index.test.js.map +1 -0
  78. package/dist/codegen/type-mapper.d.ts +20 -0
  79. package/dist/codegen/type-mapper.d.ts.map +1 -0
  80. package/dist/codegen/type-mapper.js +238 -0
  81. package/dist/codegen/type-mapper.js.map +1 -0
  82. package/dist/codegen/type-mapper.test.d.ts +2 -0
  83. package/dist/codegen/type-mapper.test.d.ts.map +1 -0
  84. package/dist/codegen/type-mapper.test.js +167 -0
  85. package/dist/codegen/type-mapper.test.js.map +1 -0
  86. package/dist/codegen/utils.d.ts +46 -0
  87. package/dist/codegen/utils.d.ts.map +1 -0
  88. package/dist/codegen/utils.js +141 -0
  89. package/dist/codegen/utils.js.map +1 -0
  90. package/dist/codegen/utils.test.d.ts +2 -0
  91. package/dist/codegen/utils.test.d.ts.map +1 -0
  92. package/dist/codegen/utils.test.js +178 -0
  93. package/dist/codegen/utils.test.js.map +1 -0
  94. package/dist/generator/index.d.ts +3 -0
  95. package/dist/generator/index.d.ts.map +1 -1
  96. package/dist/generator/index.js +17 -1
  97. package/dist/generator/index.js.map +1 -1
  98. package/dist/generator/index.test.js +104 -1
  99. package/dist/generator/index.test.js.map +1 -1
  100. package/dist/generator/loader.d.ts +15 -0
  101. package/dist/generator/loader.d.ts.map +1 -1
  102. package/dist/generator/loader.js +24 -0
  103. package/dist/generator/loader.js.map +1 -1
  104. package/dist/schema/connection.d.ts.map +1 -1
  105. package/dist/schema/connection.js +3 -2
  106. package/dist/schema/connection.js.map +1 -1
  107. package/dist/schema/datasource.d.ts.map +1 -1
  108. package/dist/schema/datasource.js +3 -2
  109. package/dist/schema/datasource.js.map +1 -1
  110. package/dist/schema/params.d.ts.map +1 -1
  111. package/dist/schema/params.js +3 -2
  112. package/dist/schema/params.js.map +1 -1
  113. package/dist/schema/pipe.d.ts +2 -2
  114. package/dist/schema/pipe.d.ts.map +1 -1
  115. package/dist/schema/pipe.js +4 -4
  116. package/dist/schema/pipe.js.map +1 -1
  117. package/dist/schema/project.d.ts.map +1 -1
  118. package/dist/schema/project.js +3 -2
  119. package/dist/schema/project.js.map +1 -1
  120. package/dist/schema/types.d.ts.map +1 -1
  121. package/dist/schema/types.js +3 -2
  122. package/dist/schema/types.js.map +1 -1
  123. package/dist/test/handlers.d.ts +49 -0
  124. package/dist/test/handlers.d.ts.map +1 -1
  125. package/dist/test/handlers.js +45 -0
  126. package/dist/test/handlers.js.map +1 -1
  127. package/package.json +4 -2
  128. package/src/api/branches.test.ts +65 -57
  129. package/src/api/branches.ts +7 -5
  130. package/src/api/build.ts +2 -1
  131. package/src/api/deploy.test.ts +141 -36
  132. package/src/api/deploy.ts +231 -23
  133. package/src/api/fetcher.ts +17 -0
  134. package/src/api/local.test.ts +43 -31
  135. package/src/api/local.ts +5 -4
  136. package/src/api/resources.test.ts +332 -0
  137. package/src/api/resources.ts +555 -0
  138. package/src/api/workspaces.test.ts +15 -9
  139. package/src/api/workspaces.ts +3 -1
  140. package/src/cli/auth.ts +2 -1
  141. package/src/cli/commands/build.ts +29 -33
  142. package/src/cli/commands/deploy.ts +131 -0
  143. package/src/cli/commands/dev.ts +10 -3
  144. package/src/cli/commands/init.test.ts +239 -30
  145. package/src/cli/commands/init.ts +548 -26
  146. package/src/cli/index.ts +117 -20
  147. package/src/cli/utils/package-manager.test.ts +118 -0
  148. package/src/cli/utils/package-manager.ts +44 -0
  149. package/src/client/base.ts +3 -2
  150. package/src/codegen/index.test.ts +367 -0
  151. package/src/codegen/index.ts +379 -0
  152. package/src/codegen/type-mapper.test.ts +224 -0
  153. package/src/codegen/type-mapper.ts +265 -0
  154. package/src/codegen/utils.test.ts +221 -0
  155. package/src/codegen/utils.ts +174 -0
  156. package/src/generator/index.test.ts +121 -1
  157. package/src/generator/index.ts +19 -1
  158. package/src/generator/loader.ts +43 -0
  159. package/src/schema/connection.ts +3 -2
  160. package/src/schema/datasource.ts +3 -2
  161. package/src/schema/params.ts +3 -2
  162. package/src/schema/pipe.ts +4 -4
  163. package/src/schema/project.ts +3 -2
  164. package/src/schema/types.ts +3 -2
  165. package/src/test/handlers.ts +58 -0
@@ -0,0 +1,332 @@
1
+ import { describe, it, expect, beforeAll, afterAll, afterEach } from "vitest";
2
+ import { setupServer } from "msw/node";
3
+ import { http, HttpResponse } from "msw";
4
+ import {
5
+ listDatasources,
6
+ getDatasource,
7
+ listPipes,
8
+ getPipe,
9
+ fetchAllResources,
10
+ hasResources,
11
+ ResourceApiError,
12
+ } from "./resources.js";
13
+
14
+ const BASE_URL = "https://api.tinybird.co";
15
+ const TOKEN = "test-token";
16
+
17
+ const handlers = [
18
+ // List datasources
19
+ http.get(`${BASE_URL}/v0/datasources`, () => {
20
+ return HttpResponse.json({
21
+ datasources: [
22
+ { name: "events", description: "Event data" },
23
+ { name: "users", description: "User data" },
24
+ ],
25
+ });
26
+ }),
27
+
28
+ // Get datasource detail - events
29
+ http.get(`${BASE_URL}/v0/datasources/events`, () => {
30
+ return HttpResponse.json({
31
+ name: "events",
32
+ description: "Event tracking data",
33
+ columns: [
34
+ { name: "timestamp", type: "DateTime" },
35
+ { name: "event_name", type: "LowCardinality(String)" },
36
+ { name: "user_id", type: "Nullable(String)" },
37
+ ],
38
+ engine: "MergeTree",
39
+ sorting_key: "event_name, timestamp",
40
+ partition_key: "toYYYYMM(timestamp)",
41
+ });
42
+ }),
43
+
44
+ // Get datasource detail - users
45
+ http.get(`${BASE_URL}/v0/datasources/users`, () => {
46
+ return HttpResponse.json({
47
+ name: "users",
48
+ description: "User data",
49
+ columns: [
50
+ { name: "user_id", type: "String" },
51
+ { name: "email", type: "String" },
52
+ { name: "created_at", type: "DateTime" },
53
+ ],
54
+ engine: "MergeTree",
55
+ sorting_key: "user_id",
56
+ });
57
+ }),
58
+
59
+ // List pipes
60
+ http.get(`${BASE_URL}/v0/pipes`, () => {
61
+ return HttpResponse.json({
62
+ pipes: [
63
+ { name: "top_events", type: "endpoint" },
64
+ { name: "daily_stats_mv", type: "materialized" },
65
+ ],
66
+ });
67
+ }),
68
+
69
+ // Get pipe detail - endpoint
70
+ http.get(`${BASE_URL}/v0/pipes/top_events`, () => {
71
+ return HttpResponse.json({
72
+ name: "top_events",
73
+ description: "Get top events by count",
74
+ endpoint: "/v0/pipes/top_events.json",
75
+ nodes: [
76
+ {
77
+ name: "aggregated",
78
+ sql: "SELECT event_name, count() AS cnt FROM events GROUP BY event_name ORDER BY cnt DESC LIMIT {{Int32(limit, 10)}}",
79
+ params: [
80
+ { name: "limit", type: "Int32", default: 10, required: false },
81
+ ],
82
+ columns: [
83
+ { name: "event_name", type: "String" },
84
+ { name: "cnt", type: "UInt64" },
85
+ ],
86
+ },
87
+ ],
88
+ });
89
+ }),
90
+
91
+ // Get pipe detail - materialized
92
+ http.get(`${BASE_URL}/v0/pipes/daily_stats_mv`, () => {
93
+ return HttpResponse.json({
94
+ name: "daily_stats_mv",
95
+ description: "Daily aggregation",
96
+ materialized_datasource: "daily_stats",
97
+ nodes: [
98
+ {
99
+ name: "aggregate",
100
+ sql: "SELECT toDate(timestamp) AS date, count() AS cnt FROM events GROUP BY date",
101
+ },
102
+ ],
103
+ });
104
+ }),
105
+
106
+ // Get pipe detail - copy
107
+ http.get(`${BASE_URL}/v0/pipes/daily_snapshot`, () => {
108
+ return HttpResponse.json({
109
+ name: "daily_snapshot",
110
+ description: "Daily snapshot copy",
111
+ copy_target_datasource: "snapshots",
112
+ copy_schedule: "0 0 * * *",
113
+ copy_mode: "append",
114
+ nodes: [
115
+ {
116
+ name: "snapshot",
117
+ sql: "SELECT * FROM events WHERE date = today()",
118
+ },
119
+ ],
120
+ });
121
+ }),
122
+ ];
123
+
124
+ const server = setupServer(...handlers);
125
+
126
+ beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
127
+ afterAll(() => server.close());
128
+ afterEach(() => server.resetHandlers());
129
+
130
+ describe("listDatasources", () => {
131
+ it("returns array of datasource names", async () => {
132
+ const result = await listDatasources({ baseUrl: BASE_URL, token: TOKEN });
133
+
134
+ expect(result).toEqual(["events", "users"]);
135
+ });
136
+
137
+ it("handles empty workspace", async () => {
138
+ server.use(
139
+ http.get(`${BASE_URL}/v0/datasources`, () => {
140
+ return HttpResponse.json({ datasources: [] });
141
+ })
142
+ );
143
+
144
+ const result = await listDatasources({ baseUrl: BASE_URL, token: TOKEN });
145
+
146
+ expect(result).toEqual([]);
147
+ });
148
+
149
+ it("throws ResourceApiError on 401", async () => {
150
+ server.use(
151
+ http.get(`${BASE_URL}/v0/datasources`, () => {
152
+ return new HttpResponse(null, { status: 401 });
153
+ })
154
+ );
155
+
156
+ await expect(
157
+ listDatasources({ baseUrl: BASE_URL, token: TOKEN })
158
+ ).rejects.toThrow(ResourceApiError);
159
+ });
160
+
161
+ it("throws ResourceApiError on 403", async () => {
162
+ server.use(
163
+ http.get(`${BASE_URL}/v0/datasources`, () => {
164
+ return new HttpResponse(null, { status: 403 });
165
+ })
166
+ );
167
+
168
+ await expect(
169
+ listDatasources({ baseUrl: BASE_URL, token: TOKEN })
170
+ ).rejects.toThrow("Insufficient permissions");
171
+ });
172
+ });
173
+
174
+ describe("getDatasource", () => {
175
+ it("returns datasource info with columns and engine", async () => {
176
+ const result = await getDatasource({ baseUrl: BASE_URL, token: TOKEN }, "events");
177
+
178
+ expect(result.name).toBe("events");
179
+ expect(result.description).toBe("Event tracking data");
180
+ expect(result.columns).toHaveLength(3);
181
+ expect(result.columns[0]).toEqual({ name: "timestamp", type: "DateTime" });
182
+ expect(result.columns[1]).toEqual({ name: "event_name", type: "LowCardinality(String)" });
183
+ expect(result.columns[2]).toEqual({ name: "user_id", type: "Nullable(String)" });
184
+ expect(result.engine.type).toBe("MergeTree");
185
+ expect(result.engine.sorting_key).toBe("event_name, timestamp");
186
+ expect(result.engine.partition_key).toBe("toYYYYMM(timestamp)");
187
+ });
188
+
189
+ it("throws ResourceApiError on 404", async () => {
190
+ server.use(
191
+ http.get(`${BASE_URL}/v0/datasources/nonexistent`, () => {
192
+ return new HttpResponse(null, { status: 404 });
193
+ })
194
+ );
195
+
196
+ await expect(
197
+ getDatasource({ baseUrl: BASE_URL, token: TOKEN }, "nonexistent")
198
+ ).rejects.toThrow("Resource not found");
199
+ });
200
+ });
201
+
202
+ describe("listPipes", () => {
203
+ it("returns array of pipe names", async () => {
204
+ const result = await listPipes({ baseUrl: BASE_URL, token: TOKEN });
205
+
206
+ expect(result).toEqual(["top_events", "daily_stats_mv"]);
207
+ });
208
+ });
209
+
210
+ describe("getPipe", () => {
211
+ it("returns endpoint pipe info", async () => {
212
+ const result = await getPipe({ baseUrl: BASE_URL, token: TOKEN }, "top_events");
213
+
214
+ expect(result.name).toBe("top_events");
215
+ expect(result.description).toBe("Get top events by count");
216
+ expect(result.type).toBe("endpoint");
217
+ expect(result.nodes).toHaveLength(1);
218
+ expect(result.nodes[0].name).toBe("aggregated");
219
+ expect(result.params).toHaveLength(1);
220
+ expect(result.params[0]).toEqual({
221
+ name: "limit",
222
+ type: "Int32",
223
+ default: 10,
224
+ required: false,
225
+ });
226
+ expect(result.output_columns).toHaveLength(2);
227
+ });
228
+
229
+ it("returns materialized pipe info", async () => {
230
+ const result = await getPipe({ baseUrl: BASE_URL, token: TOKEN }, "daily_stats_mv");
231
+
232
+ expect(result.name).toBe("daily_stats_mv");
233
+ expect(result.type).toBe("materialized");
234
+ expect(result.materialized?.datasource).toBe("daily_stats");
235
+ });
236
+
237
+ it("returns copy pipe info", async () => {
238
+ const result = await getPipe({ baseUrl: BASE_URL, token: TOKEN }, "daily_snapshot");
239
+
240
+ expect(result.name).toBe("daily_snapshot");
241
+ expect(result.type).toBe("copy");
242
+ expect(result.copy?.target_datasource).toBe("snapshots");
243
+ expect(result.copy?.copy_schedule).toBe("0 0 * * *");
244
+ expect(result.copy?.copy_mode).toBe("append");
245
+ });
246
+ });
247
+
248
+ describe("fetchAllResources", () => {
249
+ it("fetches all datasources and pipes with details", async () => {
250
+ const result = await fetchAllResources({ baseUrl: BASE_URL, token: TOKEN });
251
+
252
+ expect(result.datasources).toHaveLength(2);
253
+ expect(result.pipes).toHaveLength(2);
254
+
255
+ // Verify datasource details were fetched
256
+ const events = result.datasources.find((ds) => ds.name === "events");
257
+ expect(events?.columns).toBeDefined();
258
+ expect(events?.engine).toBeDefined();
259
+
260
+ // Verify pipe details were fetched
261
+ const topEvents = result.pipes.find((p) => p.name === "top_events");
262
+ expect(topEvents?.nodes).toBeDefined();
263
+ expect(topEvents?.type).toBe("endpoint");
264
+ });
265
+ });
266
+
267
+ describe("hasResources", () => {
268
+ it("returns true when workspace has resources", async () => {
269
+ const result = await hasResources({ baseUrl: BASE_URL, token: TOKEN });
270
+
271
+ expect(result).toBe(true);
272
+ });
273
+
274
+ it("returns false for empty workspace", async () => {
275
+ server.use(
276
+ http.get(`${BASE_URL}/v0/datasources`, () => {
277
+ return HttpResponse.json({ datasources: [] });
278
+ }),
279
+ http.get(`${BASE_URL}/v0/pipes`, () => {
280
+ return HttpResponse.json({ pipes: [] });
281
+ })
282
+ );
283
+
284
+ const result = await hasResources({ baseUrl: BASE_URL, token: TOKEN });
285
+
286
+ expect(result).toBe(false);
287
+ });
288
+
289
+ it("returns true when only datasources exist", async () => {
290
+ server.use(
291
+ http.get(`${BASE_URL}/v0/pipes`, () => {
292
+ return HttpResponse.json({ pipes: [] });
293
+ })
294
+ );
295
+
296
+ const result = await hasResources({ baseUrl: BASE_URL, token: TOKEN });
297
+
298
+ expect(result).toBe(true);
299
+ });
300
+
301
+ it("returns true when only pipes exist", async () => {
302
+ server.use(
303
+ http.get(`${BASE_URL}/v0/datasources`, () => {
304
+ return HttpResponse.json({ datasources: [] });
305
+ })
306
+ );
307
+
308
+ const result = await hasResources({ baseUrl: BASE_URL, token: TOKEN });
309
+
310
+ expect(result).toBe(true);
311
+ });
312
+ });
313
+
314
+ describe("ResourceApiError", () => {
315
+ it("includes status and endpoint", async () => {
316
+ server.use(
317
+ http.get(`${BASE_URL}/v0/datasources`, () => {
318
+ return new HttpResponse("Unauthorized", { status: 401 });
319
+ })
320
+ );
321
+
322
+ try {
323
+ await listDatasources({ baseUrl: BASE_URL, token: TOKEN });
324
+ expect.fail("Should have thrown");
325
+ } catch (error) {
326
+ expect(error).toBeInstanceOf(ResourceApiError);
327
+ const apiError = error as ResourceApiError;
328
+ expect(apiError.status).toBe(401);
329
+ expect(apiError.endpoint).toBe("/v0/datasources");
330
+ }
331
+ });
332
+ });