@lightdash/cli 2.228.0 → 2.229.0

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 (177) hide show
  1. package/dist/.tsbuildinfo.build +1 -0
  2. package/package.json +4 -4
  3. package/dist/.tsbuildinfo +0 -1
  4. package/dist/config.test.d.ts +0 -2
  5. package/dist/config.test.d.ts.map +0 -1
  6. package/dist/config.test.js +0 -72
  7. package/dist/dbt/context.test.d.ts +0 -2
  8. package/dist/dbt/context.test.d.ts.map +0 -1
  9. package/dist/dbt/context.test.js +0 -152
  10. package/dist/dbt/manifest.test.d.ts +0 -2
  11. package/dist/dbt/manifest.test.d.ts.map +0 -1
  12. package/dist/dbt/manifest.test.js +0 -170
  13. package/dist/dbt/models.test.d.ts +0 -2
  14. package/dist/dbt/models.test.d.ts.map +0 -1
  15. package/dist/dbt/models.test.js +0 -139
  16. package/dist/dbt/profiles.test.d.ts +0 -2
  17. package/dist/dbt/profiles.test.d.ts.map +0 -1
  18. package/dist/dbt/profiles.test.js +0 -50
  19. package/dist/dbt/targets/athena.test.d.ts +0 -2
  20. package/dist/dbt/targets/athena.test.d.ts.map +0 -1
  21. package/dist/dbt/targets/athena.test.js +0 -78
  22. package/dist/dbt/targets/duckdb.test.d.ts +0 -2
  23. package/dist/dbt/targets/duckdb.test.d.ts.map +0 -1
  24. package/dist/dbt/targets/duckdb.test.js +0 -149
  25. package/dist/dbt/targets/redshift.test.d.ts +0 -2
  26. package/dist/dbt/targets/redshift.test.d.ts.map +0 -1
  27. package/dist/dbt/targets/redshift.test.js +0 -198
  28. package/dist/dbt/templating.test.d.ts +0 -2
  29. package/dist/dbt/templating.test.d.ts.map +0 -1
  30. package/dist/dbt/templating.test.js +0 -51
  31. package/dist/dbt/validation.test.d.ts +0 -2
  32. package/dist/dbt/validation.test.d.ts.map +0 -1
  33. package/dist/dbt/validation.test.js +0 -67
  34. package/dist/handlers/apps/appCodeFiles.test.d.ts +0 -2
  35. package/dist/handlers/apps/appCodeFiles.test.d.ts.map +0 -1
  36. package/dist/handlers/apps/appCodeFiles.test.js +0 -391
  37. package/dist/handlers/apps/appsDownload.test.d.ts +0 -2
  38. package/dist/handlers/apps/appsDownload.test.d.ts.map +0 -1
  39. package/dist/handlers/apps/appsDownload.test.js +0 -639
  40. package/dist/handlers/apps/build.test.d.ts +0 -2
  41. package/dist/handlers/apps/build.test.d.ts.map +0 -1
  42. package/dist/handlers/apps/build.test.js +0 -252
  43. package/dist/handlers/apps/createApp.test.d.ts +0 -2
  44. package/dist/handlers/apps/createApp.test.d.ts.map +0 -1
  45. package/dist/handlers/apps/createApp.test.js +0 -340
  46. package/dist/handlers/apps/pathContainment.test.d.ts +0 -2
  47. package/dist/handlers/apps/pathContainment.test.d.ts.map +0 -1
  48. package/dist/handlers/apps/pathContainment.test.js +0 -66
  49. package/dist/handlers/apps/preview.test.d.ts +0 -2
  50. package/dist/handlers/apps/preview.test.d.ts.map +0 -1
  51. package/dist/handlers/apps/preview.test.js +0 -353
  52. package/dist/handlers/apps/previewProxy.test.d.ts +0 -2
  53. package/dist/handlers/apps/previewProxy.test.d.ts.map +0 -1
  54. package/dist/handlers/apps/previewProxy.test.js +0 -178
  55. package/dist/handlers/apps/scaffolding.test.d.ts +0 -2
  56. package/dist/handlers/apps/scaffolding.test.d.ts.map +0 -1
  57. package/dist/handlers/apps/scaffolding.test.js +0 -151
  58. package/dist/handlers/apps/uploadRetry.test.d.ts +0 -2
  59. package/dist/handlers/apps/uploadRetry.test.d.ts.map +0 -1
  60. package/dist/handlers/apps/uploadRetry.test.js +0 -103
  61. package/dist/handlers/apps/validate.test.d.ts +0 -2
  62. package/dist/handlers/apps/validate.test.d.ts.map +0 -1
  63. package/dist/handlers/apps/validate.test.js +0 -526
  64. package/dist/handlers/compile.test.d.ts +0 -2
  65. package/dist/handlers/compile.test.d.ts.map +0 -1
  66. package/dist/handlers/compile.test.js +0 -151
  67. package/dist/handlers/compileProject.test.d.ts +0 -2
  68. package/dist/handlers/compileProject.test.d.ts.map +0 -1
  69. package/dist/handlers/compileProject.test.js +0 -637
  70. package/dist/handlers/compileWarehouseColumnValidation.test.d.ts +0 -2
  71. package/dist/handlers/compileWarehouseColumnValidation.test.d.ts.map +0 -1
  72. package/dist/handlers/compileWarehouseColumnValidation.test.js +0 -493
  73. package/dist/handlers/connectSnowflake.test.d.ts +0 -2
  74. package/dist/handlers/connectSnowflake.test.d.ts.map +0 -1
  75. package/dist/handlers/connectSnowflake.test.js +0 -320
  76. package/dist/handlers/contentAsCode/fileNames.test.d.ts +0 -2
  77. package/dist/handlers/contentAsCode/fileNames.test.d.ts.map +0 -1
  78. package/dist/handlers/contentAsCode/fileNames.test.js +0 -79
  79. package/dist/handlers/contentAsCode/registry.test.d.ts +0 -2
  80. package/dist/handlers/contentAsCode/registry.test.d.ts.map +0 -1
  81. package/dist/handlers/contentAsCode/registry.test.js +0 -18
  82. package/dist/handlers/contentAsCode/resource.test.d.ts +0 -2
  83. package/dist/handlers/contentAsCode/resource.test.d.ts.map +0 -1
  84. package/dist/handlers/contentAsCode/resource.test.js +0 -121
  85. package/dist/handlers/dbt/getDbtVersion.test.d.ts +0 -2
  86. package/dist/handlers/dbt/getDbtVersion.test.d.ts.map +0 -1
  87. package/dist/handlers/dbt/getDbtVersion.test.js +0 -106
  88. package/dist/handlers/deploy.test.d.ts +0 -2
  89. package/dist/handlers/deploy.test.d.ts.map +0 -1
  90. package/dist/handlers/deploy.test.js +0 -244
  91. package/dist/handlers/download.test.d.ts +0 -2
  92. package/dist/handlers/download.test.d.ts.map +0 -1
  93. package/dist/handlers/download.test.js +0 -1739
  94. package/dist/handlers/exportChartImage.test.d.ts +0 -2
  95. package/dist/handlers/exportChartImage.test.d.ts.map +0 -1
  96. package/dist/handlers/exportChartImage.test.js +0 -55
  97. package/dist/handlers/homepagesAsCode.test.d.ts +0 -2
  98. package/dist/handlers/homepagesAsCode.test.d.ts.map +0 -1
  99. package/dist/handlers/homepagesAsCode.test.js +0 -72
  100. package/dist/handlers/lint.test.d.ts +0 -2
  101. package/dist/handlers/lint.test.d.ts.map +0 -1
  102. package/dist/handlers/lint.test.js +0 -221
  103. package/dist/handlers/login.test.d.ts +0 -2
  104. package/dist/handlers/login.test.d.ts.map +0 -1
  105. package/dist/handlers/login.test.js +0 -24
  106. package/dist/handlers/metadataFile.test.d.ts +0 -2
  107. package/dist/handlers/metadataFile.test.d.ts.map +0 -1
  108. package/dist/handlers/metadataFile.test.js +0 -46
  109. package/dist/handlers/organizationContent/customRoles.test.d.ts +0 -2
  110. package/dist/handlers/organizationContent/customRoles.test.d.ts.map +0 -1
  111. package/dist/handlers/organizationContent/customRoles.test.js +0 -179
  112. package/dist/handlers/organizationContent/groups.test.d.ts +0 -2
  113. package/dist/handlers/organizationContent/groups.test.d.ts.map +0 -1
  114. package/dist/handlers/organizationContent/groups.test.js +0 -135
  115. package/dist/handlers/organizationContent/index.test.d.ts +0 -2
  116. package/dist/handlers/organizationContent/index.test.d.ts.map +0 -1
  117. package/dist/handlers/organizationContent/index.test.js +0 -212
  118. package/dist/handlers/organizationContent/themes.test.d.ts +0 -2
  119. package/dist/handlers/organizationContent/themes.test.d.ts.map +0 -1
  120. package/dist/handlers/organizationContent/themes.test.js +0 -180
  121. package/dist/handlers/organizationContent/users.test.d.ts +0 -2
  122. package/dist/handlers/organizationContent/users.test.d.ts.map +0 -1
  123. package/dist/handlers/organizationContent/users.test.js +0 -155
  124. package/dist/handlers/preAggregateAudit.test.d.ts +0 -2
  125. package/dist/handlers/preAggregateAudit.test.d.ts.map +0 -1
  126. package/dist/handlers/preAggregateAudit.test.js +0 -102
  127. package/dist/handlers/renameHandler.test.d.ts +0 -2
  128. package/dist/handlers/renameHandler.test.d.ts.map +0 -1
  129. package/dist/handlers/renameHandler.test.js +0 -189
  130. package/dist/handlers/resolveProjectFlag.test.d.ts +0 -2
  131. package/dist/handlers/resolveProjectFlag.test.d.ts.map +0 -1
  132. package/dist/handlers/resolveProjectFlag.test.js +0 -48
  133. package/dist/handlers/selectProject.test.d.ts +0 -2
  134. package/dist/handlers/selectProject.test.d.ts.map +0 -1
  135. package/dist/handlers/selectProject.test.js +0 -224
  136. package/dist/handlers/setProject.test.d.ts +0 -2
  137. package/dist/handlers/setProject.test.d.ts.map +0 -1
  138. package/dist/handlers/setProject.test.js +0 -63
  139. package/dist/handlers/slugUpdate.test.d.ts +0 -2
  140. package/dist/handlers/slugUpdate.test.d.ts.map +0 -1
  141. package/dist/handlers/slugUpdate.test.js +0 -265
  142. package/dist/handlers/timestampConversion.test.d.ts +0 -2
  143. package/dist/handlers/timestampConversion.test.d.ts.map +0 -1
  144. package/dist/handlers/timestampConversion.test.js +0 -87
  145. package/dist/handlers/upgradeCheck.test.d.ts +0 -2
  146. package/dist/handlers/upgradeCheck.test.d.ts.map +0 -1
  147. package/dist/handlers/upgradeCheck.test.js +0 -227
  148. package/dist/handlers/validate.test.d.ts +0 -2
  149. package/dist/handlers/validate.test.d.ts.map +0 -1
  150. package/dist/handlers/validate.test.js +0 -318
  151. package/dist/handlers/validateSpaceFilter.test.d.ts +0 -2
  152. package/dist/handlers/validateSpaceFilter.test.d.ts.map +0 -1
  153. package/dist/handlers/validateSpaceFilter.test.js +0 -79
  154. package/dist/handlers/warehouseConnection.test.d.ts +0 -2
  155. package/dist/handlers/warehouseConnection.test.d.ts.map +0 -1
  156. package/dist/handlers/warehouseConnection.test.js +0 -75
  157. package/dist/lightdash-config/lightdash-config.test.d.ts +0 -2
  158. package/dist/lightdash-config/lightdash-config.test.d.ts.map +0 -1
  159. package/dist/lightdash-config/lightdash-config.test.js +0 -69
  160. package/dist/prompts/fuzzySearchPrompt.test.d.ts +0 -2
  161. package/dist/prompts/fuzzySearchPrompt.test.d.ts.map +0 -1
  162. package/dist/prompts/fuzzySearchPrompt.test.js +0 -53
  163. package/dist/refsArgument.test.d.ts +0 -2
  164. package/dist/refsArgument.test.d.ts.map +0 -1
  165. package/dist/refsArgument.test.js +0 -42
  166. package/dist/releaseSafety/index.test.d.ts +0 -2
  167. package/dist/releaseSafety/index.test.d.ts.map +0 -1
  168. package/dist/releaseSafety/index.test.js +0 -154
  169. package/dist/releaseSafety/version.test.d.ts +0 -2
  170. package/dist/releaseSafety/version.test.d.ts.map +0 -1
  171. package/dist/releaseSafety/version.test.js +0 -22
  172. package/dist/skills.test.d.ts +0 -2
  173. package/dist/skills.test.d.ts.map +0 -1
  174. package/dist/skills.test.js +0 -112
  175. package/dist/terminal/contentAsCodeOutput.test.d.ts +0 -2
  176. package/dist/terminal/contentAsCodeOutput.test.d.ts.map +0 -1
  177. package/dist/terminal/contentAsCodeOutput.test.js +0 -231
