@lightdash/cli 2.216.0 → 2.217.1

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.
@@ -1,5 +1,6 @@
1
- import { type AgentAsCode, type AlertAsCode, type ExternalConnectionAsCode, type GoogleSheetsSyncAsCode, type ScheduledDeliveryAsCode, type VirtualViewAsCode } from '@lightdash/common';
1
+ import { type AgentAsCode, type AlertAsCode, type ExternalConnectionAsCode, type GoogleSheetsSyncAsCode, type HomepageAsCode, type ScheduledDeliveryAsCode, type VirtualViewAsCode } from '@lightdash/common';
2
2
  import type { CodeResourceDefinition } from './resource';
3
+ export declare const HOMEPAGE_CODE_RESOURCE: CodeResourceDefinition<HomepageAsCode>;
3
4
  export declare const VIRTUAL_VIEW_CODE_RESOURCE: CodeResourceDefinition<VirtualViewAsCode>;
4
5
  export declare const AI_AGENT_CODE_RESOURCE: CodeResourceDefinition<AgentAsCode>;
5
6
  export declare const EXTERNAL_CONNECTION_CODE_RESOURCE: CodeResourceDefinition<ExternalConnectionAsCode>;
@@ -1 +1 @@
1
- {"version":3,"file":"projectResources.d.ts","sourceRoot":"","sources":["../../../src/handlers/contentAsCode/projectResources.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,eAAO,MAAM,0BAA0B,EAAE,sBAAsB,CAAC,iBAAiB,CAwB5E,CAAC;AAEN,eAAO,MAAM,sBAAsB,EAAE,sBAAsB,CAAC,WAAW,CAyBtE,CAAC;AAEF,eAAO,MAAM,iCAAiC,EAAE,sBAAsB,CAAC,wBAAwB,CAwB1F,CAAC;AA4CN,eAAO,MAAM,gCAAgC,iDAKvC,CAAC;AAEP,eAAO,MAAM,mBAAmB,qCAI9B,CAAC;AAEH,eAAO,MAAM,2BAA2B,gDAKlC,CAAC"}
1
+ {"version":3,"file":"projectResources.d.ts","sourceRoot":"","sources":["../../../src/handlers/contentAsCode/projectResources.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,wBAAwB,EAC7B,KAAK,sBAAsB,EAC3B,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD,eAAO,MAAM,sBAAsB,EAAE,sBAAsB,CAAC,cAAc,CAiBzE,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,sBAAsB,CAAC,iBAAiB,CAwB5E,CAAC;AAEN,eAAO,MAAM,sBAAsB,EAAE,sBAAsB,CAAC,WAAW,CAyBtE,CAAC;AAEF,eAAO,MAAM,iCAAiC,EAAE,sBAAsB,CAAC,wBAAwB,CAwB1F,CAAC;AA4CN,eAAO,MAAM,gCAAgC,iDAKvC,CAAC;AAEP,eAAO,MAAM,mBAAmB,qCAI9B,CAAC;AAEH,eAAO,MAAM,2BAA2B,gDAKlC,CAAC"}
@@ -1,7 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GOOGLE_SHEETS_CODE_RESOURCE = exports.ALERT_CODE_RESOURCE = exports.SCHEDULED_DELIVERY_CODE_RESOURCE = exports.EXTERNAL_CONNECTION_CODE_RESOURCE = exports.AI_AGENT_CODE_RESOURCE = exports.VIRTUAL_VIEW_CODE_RESOURCE = void 0;
3
+ exports.GOOGLE_SHEETS_CODE_RESOURCE = exports.ALERT_CODE_RESOURCE = exports.SCHEDULED_DELIVERY_CODE_RESOURCE = exports.EXTERNAL_CONNECTION_CODE_RESOURCE = exports.AI_AGENT_CODE_RESOURCE = exports.VIRTUAL_VIEW_CODE_RESOURCE = exports.HOMEPAGE_CODE_RESOURCE = void 0;
4
4
  const common_1 = require("@lightdash/common");
5
+ exports.HOMEPAGE_CODE_RESOURCE = {
6
+ kind: common_1.ContentAsCodeType.HOMEPAGE,
7
+ displayLabel: 'homepage',
8
+ identityLabel: 'name',
9
+ scope: 'project',
10
+ folderName: 'homepages',
11
+ acceptedExtensions: ['.yml', '.yaml'],
12
+ fileName: {
13
+ strategy: 'uriEncodedIdentity',
14
+ fallbackPrefix: 'homepage',
15
+ extension: '.yml',
16
+ },
17
+ dependencies: ['space', 'chart', 'dashboard'],
18
+ identity: ({ name }) => name,
19
+ displayName: ({ name }) => name,
20
+ parse: common_1.parseHomepageAsCode,
21
+ sort: (a, b) => a.name.localeCompare(b.name),
22
+ };
5
23
  exports.VIRTUAL_VIEW_CODE_RESOURCE = {
6
24
  kind: common_1.ContentAsCodeType.VIRTUAL_VIEW,
7
25
  displayLabel: 'virtual view',
@@ -1 +1 @@
1
- {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/handlers/contentAsCode/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,KAAK,yBAAyB,EACjC,MAAM,mBAAmB,CAAC;AAc3B,MAAM,MAAM,wBAAwB,GAAG;IACnC,IAAI,EAAE,yBAAyB,CAAC;IAChC,YAAY,EAAE,SAAS,yBAAyB,EAAE,CAAC;CACtD,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,SAAS,wBAAwB,EAMtE,CAAC;AAEN,eAAO,MAAM,sBAAsB,EAAE,SAAS,wBAAwB,EAwBrE,CAAC;AAEF,eAAO,MAAM,iCAAiC,cAC/B,SAAS,wBAAwB,EAAE,KAC/C,IAeF,CAAC"}
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/handlers/contentAsCode/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,KAAK,yBAAyB,EACjC,MAAM,mBAAmB,CAAC;AAe3B,MAAM,MAAM,wBAAwB,GAAG;IACnC,IAAI,EAAE,yBAAyB,CAAC;IAChC,YAAY,EAAE,SAAS,yBAAyB,EAAE,CAAC;CACtD,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,SAAS,wBAAwB,EAMtE,CAAC;AAEN,eAAO,MAAM,sBAAsB,EAAE,SAAS,wBAAwB,EAyBrE,CAAC;AAEF,eAAO,MAAM,iCAAiC,cAC/B,SAAS,wBAAwB,EAAE,KAC/C,IAeF,CAAC"}
@@ -37,6 +37,7 @@ exports.PROJECT_CODE_RESOURCES = [
37
37
  projectResources_1.ALERT_CODE_RESOURCE,
38
38
  projectResources_1.GOOGLE_SHEETS_CODE_RESOURCE,
39
39
  projectResources_1.EXTERNAL_CONNECTION_CODE_RESOURCE,
40
+ projectResources_1.HOMEPAGE_CODE_RESOURCE,
40
41
  ];
41
42
  const assertCodeResourceDependencyOrder = (resources) => {
42
43
  const completed = new Set();
@@ -32,6 +32,38 @@ const agent = (slug) => ({
32
32
  })));
33
33
  });
34
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).toHaveLength(1);
53
+ (0, vitest_1.expect)(fileNames[0]).not.toContain('/');
54
+ const result = await (0, resource_1.readCodeResourceFiles)({
55
+ definition: projectResources_1.HOMEPAGE_CODE_RESOURCE,
56
+ basePath,
57
+ });
58
+ (0, vitest_1.expect)(result.files[0].document).toEqual(document);
59
+ await fs_1.promises.copyFile(path_1.default.join(basePath, 'homepages', fileNames[0]), path_1.default.join(basePath, 'homepages', 'duplicate.yaml'));
60
+ const duplicates = await (0, resource_1.readCodeResourceFiles)({
61
+ definition: projectResources_1.HOMEPAGE_CODE_RESOURCE,
62
+ basePath,
63
+ });
64
+ (0, vitest_1.expect)(duplicates.files).toEqual([]);
65
+ (0, vitest_1.expect)(duplicates.failures[0].message).toContain('Duplicate');
66
+ });
35
67
  (0, vitest_1.it)('preserves other documents when writing a filtered download', async () => {
36
68
  const basePath = await fs_1.promises.mkdtemp(path_1.default.join(os_1.default.tmpdir(), 'lightdash-content-as-code-'));
37
69
  temporaryDirectories.push(basePath);
@@ -10,6 +10,10 @@ export type DownloadHandlerOptions = {
10
10
  googleSheets: string[];
11
11
  scheduledDeliveries: string[];
12
12
  virtualViews: string[];
13
+ homepages?: string[];
14
+ includeHomepages?: boolean;
15
+ skipHomepages?: boolean;
16
+ publish?: boolean;
13
17
  externalConnections: string[];
14
18
  apps?: string[];
15
19
  chartTypes?: string[];
@@ -57,7 +61,7 @@ export type DownloadHandlerOptions = {
57
61
  sendInvites?: boolean;
58
62
  };
59
63
  declare const shouldDownloadAiAgents: ({ includeAll, includeAgents, agents, appsOnly, }: Pick<DownloadHandlerOptions, 'includeAll' | 'includeAgents' | 'agents' | 'appsOnly'>) => boolean;
60
- declare const hasContentFilters: ({ spacesOnly, charts, dashboards, agents, alerts, googleSheets, scheduledDeliveries, virtualViews, externalConnections, apps, chartTypes, }: Pick<DownloadHandlerOptions, 'spacesOnly' | 'charts' | 'dashboards' | 'agents' | 'alerts' | 'googleSheets' | 'scheduledDeliveries' | 'virtualViews' | 'externalConnections' | 'apps' | 'chartTypes'>) => boolean;
64
+ declare const hasContentFilters: ({ spacesOnly, charts, dashboards, agents, alerts, googleSheets, scheduledDeliveries, virtualViews, externalConnections, homepages, apps, chartTypes, }: Pick<DownloadHandlerOptions, 'spacesOnly' | 'charts' | 'dashboards' | 'agents' | 'alerts' | 'googleSheets' | 'scheduledDeliveries' | 'virtualViews' | 'externalConnections' | 'homepages' | 'apps' | 'chartTypes'>) => boolean;
61
65
  declare const parseContentFilters: (items: string[]) => string;
62
66
  type MetadataEntry = {
63
67
  slug: string;
@@ -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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"}
@@ -28,13 +28,14 @@ const projectResources_1 = require("./contentAsCode/projectResources");
28
28
  const resource_1 = require("./contentAsCode/resource");
29
29
  const contentAsCodePaths_1 = require("./contentAsCodePaths");
30
30
  const apiClient_1 = require("./dbt/apiClient");
31
+ const homepagesAsCode_1 = require("./homepagesAsCode");
31
32
  const metadataFile_1 = require("./metadataFile");
32
33
  const organizationContent_1 = require("./organizationContent");
33
34
  const selectProject_1 = require("./selectProject");
34
35
  const spacesAsCode_1 = require("./spacesAsCode");
35
36
  const shouldDownloadAiAgents = ({ includeAll, includeAgents, agents, appsOnly, }) => appsOnly !== true &&
36
37
  (includeAll === true || includeAgents === true || agents.length > 0);
37
- const hasContentFilters = ({ spacesOnly, charts, dashboards, agents, alerts, googleSheets, scheduledDeliveries, virtualViews, externalConnections, apps, chartTypes, }) => !spacesOnly &&
38
+ const hasContentFilters = ({ spacesOnly, charts, dashboards, agents, alerts, googleSheets, scheduledDeliveries, virtualViews, externalConnections, homepages, apps, chartTypes, }) => !spacesOnly &&
38
39
  [
39
40
  charts,
40
41
  dashboards,
@@ -44,6 +45,7 @@ const hasContentFilters = ({ spacesOnly, charts, dashboards, agents, alerts, goo
44
45
  scheduledDeliveries,
45
46
  virtualViews,
46
47
  externalConnections,
48
+ homepages ?? [],
47
49
  apps ?? [],
48
50
  chartTypes ?? [],
49
51
  ].some((filters) => filters.length > 0);
@@ -1154,6 +1156,20 @@ const downloadHandler = async (options) => {
1154
1156
  // chart types step.
1155
1157
  let downloadedChartVizRefs = [];
1156
1158
  const explicitChartTypeRefs = new Set((Array.isArray(options.chartTypes) ? options.chartTypes : []).map(appsDownload_1.getDataAppReference));
1159
+ if (!options.appsOnly &&
1160
+ !options.chartTypesOnly &&
1161
+ !options.spacesOnly &&
1162
+ (options.includeHomepages ||
1163
+ options.homepages?.length ||
1164
+ includeAllOptionalContent)) {
1165
+ await output.runItem({
1166
+ label: 'Homepages',
1167
+ action: () => (0, homepagesAsCode_1.downloadHomepages)(projectId, options.homepages ?? [], options.path, includeAllOptionalContent &&
1168
+ !options.includeHomepages &&
1169
+ !options.homepages?.length),
1170
+ detail: (count) => `${count} downloaded`,
1171
+ });
1172
+ }
1157
1173
  if (shouldDownloadSpaces) {
1158
1174
  output.startItem('Spaces');
1159
1175
  try {
@@ -2806,6 +2822,21 @@ const uploadHandler = async (options) => {
2806
2822
  });
2807
2823
  }
2808
2824
  }
2825
+ if (!options.skipHomepages &&
2826
+ !options.appsOnly &&
2827
+ !options.chartTypesOnly &&
2828
+ (!hasFilters || options.homepages?.length)) {
2829
+ changes = await runUploadChangesPhase({
2830
+ output,
2831
+ label: 'Homepages',
2832
+ changes,
2833
+ action: () => {
2834
+ if (hasUploadFailures(changes))
2835
+ throw new common_1.ParameterError('Homepage upload blocked because prerequisite content uploads failed');
2836
+ return (0, homepagesAsCode_1.uploadHomepages)(projectId, options.homepages ?? [], changes, options.publish === true, contentPathOption);
2837
+ },
2838
+ });
2839
+ }
2809
2840
  const end = Date.now();
2810
2841
  await analytics_1.LightdashAnalytics.track({
2811
2842
  event: 'upload.completed',
@@ -130,6 +130,9 @@ const makeContentFilterOptions = (overrides = {}) => ({
130
130
  ...overrides,
131
131
  });
132
132
  describe('hasContentFilters', () => {
133
+ it('treats explicit homepage names as a filtered download and upload', () => {
134
+ expect(hasContentFilters(makeContentFilterOptions({ homepages: ['Team homepage'] }))).toBe(true);
135
+ });
133
136
  it('treats explicit apps as a filtered download and upload', () => {
134
137
  expect(hasContentFilters(makeContentFilterOptions({ apps: ['app-reference'] }))).toBe(true);
135
138
  });
@@ -1026,7 +1029,8 @@ describe('downloadHandler failures', () => {
1026
1029
  missingSlugs: [],
1027
1030
  };
1028
1031
  }
1029
- if (url.includes('/code/aiAgents')) {
1032
+ if (url.includes('/code/aiAgents') ||
1033
+ url.includes('/code/homepages')) {
1030
1034
  throw unavailableError;
1031
1035
  }
1032
1036
  if (url.includes('/code/alerts')) {
@@ -0,0 +1,3 @@
1
+ export declare const downloadHomepages: (projectUuid: string, names: string[], customPath?: string, implicit?: boolean) => Promise<number>;
2
+ export declare const uploadHomepages: (projectUuid: string, names: string[], changes: Record<string, number>, publish: boolean, customPath?: string) => Promise<Record<string, number>>;
3
+ //# sourceMappingURL=homepagesAsCode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"homepagesAsCode.d.ts","sourceRoot":"","sources":["../../src/handlers/homepagesAsCode.ts"],"names":[],"mappings":"AAoBA,eAAO,MAAM,iBAAiB,gBACb,MAAM,SACZ,MAAM,EAAE,eACF,MAAM,aACT,OAAO,KAClB,OAAO,CAAC,MAAM,CAqChB,CAAC;AAEF,eAAO,MAAM,eAAe,gBACX,MAAM,SACZ,MAAM,EAAE,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,WACtB,OAAO,eACH,MAAM,KACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA8ChC,CAAC"}
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.uploadHomepages = exports.downloadHomepages = void 0;
4
+ const tslib_1 = require("tslib");
5
+ /* eslint-disable no-await-in-loop */
6
+ const common_1 = require("@lightdash/common");
7
+ const globalState_1 = tslib_1.__importDefault(require("../globalState"));
8
+ const styles = tslib_1.__importStar(require("../styles"));
9
+ const projectResources_1 = require("./contentAsCode/projectResources");
10
+ const resource_1 = require("./contentAsCode/resource");
11
+ const contentAsCodePaths_1 = require("./contentAsCodePaths");
12
+ const apiClient_1 = require("./dbt/apiClient");
13
+ const downloadHomepages = async (projectUuid, names, customPath, implicit = false) => {
14
+ const query = new URLSearchParams(names.map((name) => ['names', name]));
15
+ let result;
16
+ try {
17
+ result = await (0, apiClient_1.lightdashApi)({
18
+ method: 'GET',
19
+ url: `/api/v1/projects/${projectUuid}/code/homepages?${query}`,
20
+ body: undefined,
21
+ });
22
+ }
23
+ catch (error) {
24
+ if (implicit &&
25
+ error instanceof common_1.LightdashError &&
26
+ [403, 404, 422].includes(error.statusCode)) {
27
+ globalState_1.default.log(styles.warning('Skipping homepages: the homepage builder and homepage management permission are required.'));
28
+ return 0;
29
+ }
30
+ throw error;
31
+ }
32
+ if (result.missingNames.length)
33
+ throw new common_1.ParameterError(`Homepages not found or unpublished: ${result.missingNames.join(', ')}`);
34
+ await (0, resource_1.writeCodeResourceDocuments)({
35
+ definition: projectResources_1.HOMEPAGE_CODE_RESOURCE,
36
+ basePath: (0, contentAsCodePaths_1.getDownloadFolder)(customPath),
37
+ documents: result.homepages,
38
+ pruneOtherDocuments: names.length === 0,
39
+ });
40
+ return result.homepages.length;
41
+ };
42
+ exports.downloadHomepages = downloadHomepages;
43
+ const uploadHomepages = async (projectUuid, names, changes, publish, customPath) => {
44
+ const uploadChanges = { ...changes };
45
+ const files = await (0, resource_1.readCodeResourceFiles)({
46
+ definition: projectResources_1.HOMEPAGE_CODE_RESOURCE,
47
+ basePath: (0, contentAsCodePaths_1.getDownloadFolder)(customPath),
48
+ });
49
+ (0, resource_1.assertCodeResourceFilesValid)(files);
50
+ const documents = files.files
51
+ .map((file) => file.document)
52
+ .filter((document) => names.length === 0 || names.includes(document.name));
53
+ const missing = names.filter((name) => !documents.some((document) => document.name === name));
54
+ if (missing.length)
55
+ throw new common_1.ParameterError(`Homepages not found locally: ${missing.join(', ')}`);
56
+ for (const document of documents) {
57
+ try {
58
+ const result = await (0, apiClient_1.lightdashApi)({
59
+ method: 'POST',
60
+ url: `/api/v1/projects/${projectUuid}/code/homepages/${encodeURIComponent(document.name)}?publish=${publish}`,
61
+ body: JSON.stringify(document),
62
+ });
63
+ const actionLabels = {
64
+ [common_1.PromotionAction.CREATE]: 'created',
65
+ [common_1.PromotionAction.UPDATE]: 'updated',
66
+ [common_1.PromotionAction.NO_CHANGES]: 'unchanged',
67
+ };
68
+ const key = `homepages ${actionLabels[result.action]}`;
69
+ uploadChanges[key] = (uploadChanges[key] ?? 0) + 1;
70
+ }
71
+ catch (error) {
72
+ uploadChanges['homepages with errors'] =
73
+ (uploadChanges['homepages with errors'] ?? 0) + 1;
74
+ globalState_1.default.log(styles.error(`Error uploading homepage "${document.name}": ${(0, common_1.getErrorMessage)(error)}`));
75
+ }
76
+ }
77
+ return uploadChanges;
78
+ };
79
+ exports.uploadHomepages = uploadHomepages;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=homepagesAsCode.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"homepagesAsCode.test.d.ts","sourceRoot":"","sources":["../../src/handlers/homepagesAsCode.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const common_1 = require("@lightdash/common");
4
+ const vitest_1 = require("vitest");
5
+ const resource_1 = require("./contentAsCode/resource");
6
+ const apiClient_1 = require("./dbt/apiClient");
7
+ const homepagesAsCode_1 = require("./homepagesAsCode");
8
+ vitest_1.vi.mock('./dbt/apiClient', () => ({ lightdashApi: vitest_1.vi.fn() }));
9
+ vitest_1.vi.mock('./contentAsCode/resource', () => ({
10
+ readCodeResourceFiles: vitest_1.vi.fn(),
11
+ writeCodeResourceDocuments: vitest_1.vi.fn(),
12
+ assertCodeResourceFilesValid: vitest_1.vi.fn(),
13
+ }));
14
+ vitest_1.vi.mock('../globalState', () => ({ default: { log: vitest_1.vi.fn() } }));
15
+ const document = {
16
+ contentType: common_1.ContentAsCodeType.HOMEPAGE,
17
+ version: 1,
18
+ name: 'Home / Team',
19
+ config: { version: 1, rows: [] },
20
+ publication: null,
21
+ };
22
+ (0, vitest_1.describe)('homepage CLI download/upload', () => {
23
+ (0, vitest_1.beforeEach)(() => vitest_1.vi.clearAllMocks());
24
+ (0, vitest_1.it)('requests exact names and preserves other files during a filtered download', async () => {
25
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockResolvedValue({
26
+ homepages: [document],
27
+ missingNames: [],
28
+ });
29
+ await (0, homepagesAsCode_1.downloadHomepages)('project', [document.name], '/tmp/homepage-code');
30
+ (0, vitest_1.expect)(apiClient_1.lightdashApi).toHaveBeenCalledWith(vitest_1.expect.objectContaining({
31
+ url: '/api/v1/projects/project/code/homepages?names=Home+%2F+Team',
32
+ }));
33
+ (0, vitest_1.expect)(resource_1.writeCodeResourceDocuments).toHaveBeenCalledWith(vitest_1.expect.objectContaining({
34
+ documents: [document],
35
+ pruneOtherDocuments: false,
36
+ }));
37
+ });
38
+ (0, vitest_1.it)('skips unavailable homepages only for implicit include-all downloads', async () => {
39
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockRejectedValue(new common_1.ForbiddenError());
40
+ await (0, vitest_1.expect)((0, homepagesAsCode_1.downloadHomepages)('project', [], undefined, true)).resolves.toBe(0);
41
+ await (0, vitest_1.expect)((0, homepagesAsCode_1.downloadHomepages)('project', [], undefined, false)).rejects.toThrow();
42
+ (0, vitest_1.expect)(resource_1.writeCodeResourceDocuments).not.toHaveBeenCalled();
43
+ });
44
+ (0, vitest_1.it)('passes the publication option explicitly and reports unchanged uploads', async () => {
45
+ vitest_1.vi.mocked(resource_1.readCodeResourceFiles).mockResolvedValue({
46
+ files: [{ filePath: 'home.yml', document }],
47
+ failures: [],
48
+ });
49
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockResolvedValue({
50
+ action: common_1.PromotionAction.NO_CHANGES,
51
+ });
52
+ await (0, vitest_1.expect)((0, homepagesAsCode_1.uploadHomepages)('project', [], {}, false)).resolves.toEqual({ 'homepages unchanged': 1 });
53
+ (0, vitest_1.expect)(apiClient_1.lightdashApi).toHaveBeenLastCalledWith(vitest_1.expect.objectContaining({
54
+ method: 'POST',
55
+ url: '/api/v1/projects/project/code/homepages/Home%20%2F%20Team?publish=false',
56
+ }));
57
+ await (0, homepagesAsCode_1.uploadHomepages)('project', [], {}, true);
58
+ (0, vitest_1.expect)(apiClient_1.lightdashApi).toHaveBeenLastCalledWith(vitest_1.expect.objectContaining({
59
+ url: '/api/v1/projects/project/code/homepages/Home%20%2F%20Team?publish=true',
60
+ }));
61
+ });
62
+ (0, vitest_1.it)('fails missing selections and records API failures for a nonzero CLI exit', async () => {
63
+ vitest_1.vi.mocked(resource_1.readCodeResourceFiles).mockResolvedValue({
64
+ files: [{ filePath: 'home.yml', document }],
65
+ failures: [],
66
+ });
67
+ await (0, vitest_1.expect)((0, homepagesAsCode_1.uploadHomepages)('project', ['Missing'], {}, false)).rejects.toThrow('not found locally');
68
+ (0, vitest_1.expect)(apiClient_1.lightdashApi).not.toHaveBeenCalled();
69
+ vitest_1.vi.mocked(apiClient_1.lightdashApi).mockRejectedValue(new Error('Missing reference'));
70
+ await (0, vitest_1.expect)((0, homepagesAsCode_1.uploadHomepages)('project', [], {}, false)).resolves.toEqual({ 'homepages with errors': 1 });
71
+ });
72
+ });
package/dist/index.js CHANGED
@@ -368,6 +368,8 @@ const downloadCommand = commander_1.program
368
368
  .option('--virtual-views <slugs...>', 'specify virtual view slugs to download', refsArgument_1.parseRefsArgument, [])
369
369
  .option('--google-sheets <slugs...>', 'specify Google Sheets sync slugs to download', refsArgument_1.parseRefsArgument, [])
370
370
  .option('--scheduled-deliveries <slugs...>', 'specify scheduled delivery slugs to download', refsArgument_1.parseRefsArgument, [])
371
+ .option('--homepages <names...>', 'specify exact homepage names to download', [])
372
+ .option('--include-homepages', 'include all published homepages (enterprise)', false)
371
373
  .option('--external-connections <slugs...>', 'specify external connection slugs to download (enterprise)', refsArgument_1.parseRefsArgument, [])
372
374
  .option('--include-external-connections', "include all of the project's data-app external connections (enterprise)", false)
373
375
  .option('-l, --language-map', 'generate a language maps for the downloaded charts and dashboards', false)
@@ -406,6 +408,9 @@ const uploadCommand = commander_1.program
406
408
  .option('--virtual-views <slugs...>', 'specify virtual view slugs to upload', refsArgument_1.parseRefsArgument, [])
407
409
  .option('--google-sheets <slugs...>', 'specify Google Sheets sync slugs to upload', refsArgument_1.parseRefsArgument, [])
408
410
  .option('--scheduled-deliveries <slugs...>', 'specify scheduled delivery slugs to upload', refsArgument_1.parseRefsArgument, [])
411
+ .option('--homepages <names...>', 'specify exact homepage names to upload', [])
412
+ .option('--skip-homepages', 'skip uploading homepages', false)
413
+ .option('--publish', 'publish uploaded homepages and apply their audience assignments', false)
409
414
  .option('--external-connections <slugs...>', 'specify external connection slugs to upload (enterprise)', refsArgument_1.parseRefsArgument, [])
410
415
  .option('--force', 'Force upload unchanged files and allow destructive virtual-view column changes', false)
411
416
  .option('-p, --path <path>', 'specify a custom path to upload content from', undefined)
@@ -176,9 +176,10 @@ action menu. This is part of the component contract, not an optional nicety —
176
176
  item shows; you always write the wiring, and it costs nothing visually when disabled.
177
177
 
178
178
  When `underlyingData.enabled` is true, viewers get the standard Lightdash action on
179
- chart marks: click a data point → small action menu → "View underlying data" → a dialog
180
- listing the raw rows behind that point, with a Download button. When it is false (host
181
- too old, viewer lacks permission, embed), render no menu item — never a disabled one.
179
+ chart marks: click a data point → small action menu → "View underlying data" → the
180
+ standard Lightdash dialog listing the raw rows behind that point, with its Download
181
+ button. When it is false (host too old, viewer lacks permission, embed), render no menu
182
+ item — never a disabled one.
182
183
 
183
184
  Provenance is the contract: **every interactive datum keeps a reference to its
184
185
  untransformed source row.** When mapping `rows` into chart data, carry the row:
@@ -195,33 +196,17 @@ Only attach the action where one mark maps to exactly ONE source row and ONE met
195
196
  field. A mark that aggregates several rows (a binned bucket, a "top N + other" slice)
196
197
  gets no underlying-data item.
197
198
 
198
- On click, fetch and render in a dialog themed like the rest of the viz:
199
+ On click, send the intent and render nothing else. Lightdash opens the dialog outside
200
+ the viz and owns its table, loading, error, sorting, and download controls:
199
201
 
200
202
  ```tsx
201
- const result = await underlyingData.get({ row: datum.sourceRow, metric: 'value' });
202
- // result.rows / result.columns / result.format — render as a table
203
- // Download button:
204
- // await underlyingData.download({ row: datum.sourceRow, metric: 'value', fileType: 'csv' });
203
+ underlyingData.open({ row: datum.sourceRow, metric: 'value' }).catch(() => {});
205
204
  ```
206
205
 
207
206
  `metric` is the declared field NAME from your `fields` (the same key you read from
208
- `fieldMapping`), not a query field id. Show `get()`/`download()` rejection messages in
209
- the dialog — they are written for viewers.
210
-
211
- Keep the dialog table dense — underlying data is often wide and long. Compact cell
212
- padding (the table is for scanning, not presenting). If the header is sticky: pin it
213
- flush at `top: 0` with an opaque background, and put NO padding on the scroll
214
- container itself — pad the cells, not the scroller. A sticky header pins to the
215
- container's content edge, so any container padding becomes a strip that rows
216
- visibly scroll through above the header.
217
-
218
- The table must scroll BOTH ways: `overflow: auto` on ONE wrapper directly around the
219
- `<table>`, and every element between that wrapper and the dialog body a plain block
220
- that can shrink (`min-width: 0`; no `display: table`, no `w-max`/`width:
221
- max-content`). A wrapper that sizes to the table's intrinsic width makes the
222
- overflow container's horizontal scroll dead — the table looks clipped and viewers
223
- cannot reach the right-hand columns. Verify by scrolling right in the rendered
224
- dialog with more columns than fit.
207
+ `fieldMapping`), not a query field id. The custom chart type owns only its data-point
208
+ action menu. It never renders an underlying-data dialog, table, loading state, error
209
+ state, or download control inside the iframe.
225
210
 
226
211
  ### Drill into a data point
227
212
 
@@ -28,6 +28,7 @@ Users describe features by what they see in the Lightdash editor. Translate:
28
28
  | runs inside a dashboard tile | `viz-context` | required — see below |
29
29
  | reusable chart/table with pivoted results | `viz-pivoted-results` | required — see below |
30
30
  | "view underlying data", raw rows behind a point (viz only) | `viz-underlying-data` | app code opt-in |
31
+ | Lightdash-owned "View underlying data" dialog (viz only) | `viz-host-underlying-data` | app code opt-in |
31
32
  | light/dark mode, "matches my Lightdash theme" | `follow-host-theme` | CSS tokens — see below |
32
33
 
33
34
  ## Automatic (zero wiring — active on any current-SDK bundle)
@@ -119,11 +120,11 @@ order, `sortBy` describes result ordering, `totalColumnCount` exposes truncation
119
120
  every tab's queries execute during capture while only the active tab stays
120
121
  visible — never mount all tabs unconditionally (interactive loads must stay
121
122
  lazy).
122
- - `viz-underlying-data` (vizs only): keep the untransformed `sourceRow` on each
123
- interactive datum, gate a data-point action menu on
124
- `useVizContext().underlyingData.enabled`, render
125
- `underlyingData.get({ row, metric })` in a themed dialog, and wire its
126
- Download button to `underlyingData.download`. Full contract in the
123
+ - `viz-underlying-data` and `viz-host-underlying-data` (vizs only): keep the
124
+ untransformed `sourceRow` on each interactive datum, gate a data-point action menu on
125
+ `useVizContext().underlyingData.enabled`, and call
126
+ `underlyingData.open({ row, metric })`. Lightdash renders the dialog and
127
+ owns its table, loading, error, and download controls. Full contract in the
127
128
  `reusable-visualization` skill.
128
129
 
129
130
  ## After a template upgrade
@@ -132,3 +133,7 @@ An upgrade rebuilds the app on the current SDK, which turns on the automatic
132
133
  capabilities but does NOT add wiring — that is a deliberate rail. When you
133
134
  offer newly available features, offer wiring-required ones too (their registry
134
135
  entries carry a `wiring` note); implement only when the user asks.
136
+
137
+ Existing published visualization bundles are not rewritten by this registry.
138
+ Bundles built before `viz-host-underlying-data` keep their iframe-owned
139
+ `get`/`download` dialog until the user explicitly upgrades that feature.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightdash/cli",
3
- "version": "2.216.0",
3
+ "version": "2.217.1",
4
4
  "description": "Lightdash CLI tool",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -22,8 +22,8 @@
22
22
  "@actions/core": "1.11.1",
23
23
  "@aws-sdk/credential-providers": "3.995.0",
24
24
  "@casl/ability": "6.8.0",
25
- "@lightdash/common": "2.216.0",
26
- "@lightdash/warehouses": "2.216.0",
25
+ "@lightdash/common": "2.217.1",
26
+ "@lightdash/warehouses": "2.217.1",
27
27
  "@sidvind/better-ajv-errors": "7.0.0",
28
28
  "@types/columnify": "1.5.1",
29
29
  "ajv": "8.18.0",