@lightdash/cli 1.48.0 → 1.49.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.
@@ -36,6 +36,7 @@ export type CodeResourceUploadSummary = {
36
36
  created: number;
37
37
  updated: number;
38
38
  unchanged: number;
39
+ skipped?: number;
39
40
  failed: number;
40
41
  failures: CodeFileFailure[];
41
42
  };
@@ -59,10 +60,11 @@ export declare const downloadCodeResource: <Document>({ definition, basePath, li
59
60
  basePath: string;
60
61
  list: () => Promise<Document[]>;
61
62
  }) => Promise<number>;
62
- export declare const uploadCodeResource: <Document>({ definition, basePath, upsert, }: {
63
+ export declare const uploadCodeResource: <Document>({ definition, basePath, upsert, skipError, }: {
63
64
  definition: CodeResourceDefinition<Document>;
64
65
  basePath: string;
65
66
  upsert: (document: Document) => Promise<ContentAsCodeUpsertAction>;
67
+ skipError?: (error: unknown) => boolean;
66
68
  }) => Promise<CodeResourceUploadSummary>;
67
69
  export declare const assertCodeResourceFilesValid: <Document>(result: CodeResourceFiles<Document>) => void;
68
70
  //# sourceMappingURL=resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../../src/handlers/contentAsCode/resource.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../../src/handlers/contentAsCode/resource.ts"],"names":[],"mappings":"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"}
@@ -144,7 +144,7 @@ const getActionCountKey = (action) => {
144
144
  return (0, common_1.assertUnreachable)(action, 'Unsupported content-as-code action');
145
145
  }
146
146
  };
147
- const uploadCodeResource = async ({ definition, basePath, upsert, }) => {
147
+ const uploadCodeResource = async ({ definition, basePath, upsert, skipError, }) => {
148
148
  const { files, failures } = await (0, exports.readCodeResourceFiles)({
149
149
  definition,
150
150
  basePath,
@@ -161,9 +161,14 @@ const uploadCodeResource = async ({ definition, basePath, upsert, }) => {
161
161
  summary[getActionCountKey(await upsert(document))] += 1;
162
162
  }
163
163
  catch (error) {
164
- summary.failures.push({
165
- message: `Invalid ${definition.displayLabel} file "${filePath}": ${(0, common_1.getErrorMessage)(error)}`,
166
- });
164
+ if (skipError?.(error)) {
165
+ summary.skipped = (summary.skipped ?? 0) + 1;
166
+ }
167
+ else {
168
+ summary.failures.push({
169
+ message: `Invalid ${definition.displayLabel} file "${filePath}": ${(0, common_1.getErrorMessage)(error)}`,
170
+ });
171
+ }
167
172
  }
168
173
  }
169
174
  summary.failed = summary.failures.length;
@@ -70,9 +70,12 @@ export declare const getExternalConnectionSecretEnvVar: (slug: string) => string
70
70
  * no EE coder service provider (MissingConfigError).
71
71
  */
72
72
  declare const isExternalConnectionsUnavailableError: (error: unknown) => boolean;
73
+ declare const isAiAgentsUnavailableError: (error: unknown) => boolean;
73
74
  declare const readExternalConnectionFiles: (customPath?: string) => Promise<ExternalConnectionAsCode[]>;
74
75
  declare const upsertExternalConnections: (projectId: string, slugs: string[], changes: Record<string, number>, force: boolean, canUpload: boolean, customPath?: string) => Promise<Record<string, number>>;
76
+ declare const downloadAiAgents: (projectId: string, ids: string[], implicit: boolean, customPath?: string) => Promise<number>;
75
77
  declare const readAiAgentFiles: (customPath?: string) => Promise<AgentAsCode[]>;
78
+ declare const upsertAiAgents: (projectId: string, slugs: string[], changes: Record<string, number>, force: boolean, customPath?: string, implicit?: boolean) => Promise<Record<string, number>>;
76
79
  export declare const downloadHandler: (options: DownloadHandlerOptions) => Promise<void>;
77
80
  declare const summarizeUploadChanges: (before: Record<string, number>, after: Record<string, number>) => {
78
81
  detail: string;
@@ -88,7 +91,9 @@ export declare const testHelpers: {
88
91
  getFlatSpaceFileNames: typeof getFlatSpaceFileNames;
89
92
  getDashboardChartSlugs: typeof getDashboardChartSlugs;
90
93
  hasContentFilters: typeof hasContentFilters;
94
+ isAiAgentsUnavailableError: typeof isAiAgentsUnavailableError;
91
95
  isExternalConnectionsUnavailableError: typeof isExternalConnectionsUnavailableError;
96
+ downloadAiAgents: typeof downloadAiAgents;
92
97
  readAiAgentFiles: typeof readAiAgentFiles;
93
98
  readExternalConnectionFiles: typeof readExternalConnectionFiles;
94
99
  readSpaceFiles: typeof readSpaceFiles;
@@ -98,6 +103,7 @@ export declare const testHelpers: {
98
103
  shouldDownloadAiAgents: typeof shouldDownloadAiAgents;
99
104
  sortSpaceFilesParentFirst: typeof sortSpaceFilesParentFirst;
100
105
  summarizeUploadChanges: typeof summarizeUploadChanges;
106
+ upsertAiAgents: typeof upsertAiAgents;
101
107
  upsertExternalConnections: typeof upsertExternalConnections;
102
108
  upsertSpaces: typeof upsertSpaces;
103
109
  upsertVirtualViews: typeof upsertVirtualViews;
@@ -1 +1 @@
1
- {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/handlers/download.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/handlers/download.ts"],"names":[],"mappings":"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"}
@@ -521,6 +521,8 @@ const EXTERNAL_CONNECTION_SECRET_TYPES = new Set([
521
521
  */
522
522
  const isExternalConnectionsUnavailableError = (error) => error instanceof common_1.LightdashError &&
523
523
  [403, 404, 422].includes(error.statusCode);
524
+ const isAiAgentsUnavailableError = (error) => error instanceof common_1.LightdashError &&
525
+ [403, 404, 422].includes(error.statusCode);
524
526
  const downloadExternalConnections = async (projectId, slugs, implicit, customPath) => {
525
527
  const slugQuery = slugs.map((slug) => ['slugs', slug]);
526
528
  let offset = 0;
@@ -656,34 +658,44 @@ const getScheduledContentConfig = (contentType, customPath) => {
656
658
  return (0, common_1.assertUnreachable)(contentType, 'Unknown scheduled content type');
657
659
  }
658
660
  };
659
- const downloadAiAgents = async (projectId, ids, customPath) => {
661
+ const downloadAiAgents = async (projectId, ids, implicit, customPath) => {
660
662
  const idQuery = ids.map((id) => ['ids', id]);
661
663
  let offset = 0;
662
664
  let total = 0;
663
665
  let downloaded = 0;
664
666
  const agents = [];
665
- do {
666
- const query = new URLSearchParams([
667
- ...idQuery,
668
- ['offset', String(offset)],
669
- ]).toString();
670
- const results = await (0, apiClient_1.lightdashApi)({
671
- method: 'GET',
672
- url: `/api/v1/projects/${projectId}/code/aiAgents?${query}`,
673
- body: undefined,
667
+ try {
668
+ do {
669
+ const query = new URLSearchParams([
670
+ ...idQuery,
671
+ ['offset', String(offset)],
672
+ ]).toString();
673
+ const results = await (0, apiClient_1.lightdashApi)({
674
+ method: 'GET',
675
+ url: `/api/v1/projects/${projectId}/code/aiAgents?${query}`,
676
+ body: undefined,
677
+ });
678
+ agents.push(...results.agents);
679
+ results.missingIds.forEach((id) => globalState_1.default.debug(`No AI agent with id "${id}"`));
680
+ downloaded += results.agents.length;
681
+ offset = results.offset;
682
+ total = results.total;
683
+ } while (offset < total);
684
+ await (0, resource_1.writeCodeResourceDocuments)({
685
+ definition: projectResources_1.AI_AGENT_CODE_RESOURCE,
686
+ basePath: (0, contentAsCodePaths_1.getDownloadFolder)(customPath),
687
+ documents: agents,
688
+ pruneOtherDocuments: ids.length === 0,
674
689
  });
675
- agents.push(...results.agents);
676
- results.missingIds.forEach((id) => globalState_1.default.debug(`No AI agent with id "${id}"`));
677
- downloaded += results.agents.length;
678
- offset = results.offset;
679
- total = results.total;
680
- } while (offset < total);
681
- await (0, resource_1.writeCodeResourceDocuments)({
682
- definition: projectResources_1.AI_AGENT_CODE_RESOURCE,
683
- basePath: (0, contentAsCodePaths_1.getDownloadFolder)(customPath),
684
- documents: agents,
685
- pruneOtherDocuments: ids.length === 0,
686
- });
690
+ }
691
+ catch (error) {
692
+ if (implicit && isAiAgentsUnavailableError(error)) {
693
+ globalState_1.default.log(styles.warning('Skipping AI agents: they require Lightdash Enterprise and AI agent access.'));
694
+ globalState_1.default.debug(`Could not download AI agents: ${(0, common_1.getErrorMessage)(error)}`);
695
+ return 0;
696
+ }
697
+ throw error;
698
+ }
687
699
  return downloaded;
688
700
  };
689
701
  const readAiAgentFiles = async (customPath) => {
@@ -703,7 +715,7 @@ class AiAgentAsCodeUploadError extends Error {
703
715
  this.originalError = originalError;
704
716
  }
705
717
  }
706
- const upsertAiAgents = async (projectId, slugs, changes, force, customPath) => {
718
+ const upsertAiAgents = async (projectId, slugs, changes, force, customPath, implicit = false) => {
707
719
  const agents = await readAiAgentFiles(customPath);
708
720
  const filteredAgents = slugs.length
709
721
  ? agents.filter((agent) => slugs.includes(agent.slug))
@@ -715,11 +727,22 @@ const upsertAiAgents = async (projectId, slugs, changes, force, customPath) => {
715
727
  return changes;
716
728
  }
717
729
  (0, contentAsCodeOutput_1.logContentAsCodeDiscovery)(`Found ${filteredAgents.length} AI agent files`);
718
- const results = await (0, apiClient_1.lightdashApi)({
719
- method: 'POST',
720
- url: `/api/v1/projects/${projectId}/code/aiAgents?force=${force}`,
721
- body: JSON.stringify({ agents: filteredAgents }),
722
- });
730
+ let results;
731
+ try {
732
+ results = await (0, apiClient_1.lightdashApi)({
733
+ method: 'POST',
734
+ url: `/api/v1/projects/${projectId}/code/aiAgents?force=${force}`,
735
+ body: JSON.stringify({ agents: filteredAgents }),
736
+ });
737
+ }
738
+ catch (error) {
739
+ if (implicit && isAiAgentsUnavailableError(error)) {
740
+ globalState_1.default.log(styles.warning('Skipping AI agents: they require Lightdash Enterprise and AI agent access.'));
741
+ globalState_1.default.debug(`Could not upload AI agents: ${(0, common_1.getErrorMessage)(error)}`);
742
+ return changes;
743
+ }
744
+ throw error;
745
+ }
723
746
  const counts = {
724
747
  'AI agents created': results.created.length,
725
748
  'AI agents updated': results.updated.length,
@@ -1028,9 +1051,12 @@ const downloadHandler = async (options) => {
1028
1051
  }
1029
1052
  }
1030
1053
  if (!options.spacesOnly && shouldDownloadAiAgents(options)) {
1054
+ const implicit = includeAllOptionalContent &&
1055
+ options.includeAgents !== true &&
1056
+ options.agents.length === 0;
1031
1057
  await output.runItem({
1032
1058
  label: 'AI agents',
1033
- action: () => downloadAiAgents(projectId, options.agents, options.path),
1059
+ action: () => downloadAiAgents(projectId, options.agents, implicit, options.path),
1034
1060
  detail: (total) => `${total} downloaded`,
1035
1061
  });
1036
1062
  }
@@ -1249,8 +1275,7 @@ const downloadHandler = async (options) => {
1249
1275
  error: `${error}`,
1250
1276
  },
1251
1277
  });
1252
- if ((0, spacesAsCode_1.isSpaceAsCodeDownloadError)(error))
1253
- throw error;
1278
+ throw error;
1254
1279
  }
1255
1280
  };
1256
1281
  exports.downloadHandler = downloadHandler;
@@ -1690,7 +1715,7 @@ const uploadHandler = async (options) => {
1690
1715
  output,
1691
1716
  label: 'AI agents',
1692
1717
  changes,
1693
- action: () => upsertAiAgents(projectId, options.agents, changes, options.force, options.path),
1718
+ action: () => upsertAiAgents(projectId, options.agents, changes, options.force, options.path, options.agents.length === 0),
1694
1719
  });
1695
1720
  }
1696
1721
  catch (error) {
@@ -1965,10 +1990,9 @@ const uploadHandler = async (options) => {
1965
1990
  error: (0, common_1.getErrorMessage)(error),
1966
1991
  },
1967
1992
  });
1968
- if ((0, spacesAsCode_1.isSpaceAsCodeUploadError)(error))
1969
- throw error;
1970
1993
  if (error instanceof AiAgentAsCodeUploadError)
1971
1994
  throw error.originalError;
1995
+ throw error;
1972
1996
  }
1973
1997
  };
1974
1998
  exports.uploadHandler = uploadHandler;
@@ -1978,7 +2002,9 @@ exports.testHelpers = {
1978
2002
  getFlatSpaceFileNames: spacesAsCode_1.getFlatSpaceFileNames,
1979
2003
  getDashboardChartSlugs,
1980
2004
  hasContentFilters,
2005
+ isAiAgentsUnavailableError,
1981
2006
  isExternalConnectionsUnavailableError,
2007
+ downloadAiAgents,
1982
2008
  readAiAgentFiles,
1983
2009
  readExternalConnectionFiles,
1984
2010
  readSpaceFiles: spacesAsCode_1.readSpaceFiles,
@@ -1988,6 +2014,7 @@ exports.testHelpers = {
1988
2014
  shouldDownloadAiAgents,
1989
2015
  sortSpaceFilesParentFirst: spacesAsCode_1.sortSpaceFilesParentFirst,
1990
2016
  summarizeUploadChanges,
2017
+ upsertAiAgents,
1991
2018
  upsertExternalConnections,
1992
2019
  upsertSpaces: spacesAsCode_1.upsertSpaces,
1993
2020
  upsertVirtualViews,
@@ -10,11 +10,75 @@ const globalState_1 = tslib_1.__importDefault(require("../globalState"));
10
10
  const appsDownload_1 = require("./apps/appsDownload");
11
11
  const apiClient_1 = require("./dbt/apiClient");
12
12
  const download_1 = require("./download");
13
+ vitest_1.vi.mock('../analytics/analytics', () => ({
14
+ LightdashAnalytics: {
15
+ track: vitest_1.vi.fn(),
16
+ },
17
+ }));
18
+ vitest_1.vi.mock('../config', () => ({
19
+ getConfig: vitest_1.vi.fn().mockResolvedValue({
20
+ user: {
21
+ userUuid: 'user-uuid',
22
+ organizationUuid: 'organization-uuid',
23
+ },
24
+ context: {
25
+ apiKey: 'api-key',
26
+ serverUrl: 'http://lightdash.test',
27
+ project: 'project-uuid',
28
+ projectName: 'Test project',
29
+ },
30
+ answers: {
31
+ metadataFileGitignoreNoticeShown: true,
32
+ },
33
+ }),
34
+ setAnswer: vitest_1.vi.fn(),
35
+ }));
13
36
  vitest_1.vi.mock('./dbt/apiClient', async (importOriginal) => ({
14
37
  ...(await importOriginal()),
38
+ getContentAsCodeUploadPermissions: vitest_1.vi.fn(),
15
39
  lightdashApi: vitest_1.vi.fn(),
16
40
  }));
17
- const { assertUniqueSpacePaths, downloadSpaces, getDashboardChartSlugs, getFlatSpaceFileNames, hasContentFilters, isExternalConnectionsUnavailableError, readAiAgentFiles, readSpaceFiles, readSpaceNames, sanitizeChartForDownload, shouldFallBackToEmbeddedSpaces, shouldDownloadAiAgents, summarizeUploadChanges, upsertExternalConnections, upsertSpaces, upsertVirtualViews, validateSpaceIdentity, writeSpaceFiles, } = download_1.testHelpers;
41
+ const { assertUniqueSpacePaths, downloadSpaces, getDashboardChartSlugs, getFlatSpaceFileNames, hasContentFilters, isAiAgentsUnavailableError, isExternalConnectionsUnavailableError, downloadAiAgents, readAiAgentFiles, readSpaceFiles, readSpaceNames, sanitizeChartForDownload, shouldFallBackToEmbeddedSpaces, shouldDownloadAiAgents, summarizeUploadChanges, upsertAiAgents, upsertExternalConnections, upsertSpaces, upsertVirtualViews, validateSpaceIdentity, writeSpaceFiles, } = download_1.testHelpers;
42
+ const makeDownloadHandlerOptions = (overrides = {}) => ({
43
+ verbose: false,
44
+ charts: [],
45
+ dashboards: [],
46
+ alerts: [],
47
+ agents: [],
48
+ googleSheets: [],
49
+ scheduledDeliveries: [],
50
+ virtualViews: [],
51
+ externalConnections: [],
52
+ apps: [],
53
+ includeAgents: false,
54
+ includeApps: false,
55
+ force: false,
56
+ languageMap: false,
57
+ skipSpaceCreate: false,
58
+ public: false,
59
+ includeCharts: false,
60
+ nested: false,
61
+ rootSpaces: false,
62
+ skipSpaces: true,
63
+ skipCharts: true,
64
+ skipDashboards: true,
65
+ skipAlerts: false,
66
+ skipAgents: false,
67
+ skipGoogleSheets: false,
68
+ skipScheduledDeliveries: false,
69
+ skipVirtualViews: false,
70
+ skipExternalConnections: false,
71
+ includeAlerts: false,
72
+ includeGoogleSheets: false,
73
+ includeScheduledDeliveries: false,
74
+ includeVirtualViews: false,
75
+ includeExternalConnections: false,
76
+ includeAll: false,
77
+ stripPivotSeries: false,
78
+ concurrency: 1,
79
+ organization: false,
80
+ ...overrides,
81
+ });
18
82
  const makeContentFilterOptions = (overrides = {}) => ({
19
83
  spacesOnly: false,
20
84
  charts: [],
@@ -442,6 +506,130 @@ describe('isExternalConnectionsUnavailableError', () => {
442
506
  expect(isExternalConnectionsUnavailableError(new Error('network down'))).toBe(false);
443
507
  });
444
508
  });
509
+ describe('AI agent downloads', () => {
510
+ const unavailableError = new common_1.LightdashError({
511
+ message: "Unable to initialize service 'aiAgentCoderService' - no factory or provider.",
512
+ name: 'MissingConfigError',
513
+ statusCode: 422,
514
+ data: {},
515
+ });
516
+ beforeEach(() => {
517
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockReset();
518
+ });
519
+ it('classifies permission, missing-route and non-EE errors as unavailable', () => {
520
+ [403, 404, 422].forEach((statusCode) => {
521
+ expect(isAiAgentsUnavailableError(new common_1.LightdashError({
522
+ message: 'AI agents unavailable',
523
+ name: 'TestError',
524
+ statusCode,
525
+ data: {},
526
+ }))).toBe(true);
527
+ });
528
+ });
529
+ it('skips unavailable AI agents when selected implicitly', async () => {
530
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockRejectedValueOnce(unavailableError);
531
+ await expect(downloadAiAgents('project-uuid', [], true)).resolves.toBe(0);
532
+ });
533
+ it('keeps explicit AI agent downloads strict', async () => {
534
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockRejectedValueOnce(unavailableError);
535
+ await expect(downloadAiAgents('project-uuid', [], false)).rejects.toBe(unavailableError);
536
+ });
537
+ });
538
+ describe('downloadHandler failures', () => {
539
+ const unavailableError = new common_1.LightdashError({
540
+ message: "Unable to initialize service 'aiAgentCoderService' - no factory or provider.",
541
+ name: 'MissingConfigError',
542
+ statusCode: 422,
543
+ data: {},
544
+ });
545
+ beforeEach(() => {
546
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockReset();
547
+ });
548
+ it('continues to scheduled content after implicitly skipping unavailable AI agents', async () => {
549
+ const tmpDir = await fs_1.promises.mkdtemp(path.join(os.tmpdir(), 'include-all-download-test-'));
550
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockImplementation(async ({ url }) => {
551
+ if (url.startsWith('/api/v1/health')) {
552
+ return { version: '0.0.0' };
553
+ }
554
+ if (url === '/api/v1/projects/project-uuid') {
555
+ return { name: 'Test project' };
556
+ }
557
+ if (url.includes('/code/virtualViews')) {
558
+ return {
559
+ virtualViews: [],
560
+ skipped: [],
561
+ missingSlugs: [],
562
+ };
563
+ }
564
+ if (url.includes('/code/aiAgents')) {
565
+ throw unavailableError;
566
+ }
567
+ if (url.includes('/code/alerts')) {
568
+ return { alerts: [], skipped: [] };
569
+ }
570
+ if (url.includes('/code/scheduledDeliveries')) {
571
+ return { scheduledDeliveries: [], skipped: [] };
572
+ }
573
+ if (url.includes('/code/googleSheets')) {
574
+ return { googleSheetsSyncs: [], skipped: [] };
575
+ }
576
+ if (url.includes('/code/externalConnections')) {
577
+ throw unavailableError;
578
+ }
579
+ if (url.includes('/ee/projects/project-uuid/apps')) {
580
+ throw unavailableError;
581
+ }
582
+ throw new Error(`Unexpected request: ${url}`);
583
+ });
584
+ try {
585
+ await expect((0, download_1.downloadHandler)(makeDownloadHandlerOptions({
586
+ includeAll: true,
587
+ path: tmpDir,
588
+ project: 'project-uuid',
589
+ }))).resolves.toBeUndefined();
590
+ expect(vitest_1.vi.mocked(apiClient_1.lightdashApi)).toHaveBeenCalledWith(expect.objectContaining({
591
+ url: expect.stringContaining('/code/scheduledDeliveries'),
592
+ }));
593
+ }
594
+ finally {
595
+ await fs_1.promises.rm(tmpDir, { recursive: true, force: true });
596
+ }
597
+ });
598
+ it('rethrows fatal project download errors', async () => {
599
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockImplementation(async ({ url }) => {
600
+ if (url.startsWith('/api/v1/health')) {
601
+ return { version: '0.0.0' };
602
+ }
603
+ if (url === '/api/v1/projects/project-uuid') {
604
+ return { name: 'Test project' };
605
+ }
606
+ if (url.includes('/code/aiAgents')) {
607
+ throw unavailableError;
608
+ }
609
+ throw new Error(`Unexpected request: ${url}`);
610
+ });
611
+ await expect((0, download_1.downloadHandler)(makeDownloadHandlerOptions({
612
+ agents: ['sales-agent'],
613
+ project: 'project-uuid',
614
+ }))).rejects.toBe(unavailableError);
615
+ });
616
+ });
617
+ describe('uploadHandler failures', () => {
618
+ beforeEach(() => {
619
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockReset();
620
+ vitest_1.vi.mocked(apiClient_1.getContentAsCodeUploadPermissions).mockReset();
621
+ });
622
+ it('rethrows fatal project upload errors', async () => {
623
+ const fatalError = new Error('Failed to load dbt credentials');
624
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockResolvedValue({
625
+ version: '0.0.0',
626
+ });
627
+ vitest_1.vi.mocked(apiClient_1.getContentAsCodeUploadPermissions).mockRejectedValueOnce(fatalError);
628
+ await expect((0, download_1.uploadHandler)(makeDownloadHandlerOptions({
629
+ project: 'project-uuid',
630
+ }))).rejects.toBe(fatalError);
631
+ });
632
+ });
445
633
  describe('readAiAgentFiles', () => {
446
634
  let tmpDir;
447
635
  beforeEach(async () => {
@@ -481,6 +669,40 @@ describe('readAiAgentFiles', () => {
481
669
  ]);
482
670
  });
483
671
  });
672
+ describe('upsertAiAgents', () => {
673
+ let tmpDir;
674
+ const unavailableError = new common_1.LightdashError({
675
+ message: "Unable to initialize service 'aiAgentCoderService' - no factory or provider.",
676
+ name: 'MissingConfigError',
677
+ statusCode: 422,
678
+ data: {},
679
+ });
680
+ beforeEach(async () => {
681
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockReset();
682
+ vitest_1.vi.spyOn(globalState_1.default, 'log').mockImplementation(() => undefined);
683
+ tmpDir = await fs_1.promises.mkdtemp(path.join(os.tmpdir(), 'agent-upload-test-'));
684
+ await fs_1.promises.mkdir(path.join(tmpDir, 'ai-agents'));
685
+ await fs_1.promises.writeFile(path.join(tmpDir, 'ai-agents', 'revenue-agent.yml'), [
686
+ 'contentType: ai_agent',
687
+ 'version: 1',
688
+ 'slug: revenue-agent',
689
+ '',
690
+ ].join('\n'));
691
+ });
692
+ afterEach(async () => {
693
+ vitest_1.vi.restoreAllMocks();
694
+ await fs_1.promises.rm(tmpDir, { recursive: true, force: true });
695
+ });
696
+ it('skips unavailable AI agents when their upload is implicit', async () => {
697
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockRejectedValueOnce(unavailableError);
698
+ await expect(upsertAiAgents('project-uuid', [], {}, false, tmpDir, true)).resolves.toStrictEqual({});
699
+ expect(globalState_1.default.log).toHaveBeenCalledWith(expect.stringContaining('Skipping AI agents'));
700
+ });
701
+ it('fails when unavailable AI agents were selected explicitly', async () => {
702
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockRejectedValueOnce(unavailableError);
703
+ await expect(upsertAiAgents('project-uuid', ['revenue-agent'], {}, false, tmpDir, false)).rejects.toBe(unavailableError);
704
+ });
705
+ });
484
706
  describe('shouldDownloadAiAgents', () => {
485
707
  it('does not download AI agents by default', () => {
486
708
  expect(shouldDownloadAiAgents({
@@ -5,6 +5,7 @@ export type CustomRoleUploadSummary = {
5
5
  created: number;
6
6
  updated: number;
7
7
  unchanged: number;
8
+ skipped?: number;
8
9
  failed: number;
9
10
  failures: CustomRoleUploadFailure[];
10
11
  };
@@ -1 +1 @@
1
- {"version":3,"file":"customRoles.d.ts","sourceRoot":"","sources":["../../../src/handlers/organizationContent/customRoles.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,KAAK,gBAAgB,EACxB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAKH,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC9B,MAAM,2BAA2B,CAAC;AAGnC,MAAM,MAAM,uBAAuB,GAAG,eAAe,CAAC;AAEtD,MAAM,MAAM,uBAAuB,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,uBAAuB,EAAE,CAAC;CACvC,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,sBAAsB,CAAC,gBAAgB,CAkB1E,CAAC;AAEN,eAAO,MAAM,oBAAoB,4BAA6B,MAAM,KAAG,MACK,CAAC;AAE7E,eAAO,MAAM,6BAA6B,YAC7B,uBAAuB,KACjC,MACmH,CAAC;AAEvH,eAAO,MAAM,mBAAmB,4BACH,MAAM,KAChC,OAAO,CAAC,OAAO,EAAE,CAOnB,CAAC;AAEF,eAAO,MAAM,iBAAiB,qBACR,MAAM,2BACC,MAAM,KAChC,OAAO,CAAC,uBAAuB,CAc5B,CAAC;AAEP,eAAO,MAAM,mBAAmB,qBACV,MAAM,2BACC,MAAM,KAChC,OAAO,CAAC,MAAM,CAcX,CAAC"}
1
+ {"version":3,"file":"customRoles.d.ts","sourceRoot":"","sources":["../../../src/handlers/organizationContent/customRoles.ts"],"names":[],"mappings":"AAAA,OAAO,EAKH,KAAK,gBAAgB,EACxB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAKH,KAAK,eAAe,EACpB,KAAK,sBAAsB,EAC9B,MAAM,2BAA2B,CAAC;AAGnC,MAAM,MAAM,uBAAuB,GAAG,eAAe,CAAC;AAEtD,MAAM,MAAM,uBAAuB,GAAG;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,uBAAuB,EAAE,CAAC;CACvC,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,sBAAsB,CAAC,gBAAgB,CAkB1E,CAAC;AAEN,eAAO,MAAM,oBAAoB,4BAA6B,MAAM,KAAG,MACK,CAAC;AAE7E,eAAO,MAAM,6BAA6B,YAC7B,uBAAuB,KACjC,MAMF,CAAC;AAUF,eAAO,MAAM,mBAAmB,4BACH,MAAM,KAChC,OAAO,CAAC,OAAO,EAAE,CAOnB,CAAC;AAEF,eAAO,MAAM,iBAAiB,qBACR,MAAM,2BACC,MAAM,KAChC,OAAO,CAAC,uBAAuB,CAe5B,CAAC;AAEP,eAAO,MAAM,mBAAmB,qBACV,MAAM,2BACC,MAAM,KAChC,OAAO,CAAC,MAAM,CAcX,CAAC"}
@@ -26,8 +26,17 @@ exports.CUSTOM_ROLE_CODE_RESOURCE = {
26
26
  };
27
27
  const getCustomRolesFolder = (organizationContentPath) => path.join(organizationContentPath, exports.CUSTOM_ROLE_CODE_RESOURCE.folderName);
28
28
  exports.getCustomRolesFolder = getCustomRolesFolder;
29
- const formatCustomRoleUploadSummary = (summary) => `${summary.created} created, ${summary.updated} updated, ${summary.unchanged} unchanged, ${summary.failed} failed`;
29
+ const formatCustomRoleUploadSummary = (summary) => {
30
+ const skipped = summary.skipped && summary.skipped > 0
31
+ ? `, ${summary.skipped} skipped`
32
+ : '';
33
+ return `${summary.created} created, ${summary.updated} updated, ${summary.unchanged} unchanged${skipped}, ${summary.failed} failed`;
34
+ };
30
35
  exports.formatCustomRoleUploadSummary = formatCustomRoleUploadSummary;
36
+ const isCustomRolesUnavailableError = (error) => error instanceof common_1.LightdashError &&
37
+ ([404, 422].includes(error.statusCode) ||
38
+ (error.statusCode === 403 &&
39
+ error.message.includes('Custom roles require a Lightdash Enterprise license')));
31
40
  const readCustomRoleFiles = async (organizationContentPath) => {
32
41
  const result = await (0, resource_1.readCodeResourceFiles)({
33
42
  definition: exports.CUSTOM_ROLE_CODE_RESOURCE,
@@ -48,6 +57,7 @@ const uploadCustomRoles = async (organizationUuid, organizationContentPath) => (
48
57
  });
49
58
  return result.action;
50
59
  },
60
+ skipError: isCustomRolesUnavailableError,
51
61
  });
52
62
  exports.uploadCustomRoles = uploadCustomRoles;
53
63
  const downloadCustomRoles = async (organizationUuid, organizationContentPath) => (0, resource_1.downloadCodeResource)({
@@ -158,4 +158,22 @@ describe('custom roles as code', () => {
158
158
  expect(summary.failures[0].message).toContain('Unknown custom role scopes: delete:VirtualViewsss');
159
159
  expect(apiClient_1.lightdashApi).toHaveBeenCalledTimes(2);
160
160
  });
161
+ it('skips custom roles when the target has no Enterprise license', async () => {
162
+ await writeCustomRole('role.yml', customRole('Enterprise role'));
163
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockRejectedValueOnce(new common_1.LightdashError({
164
+ message: 'Custom roles require a Lightdash Enterprise license',
165
+ name: 'ForbiddenError',
166
+ statusCode: 403,
167
+ data: {},
168
+ }));
169
+ const summary = await (0, customRoles_1.uploadCustomRoles)('organization-uuid', tmpDir);
170
+ expect(summary).toMatchObject({
171
+ created: 0,
172
+ updated: 0,
173
+ unchanged: 0,
174
+ skipped: 1,
175
+ failed: 0,
176
+ failures: [],
177
+ });
178
+ });
161
179
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/handlers/organizationContent/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AA0B3C,KAAK,0BAA0B,GAAG;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAkBF,eAAO,MAAM,4BAA4B,gBAAiB,MAAM,KAAG,MAClC,CAAC;AAElC,eAAO,MAAM,2BAA2B,4BAGrC,0BAA0B,KAAG,OAAO,CAAC,IAAI,CA2F3C,CAAC;AAEF,eAAO,MAAM,yBAAyB,yCAInC,0BAA0B,KAAG,OAAO,CAAC,IAAI,CA0I3C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/handlers/organizationContent/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AA0B3C,KAAK,0BAA0B,GAAG;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAkBF,eAAO,MAAM,4BAA4B,gBAAiB,MAAM,KAAG,MAClC,CAAC;AAElC,eAAO,MAAM,2BAA2B,4BAGrC,0BAA0B,KAAG,OAAO,CAAC,IAAI,CA2F3C,CAAC;AAEF,eAAO,MAAM,yBAAyB,yCAInC,0BAA0B,KAAG,OAAO,CAAC,IAAI,CAoJ3C,CAAC"}
@@ -128,7 +128,10 @@ const uploadOrganizationContent = async ({ customPath, config, sendInvites = fal
128
128
  globalState_1.default.log(styles.warning(`Skipped users and ${skippedGroups} groups because custom roles failed`));
129
129
  throw createCustomRolePartialUploadError(`Processed custom roles: ${summaryMessage}`);
130
130
  }
131
- output.completeItem(summaryMessage);
131
+ if (summary.skipped && summary.skipped > 0) {
132
+ globalState_1.default.log(styles.warning('Skipped custom roles because they require a Lightdash Enterprise license.'));
133
+ }
134
+ output.completeItem(summaryMessage, summary.skipped && summary.skipped > 0 ? 'warning' : undefined);
132
135
  output.startItem('Users');
133
136
  const userSummary = await (0, users_1.uploadUsers)(organizationUuid, organizationContentPath, sendInvites);
134
137
  const userSummaryMessage = (0, users_1.formatUserUploadSummary)(userSummary);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightdash/cli",
3
- "version": "1.48.0",
3
+ "version": "1.49.0",
4
4
  "description": "Lightdash CLI tool",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -41,8 +41,8 @@
41
41
  "unique-names-generator": "4.7.1",
42
42
  "uuid": "11.1.1",
43
43
  "yaml": "2.8.3",
44
- "@lightdash/common": "1.48.0",
45
- "@lightdash/warehouses": "1.48.0"
44
+ "@lightdash/common": "1.49.0",
45
+ "@lightdash/warehouses": "1.49.0"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@types/inquirer": "8.2.4",