@@ -1,320 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
- const common_1 = require("@lightdash/common");
5
- const node_fetch_1 = require("node-fetch");
6
- const analytics_1 = require("../analytics/analytics");
7
- const connectSnowflake_1 = require("./connectSnowflake");
8
- vi.mock('../analytics/analytics');
9
- const options = {
10
- code: '11111111_random-code',
11
- url: 'https://lightdash.example.com/setup',
12
- account: 'acme.eu-west-1',
13
- user: 'lightdash_user',
14
- };
15
- const discovery = {
16
- user: 'lightdash_user',
17
- defaults: {
18
- database: 'analytics',
19
- warehouse: 'lightdash_wh',
20
- role: 'lightdash_role',
21
- schema: 'public',
22
- },
23
- inventory: {
24
- databases: [
25
- {
26
- name: 'analytics',
27
- comment: 'Analytics data',
28
- kind: 'STANDARD',
29
- sizeBytes: 1234,
30
- },
31
- ],
32
- warehouses: [
33
- {
34
- name: 'lightdash_wh',
35
- size: 'X-Small',
36
- state: 'STARTED',
37
- autoSuspendSeconds: 300,
38
- },
39
- ],
40
- roles: [
41
- { name: 'lightdash_role', isDefault: true },
42
- { name: 'PUBLIC', isDefault: false },
43
- ],
44
- schemas: [{ databaseName: 'analytics', name: 'public' }],
45
- },
46
- };
47
- const privateKey = '-----BEGIN PRIVATE KEY-----\ntest-private-key\n-----END PRIVATE KEY-----\n';
48
- const publicKey = 'dGVzdC1wdWJsaWMta2V5';
49
- const getSessionDiscovery = vi.fn();
50
- const getUserPublicKeySlots = vi.fn();
51
- const setUserPublicKey = vi.fn();
52
- const unsetUserPublicKey = vi.fn();
53
- const createProgrammaticAccessToken = vi.fn();
54
- const openDiagnosticConnection = vi.fn();
55
- const selectOneDiagnosticConnection = vi.fn();
56
- const closeDiagnosticConnection = vi.fn();
57
- const warehouseClientFactory = vi.fn(() => ({
58
- closeDiagnosticConnection,
59
- createProgrammaticAccessToken,
60
- getSessionDiscovery,
61
- getUserPublicKeySlots,
62
- openDiagnosticConnection,
63
- selectOneDiagnosticConnection,
64
- setUserPublicKey,
65
- unsetUserPublicKey,
66
- }));
67
- const fetchMock = vi.fn();
68
- const write = vi.fn();
69
- const sleep = vi.fn(async () => undefined);
70
- const responseForDeposit = () => new node_fetch_1.Response(JSON.stringify({ status: 'ok' }), {
71
- status: 200,
72
- headers: { 'Content-Type': 'application/json' },
73
- });
74
- const getDepositRequest = () => JSON.parse(String(vi.mocked(fetchMock).mock.calls[0][1]?.body));
75
- const getDependencies = () => ({
76
- warehouseClientFactory,
77
- generateKeyPair: () => ({ privateKey, publicKey }),
78
- sleep,
79
- now: () => new Date('2026-07-14T12:00:00.000Z'),
80
- fetch: fetchMock,
81
- write,
82
- });
83
- const depositInventory = {
84
- databases: [
85
- { name: 'analytics', comment: 'Analytics data', sizeBytes: 1234 },
86
- ],
87
- warehouses: [{ name: 'lightdash_wh', size: 'X-Small', state: 'STARTED' }],
88
- roles: [
89
- { name: 'lightdash_role', isDefault: true },
90
- { name: 'PUBLIC', isDefault: false },
91
- ],
92
- schemas: [{ database: 'analytics', name: 'public' }],
93
- };
94
- const privateKeyCredentials = (values = {}) => ({
95
- type: common_1.WarehouseTypes.SNOWFLAKE,
96
- account: options.account,
97
- user: discovery.user,
98
- authenticationType: common_1.SnowflakeAuthenticationType.PRIVATE_KEY,
99
- role: discovery.defaults.role ?? undefined,
100
- database: discovery.defaults.database ?? '',
101
- warehouse: discovery.defaults.warehouse ?? '',
102
- schema: discovery.defaults.schema ?? '',
103
- privateKey,
104
- ...values,
105
- });
106
- describe('connectSnowflakeHandler', () => {
107
- beforeEach(() => {
108
- vi.clearAllMocks();
109
- vi.mocked(analytics_1.categorizeError).mockReturnValue('warehouse_connection');
110
- vi.mocked(getSessionDiscovery).mockResolvedValue(discovery);
111
- vi.mocked(getUserPublicKeySlots).mockResolvedValue({
112
- RSA_PUBLIC_KEY: null,
113
- RSA_PUBLIC_KEY_2: null,
114
- });
115
- vi.mocked(setUserPublicKey).mockResolvedValue(undefined);
116
- vi.mocked(unsetUserPublicKey).mockResolvedValue(undefined);
117
- vi.mocked(createProgrammaticAccessToken).mockResolvedValue({
118
- tokenName: 'LIGHTDASH_ONBOARDING_1234567890',
119
- tokenSecret: 'pat-secret',
120
- });
121
- vi.mocked(openDiagnosticConnection).mockResolvedValue({
122
- connection: {},
123
- });
124
- vi.mocked(selectOneDiagnosticConnection).mockResolvedValue(undefined);
125
- vi.mocked(closeDiagnosticConnection).mockResolvedValue(undefined);
126
- vi.mocked(fetchMock).mockResolvedValue(responseForDeposit());
127
- });
128
- it('deposits private-key credentials using session defaults', async () => {
129
- await expect((0, connectSnowflake_1.connectSnowflakeHandler)(options, getDependencies())).resolves.toBeUndefined();
130
- expect(warehouseClientFactory).toHaveBeenNthCalledWith(1, {
131
- type: common_1.WarehouseTypes.SNOWFLAKE,
132
- account: options.account,
133
- user: options.user,
134
- authenticationType: common_1.SnowflakeAuthenticationType.OAUTH_AUTHORIZATION_CODE,
135
- role: undefined,
136
- database: '',
137
- warehouse: '',
138
- schema: '',
139
- });
140
- expect(getSessionDiscovery).toHaveBeenCalledWith(options.user);
141
- expect(setUserPublicKey).toHaveBeenCalledWith(discovery.user, 'RSA_PUBLIC_KEY', publicKey);
142
- expect(warehouseClientFactory).toHaveBeenNthCalledWith(2, privateKeyCredentials({
143
- database: '',
144
- warehouse: '',
145
- schema: '',
146
- role: undefined,
147
- }));
148
- const request = {
149
- code: options.code,
150
- credentials: privateKeyCredentials(),
151
- inventory: depositInventory,
152
- };
153
- expect(getDepositRequest()).toEqual(request);
154
- expect(write).toHaveBeenCalledWith('Secured with a key pair');
155
- expect(write).toHaveBeenCalledWith('Connection values resolved: database (session default), warehouse (session default), schema (session default), role (session default)');
156
- expect(write).not.toHaveBeenCalledWith(expect.stringContaining(options.code));
157
- expect(write).not.toHaveBeenCalledWith(expect.stringContaining(privateKey));
158
- const { LightdashAnalytics } = await Promise.resolve().then(() => tslib_1.__importStar(require('../analytics/analytics')));
159
- expect(vi.mocked(LightdashAnalytics.track)).toHaveBeenCalledWith({
160
- event: 'connect_snowflake.started',
161
- properties: {},
162
- });
163
- expect(vi.mocked(LightdashAnalytics.track)).toHaveBeenCalledWith({
164
- event: 'connect_snowflake.completed',
165
- properties: { method: 'key_pair' },
166
- });
167
- });
168
- it('prefers explicit flags over session defaults', async () => {
169
- const overrideOptions = {
170
- ...options,
171
- database: 'flag_db',
172
- warehouse: 'flag_wh',
173
- role: 'flag_role',
174
- schema: 'flag_schema',
175
- };
176
- await expect((0, connectSnowflake_1.connectSnowflakeHandler)(overrideOptions, getDependencies())).resolves.toBeUndefined();
177
- const request = {
178
- code: options.code,
179
- credentials: privateKeyCredentials({
180
- database: 'flag_db',
181
- warehouse: 'flag_wh',
182
- role: 'flag_role',
183
- schema: 'flag_schema',
184
- }),
185
- inventory: depositInventory,
186
- };
187
- expect(getDepositRequest()).toEqual(request);
188
- expect(write).toHaveBeenCalledWith('Connection values resolved: database (flag), warehouse (flag), schema (flag), role (flag)');
189
- });
190
- it('omits missing values and deposits capped inventory names', async () => {
191
- const inventoryNames = Array.from({ length: 105 }, (_, index) => `item_${index}`);
192
- vi.mocked(getSessionDiscovery).mockResolvedValue({
193
- ...discovery,
194
- defaults: {
195
- database: null,
196
- warehouse: null,
197
- role: null,
198
- schema: null,
199
- },
200
- inventory: {
201
- databases: inventoryNames.map((name) => ({
202
- name,
203
- comment: null,
204
- kind: null,
205
- sizeBytes: null,
206
- })),
207
- warehouses: inventoryNames.map((name) => ({
208
- name,
209
- size: null,
210
- state: null,
211
- autoSuspendSeconds: null,
212
- })),
213
- roles: inventoryNames.map((name) => ({
214
- name,
215
- isDefault: false,
216
- })),
217
- schemas: inventoryNames.map((name) => ({
218
- databaseName: 'analytics',
219
- name,
220
- })),
221
- },
222
- });
223
- await expect((0, connectSnowflake_1.connectSnowflakeHandler)(options, getDependencies())).resolves.toBeUndefined();
224
- const request = getDepositRequest();
225
- expect(request.credentials).not.toHaveProperty('database');
226
- expect(request.credentials).not.toHaveProperty('warehouse');
227
- expect(request.credentials).not.toHaveProperty('schema');
228
- expect(request.credentials).not.toHaveProperty('role');
229
- expect(request.inventory).toEqual({
230
- databases: inventoryNames
231
- .slice(0, 100)
232
- .map((name) => ({ name, comment: null, sizeBytes: null })),
233
- warehouses: inventoryNames
234
- .slice(0, 100)
235
- .map((name) => ({ name, size: null, state: null })),
236
- roles: inventoryNames
237
- .slice(0, 100)
238
- .map((name) => ({ name, isDefault: false })),
239
- schemas: inventoryNames
240
- .slice(0, 1000)
241
- .map((name) => ({ database: 'analytics', name })),
242
- });
243
- expect(write).toHaveBeenCalledWith('Connection values resolved: none');
244
- expect(write).toHaveBeenCalledWith('Finish choosing database, warehouse, schema, role in the browser.');
245
- });
246
- it('selects the first free Snowflake public-key slot', async () => {
247
- vi.mocked(getUserPublicKeySlots).mockResolvedValue({
248
- RSA_PUBLIC_KEY: 'SHA256:existing-key',
249
- RSA_PUBLIC_KEY_2: null,
250
- });
251
- await expect((0, connectSnowflake_1.connectSnowflakeHandler)(options, getDependencies())).resolves.toBeUndefined();
252
- expect(setUserPublicKey).toHaveBeenCalledWith(discovery.user, 'RSA_PUBLIC_KEY_2', publicKey);
253
- expect(createProgrammaticAccessToken).not.toHaveBeenCalled();
254
- });
255
- it('uses a unique PAT name when both key slots are occupied', async () => {
256
- vi.mocked(getUserPublicKeySlots).mockResolvedValue({
257
- RSA_PUBLIC_KEY: 'SHA256:existing-key-1',
258
- RSA_PUBLIC_KEY_2: 'SHA256:existing-key-2',
259
- });
260
- await expect((0, connectSnowflake_1.connectSnowflakeHandler)(options, getDependencies())).resolves.toBeUndefined();
261
- expect(setUserPublicKey).not.toHaveBeenCalled();
262
- expect(createProgrammaticAccessToken).toHaveBeenCalledWith(expect.stringMatching(/^LIGHTDASH_ONBOARDING_\d+$/), 365, 60);
263
- const request = {
264
- code: options.code,
265
- credentials: {
266
- type: common_1.WarehouseTypes.SNOWFLAKE,
267
- account: options.account,
268
- user: discovery.user,
269
- authenticationType: common_1.SnowflakeAuthenticationType.PASSWORD,
270
- role: discovery.defaults.role ?? undefined,
271
- database: discovery.defaults.database ?? '',
272
- warehouse: discovery.defaults.warehouse ?? '',
273
- schema: discovery.defaults.schema ?? '',
274
- password: 'pat-secret',
275
- },
276
- inventory: depositInventory,
277
- };
278
- expect(getDepositRequest()).toEqual(request);
279
- });
280
- it('unsets an unverified key before falling back to a PAT', async () => {
281
- vi.mocked(openDiagnosticConnection).mockRejectedValue(new Error('Authentication policy excludes KEYPAIR'));
282
- await expect((0, connectSnowflake_1.connectSnowflakeHandler)(options, getDependencies())).resolves.toBeUndefined();
283
- expect(openDiagnosticConnection).toHaveBeenCalledTimes(3);
284
- expect(sleep).toHaveBeenCalledTimes(2);
285
- expect(sleep).toHaveBeenCalledWith(5_000);
286
- expect(unsetUserPublicKey).toHaveBeenCalledWith(discovery.user, 'RSA_PUBLIC_KEY');
287
- expect(createProgrammaticAccessToken).toHaveBeenCalledAfter(unsetUserPublicKey);
288
- });
289
- it('falls back to a PAT when key registration lacks privileges', async () => {
290
- vi.mocked(setUserPublicKey).mockRejectedValue(new Error('Insufficient privileges to operate on user'));
291
- await expect((0, connectSnowflake_1.connectSnowflakeHandler)(options, getDependencies())).resolves.toBeUndefined();
292
- expect(createProgrammaticAccessToken).toHaveBeenCalledWith(expect.stringMatching(/^LIGHTDASH_ONBOARDING_\d+$/), 365, 60);
293
- expect(write).toHaveBeenCalledWith('Secured with a programmatic access token (expires 2027-07-14)');
294
- expect(write).toHaveBeenCalledWith(expect.stringContaining('bypasses your Snowflake network policy for the next 60 minutes'));
295
- });
296
- it('prints administrator remediation when both durable paths fail', async () => {
297
- vi.mocked(setUserPublicKey).mockRejectedValue(new Error('Insufficient privileges'));
298
- vi.mocked(createProgrammaticAccessToken).mockRejectedValue(new Error('Authentication policy does not allow PROGRAMMATIC_ACCESS_TOKEN'));
299
- await expect((0, connectSnowflake_1.connectSnowflakeHandler)(options, getDependencies())).rejects.toThrow('Snowflake programmatic access token creation failed');
300
- expect(write).toHaveBeenCalledWith('GRANT MODIFY PROGRAMMATIC AUTHENTICATION METHODS ON USER lightdash_user TO ROLE lightdash_role;');
301
- expect(write).toHaveBeenCalledWith(expect.stringContaining('PROGRAMMATIC_ACCESS_TOKEN'));
302
- expect(fetchMock).not.toHaveBeenCalled();
303
- });
304
- it('reports an expired connect code without exposing it', async () => {
305
- vi.mocked(fetchMock).mockResolvedValue(new node_fetch_1.Response(JSON.stringify({ status: 'error' }), {
306
- status: 404,
307
- headers: { 'Content-Type': 'application/json' },
308
- }));
309
- await expect((0, connectSnowflake_1.connectSnowflakeHandler)(options, getDependencies())).rejects.toThrow(common_1.WarehouseConnectionError);
310
- expect(write).not.toHaveBeenCalledWith(expect.stringContaining(options.code));
311
- const { LightdashAnalytics } = await Promise.resolve().then(() => tslib_1.__importStar(require('../analytics/analytics')));
312
- expect(vi.mocked(LightdashAnalytics.track)).toHaveBeenCalledWith({
313
- event: 'connect_snowflake.error',
314
- properties: {
315
- error: expect.any(String),
316
- errorCategory: 'warehouse_connection',
317
- },
318
- });
319
- });
320
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=fileNames.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fileNames.test.d.ts","sourceRoot":"","sources":["../../../src/handlers/contentAsCode/fileNames.test.ts"],"names":[],"mappings":""}
@@ -1,79 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const crypto_1 = require("crypto");
4
- const vitest_1 = require("vitest");
5
- const fileNames_1 = require("./fileNames");
6
- (0, vitest_1.describe)('content-as-code filenames', () => {
7
- (0, vitest_1.it)('normalizes unicode and preserves compatibility extensions', () => {
8
- (0, vitest_1.expect)((0, fileNames_1.allocateContentFileNames)({
9
- items: [{ identity: 'malaga', displayName: 'Málaga Finance' }],
10
- fallbackPrefix: 'group',
11
- extension: '.space.yml',
12
- })).toStrictEqual(['malaga-finance.space.yml']);
13
- });
14
- (0, vitest_1.it)('uses stable identities for unicode-only names and collisions', () => {
15
- const hash = (0, crypto_1.createHash)('sha256')
16
- .update('party')
17
- .digest('hex')
18
- .slice(0, 8);
19
- (0, vitest_1.expect)((0, fileNames_1.allocateContentFileNames)({
20
- items: [{ identity: 'party', displayName: '🎉' }],
21
- fallbackPrefix: 'group',
22
- })).toStrictEqual([`group-${hash}.yml`]);
23
- const names = (0, fileNames_1.allocateContentFileNames)({
24
- items: [
25
- { identity: 'one', displayName: 'Finance' },
26
- { identity: 'two', displayName: 'finance' },
27
- ],
28
- fallbackPrefix: 'group',
29
- });
30
- (0, vitest_1.expect)(new Set(names.map((name) => name.toLowerCase())).size).toBe(2);
31
- });
32
- (0, vitest_1.it)('bounds long stems while retaining a stable collision suffix', () => {
33
- const names = (0, fileNames_1.allocateContentFileNames)({
34
- items: [
35
- { identity: 'one', displayName: 'a'.repeat(500) },
36
- { identity: 'two', displayName: 'a'.repeat(500) },
37
- ],
38
- fallbackPrefix: 'chart',
39
- });
40
- (0, vitest_1.expect)(names.every((name) => name.length <= 204)).toBe(true);
41
- (0, vitest_1.expect)(names[0]).not.toBe(names[1]);
42
- });
43
- (0, vitest_1.it)('preserves SQL and URI-encoded compatibility profiles', () => {
44
- (0, vitest_1.expect)((0, fileNames_1.allocateContentFileNames)({
45
- items: [
46
- {
47
- identity: 'orders',
48
- displayName: 'Orders',
49
- preferredFileName: 'orders.sql.yml',
50
- },
51
- ],
52
- fallbackPrefix: 'sql-chart',
53
- extension: '.sql.yml',
54
- })).toStrictEqual(['orders.sql.yml']);
55
- (0, vitest_1.expect)((0, fileNames_1.allocateContentFileNames)({
56
- items: [
57
- {
58
- identity: 'finance/view',
59
- displayName: 'Finance view',
60
- preferredFileName: 'finance%2Fview.yml',
61
- },
62
- ],
63
- fallbackPrefix: 'virtual-view',
64
- })).toStrictEqual(['finance%2Fview.yml']);
65
- });
66
- (0, vitest_1.it)('reuses an existing file owned by the same identity', () => {
67
- (0, vitest_1.expect)((0, fileNames_1.allocateContentFileNames)({
68
- items: [{ identity: 'finance', displayName: 'New name' }],
69
- fallbackPrefix: 'group',
70
- existingFileNameByIdentity: new Map([
71
- ['finance', 'user-owned-name.yaml'],
72
- ]),
73
- })).toStrictEqual(['user-owned-name.yaml']);
74
- });
75
- (0, vitest_1.it)('rejects absolute and parent-traversal paths', () => {
76
- (0, vitest_1.expect)(() => (0, fileNames_1.assertSafeContentFilePath)('../secrets.yml')).toThrow('Unsafe');
77
- (0, vitest_1.expect)(() => (0, fileNames_1.assertSafeContentFilePath)('/tmp/file.yml')).toThrow('Unsafe');
78
- });
79
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=registry.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"registry.test.d.ts","sourceRoot":"","sources":["../../../src/handlers/contentAsCode/registry.test.ts"],"names":[],"mappings":""}
@@ -1,18 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const vitest_1 = require("vitest");
4
- const registry_1 = require("./registry");
5
- (0, vitest_1.describe)('content-as-code resource registry', () => {
6
- (0, vitest_1.it)('keeps organization dependencies in a valid order', () => {
7
- (0, vitest_1.expect)(() => (0, registry_1.assertCodeResourceDependencyOrder)(registry_1.ORGANIZATION_CODE_RESOURCES)).not.toThrow();
8
- });
9
- (0, vitest_1.it)('keeps project dependencies in a valid order', () => {
10
- (0, vitest_1.expect)(() => (0, registry_1.assertCodeResourceDependencyOrder)(registry_1.PROJECT_CODE_RESOURCES)).not.toThrow();
11
- });
12
- (0, vitest_1.it)('rejects resources registered before their dependencies', () => {
13
- (0, vitest_1.expect)(() => (0, registry_1.assertCodeResourceDependencyOrder)([
14
- { kind: 'group', dependencies: ['user'] },
15
- { kind: 'user', dependencies: [] },
16
- ])).toThrow('group');
17
- });
18
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=resource.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"resource.test.d.ts","sourceRoot":"","sources":["../../../src/handlers/contentAsCode/resource.test.ts"],"names":[],"mappings":""}
@@ -1,121 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const tslib_1 = require("tslib");
4
- const common_1 = require("@lightdash/common");
5
- const fs_1 = require("fs");
6
- const os_1 = tslib_1.__importDefault(require("os"));
7
- const path_1 = tslib_1.__importDefault(require("path"));
8
- const vitest_1 = require("vitest");
9
- const projectResources_1 = require("./projectResources");
10
- const resource_1 = require("./resource");
11
- const temporaryDirectories = [];
12
- const agent = (slug) => ({
13
- contentType: common_1.ContentAsCodeType.AI_AGENT,
14
- version: 1,
15
- agentVersion: 2,
16
- slug,
17
- name: slug,
18
- description: null,
19
- imageUrl: null,
20
- instruction: null,
21
- tags: null,
22
- enableDataAccess: true,
23
- enableSelfImprovement: false,
24
- enableContentTools: false,
25
- enableUserContext: false,
26
- modelConfig: null,
27
- });
28
- (0, vitest_1.afterEach)(async () => {
29
- await Promise.all(temporaryDirectories.splice(0).map((directory) => fs_1.promises.rm(directory, {
30
- recursive: true,
31
- force: true,
32
- })));
33
- });
34
- (0, vitest_1.describe)('content-as-code resource files', () => {
35
- (0, vitest_1.it)('round trips exact homepage names safely and rejects duplicate local identities', async () => {
36
- const basePath = await fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'lightdash-homepage-code-'));
37
- temporaryDirectories.push(basePath);
38
- const document = {
39
- contentType: common_1.ContentAsCodeType.HOMEPAGE,
40
- version: 1,
41
- name: '../Home / 100%',
42
- config: { version: 1, rows: [] },
43
- publication: null,
44
- };
45
- await (0, resource_1.writeCodeResourceDocuments)({
46
- definition: projectResources_1.HOMEPAGE_CODE_RESOURCE,
47
- basePath,
48
- documents: [document],
49
- pruneOtherDocuments: true,
50
- });
51
- const fileNames = await fs_1.promises.readdir(path_1.default.join(basePath, 'homepages'));
52
- (0, vitest_1.expect)(fileNames).toEqual(['..%2FHome %2F 100%25.yml']);
53
- const result = await (0, resource_1.readCodeResourceFiles)({
54
- definition: projectResources_1.HOMEPAGE_CODE_RESOURCE,
55
- basePath,
56
- });
57
- (0, vitest_1.expect)(result.files[0].document).toEqual(document);
58
- await fs_1.promises.copyFile(path_1.default.join(basePath, 'homepages', fileNames[0]), path_1.default.join(basePath, 'homepages', 'duplicate.yaml'));
59
- const duplicates = await (0, resource_1.readCodeResourceFiles)({
60
- definition: projectResources_1.HOMEPAGE_CODE_RESOURCE,
61
- basePath,
62
- });
63
- (0, vitest_1.expect)(duplicates.files).toEqual([]);
64
- (0, vitest_1.expect)(duplicates.failures[0].message).toContain('Duplicate');
65
- });
66
- (0, vitest_1.it)('preserves other documents when writing a filtered download', async () => {
67
- const basePath = await fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'lightdash-content-as-code-'));
68
- temporaryDirectories.push(basePath);
69
- await (0, resource_1.writeCodeResourceDocuments)({
70
- definition: projectResources_1.AI_AGENT_CODE_RESOURCE,
71
- basePath,
72
- documents: [agent('alpha'), agent('beta')],
73
- pruneOtherDocuments: true,
74
- });
75
- await (0, resource_1.writeCodeResourceDocuments)({
76
- definition: projectResources_1.AI_AGENT_CODE_RESOURCE,
77
- basePath,
78
- documents: [agent('alpha')],
79
- pruneOtherDocuments: false,
80
- });
81
- await (0, vitest_1.expect)(fs_1.promises.readdir(path_1.default.join(basePath, 'ai-agents'))).resolves.toEqual(['alpha.yml', 'beta.yml']);
82
- });
83
- (0, vitest_1.it)('prunes other documents when writing a complete download', async () => {
84
- const basePath = await fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'lightdash-content-as-code-'));
85
- temporaryDirectories.push(basePath);
86
- await (0, resource_1.writeCodeResourceDocuments)({
87
- definition: projectResources_1.AI_AGENT_CODE_RESOURCE,
88
- basePath,
89
- documents: [agent('alpha'), agent('beta')],
90
- pruneOtherDocuments: true,
91
- });
92
- await (0, resource_1.writeCodeResourceDocuments)({
93
- definition: projectResources_1.AI_AGENT_CODE_RESOURCE,
94
- basePath,
95
- documents: [agent('alpha')],
96
- pruneOtherDocuments: true,
97
- });
98
- await (0, vitest_1.expect)(fs_1.promises.readdir(path_1.default.join(basePath, 'ai-agents'))).resolves.toEqual(['alpha.yml']);
99
- });
100
- (0, vitest_1.it)('ignores other scheduled content types in a shared legacy folder', async () => {
101
- const basePath = await fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'lightdash-content-as-code-'));
102
- temporaryDirectories.push(basePath);
103
- const alertFolder = path_1.default.join(basePath, 'alerts', 'charts', 'orders');
104
- await fs_1.promises.mkdir(alertFolder, { recursive: true });
105
- await Promise.all([
106
- fs_1.promises.writeFile(path_1.default.join(alertFolder, 'alert.yml'), 'contentType: alert\nversion: 1\nslug: revenue-alert\nname: Revenue alert\n'),
107
- fs_1.promises.writeFile(path_1.default.join(alertFolder, 'delivery.yml'), 'contentType: scheduled_delivery\nversion: 1\nslug: revenue-delivery\nname: Revenue delivery\n'),
108
- fs_1.promises.writeFile(path_1.default.join(alertFolder, 'invalid-alert.yml'), 'contentType: alert\nslug: invalid-alert\nname: Invalid alert\n'),
109
- ]);
110
- const result = await (0, resource_1.readCodeResourceFiles)({
111
- definition: projectResources_1.ALERT_CODE_RESOURCE,
112
- basePath,
113
- });
114
- (0, vitest_1.expect)(result.files.map(({ document }) => document.slug)).toEqual([
115
- 'revenue-alert',
116
- ]);
117
- (0, vitest_1.expect)(result.failures).toHaveLength(1);
118
- (0, vitest_1.expect)(result.failures[0].message).toContain('invalid-alert.yml');
119
- (0, vitest_1.expect)(result.failures[0].message).toContain('expected version 1');
120
- });
121
- });
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=getDbtVersion.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"getDbtVersion.test.d.ts","sourceRoot":"","sources":["../../../src/handlers/dbt/getDbtVersion.test.ts"],"names":[],"mappings":""}