@lightdash/cli 1.68.0 → 1.69.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.
|
@@ -60,14 +60,24 @@ type MetadataEntry = {
|
|
|
60
60
|
declare const sanitizeChartForDownload: (chart: ChartAsCode, stripPivotSeries: boolean) => ChartAsCode;
|
|
61
61
|
type DownloadContentType = 'charts' | 'dashboards' | 'sqlCharts';
|
|
62
62
|
declare const extractAppSlugsFromDashboards: (dashboards: DashboardAsCode[]) => string[];
|
|
63
|
+
declare const extractChartTableNames: (charts: ChartAsCode[]) => string[];
|
|
63
64
|
export type DownloadContentResult = {
|
|
64
65
|
total: number;
|
|
65
66
|
chartSlugs: string[];
|
|
67
|
+
chartTableNames: string[];
|
|
66
68
|
appSlugs: string[];
|
|
67
69
|
metadataEntries: MetadataEntry[];
|
|
68
70
|
spaces: SpaceAsCode[];
|
|
69
71
|
};
|
|
70
72
|
export declare const downloadContent: (ids: string[], type: DownloadContentType, projectId: string, projectName: string, customPath?: string, languageMap?: boolean, nested?: boolean, skipSpaces?: boolean, stripPivotSeries?: boolean, rootSpaces?: boolean, onProgress?: (detail: string) => void) => Promise<DownloadContentResult>;
|
|
73
|
+
declare const isVirtualViewsUnavailableError: (error: unknown) => boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Downloads the virtual views backing a dashboard's charts. The candidates are
|
|
76
|
+
* chart table names, so ones the server reports as missing are just regular
|
|
77
|
+
* dbt explores — expected, never warned. Returns null when virtual views are
|
|
78
|
+
* unavailable on the server.
|
|
79
|
+
*/
|
|
80
|
+
declare const downloadLinkedVirtualViews: (projectId: string, tableNames: string[], customPath?: string) => Promise<number | null>;
|
|
71
81
|
declare const upsertVirtualViews: (projectId: string, slugs: string[], changes: Record<string, number>, force: boolean, canUpload: boolean, customPath?: string) => Promise<Record<string, number>>;
|
|
72
82
|
export declare const getExternalConnectionSecretEnvVar: (slug: string) => string;
|
|
73
83
|
/**
|
|
@@ -99,14 +109,17 @@ declare const isFilteredWithNoDashboards: (hasFilters: boolean, dashboardSlugs:
|
|
|
99
109
|
export declare const uploadHandler: (options: DownloadHandlerOptions) => Promise<void>;
|
|
100
110
|
export declare const testHelpers: {
|
|
101
111
|
assertUniqueSpacePaths: typeof assertUniqueSpacePaths;
|
|
112
|
+
downloadLinkedVirtualViews: typeof downloadLinkedVirtualViews;
|
|
102
113
|
downloadSpaces: typeof downloadSpaces;
|
|
103
114
|
extractAppSlugsFromDashboards: typeof extractAppSlugsFromDashboards;
|
|
115
|
+
extractChartTableNames: typeof extractChartTableNames;
|
|
104
116
|
getFlatSpaceFileNames: typeof getFlatSpaceFileNames;
|
|
105
117
|
getDashboardAppSlugs: typeof getDashboardAppSlugs;
|
|
106
118
|
getDashboardChartSlugs: typeof getDashboardChartSlugs;
|
|
107
119
|
hasContentFilters: typeof hasContentFilters;
|
|
108
120
|
isAiAgentsUnavailableError: typeof isAiAgentsUnavailableError;
|
|
109
121
|
isExternalConnectionsUnavailableError: typeof isExternalConnectionsUnavailableError;
|
|
122
|
+
isVirtualViewsUnavailableError: typeof isVirtualViewsUnavailableError;
|
|
110
123
|
downloadAiAgents: typeof downloadAiAgents;
|
|
111
124
|
isFilteredWithNoDashboards: typeof isFilteredWithNoDashboards;
|
|
112
125
|
readAiAgentFiles: typeof readAiAgentFiles;
|
|
@@ -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":"AAEA,OAAO,EACH,WAAW,EAyBX,WAAW,EAGX,eAAe,EAEf,wBAAwB,EAgBxB,KAAK,WAAW,EACnB,MAAM,mBAAmB,CAAC;AAY3B,OAAO,EAIH,KAAK,0BAA0B,EAClC,MAAM,iCAAiC,CAAC;AAiEzC,OAAO,EACH,sBAAsB,EAGtB,cAAc,EACd,qBAAqB,EAKrB,cAAc,EACd,cAAc,EACd,8BAA8B,EAC9B,yBAAyB,EACzB,YAAY,EACZ,qBAAqB,EACrB,eAAe,EAElB,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,sBAAsB,GAAG;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,OAAO,CAAC;IACzB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,cAAc,EAAE,OAAO,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,uBAAuB,EAAE,OAAO,CAAC;IACjC,gBAAgB,EAAE,OAAO,CAAC;IAC1B,uBAAuB,EAAE,OAAO,CAAC;IACjC,aAAa,EAAE,OAAO,CAAC;IACvB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,0BAA0B,EAAE,OAAO,CAAC;IACpC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,0BAA0B,EAAE,OAAO,CAAC;IACpC,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAIF,QAAA,MAAM,sBAAsB,qDAKzB,IAAI,CACH,sBAAsB,EACtB,YAAY,GAAG,eAAe,GAAG,QAAQ,GAAG,UAAU,CACzD,KAAG,OAEoE,CAAC;AAEzE,QAAA,MAAM,iBAAiB,oIAWpB,IAAI,CACH,sBAAsB,EACpB,YAAY,GACZ,QAAQ,GACR,YAAY,GACZ,QAAQ,GACR,QAAQ,GACR,cAAc,GACd,qBAAqB,GACrB,cAAc,GACd,qBAAqB,GACrB,MAAM,CACX,KAAG,OAYuC,CAAC;AAgF5C,KAAK,aAAa,GAAG;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,QAAQ,GAAG,YAAY,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,QAAA,MAAM,wBAAwB,UACnB,WAAW,oBACA,OAAO,KAC1B,WAQY,CAAC;AAgXhB,KAAK,mBAAmB,GAAG,QAAQ,GAAG,YAAY,GAAG,WAAW,CAAC;AAkDjE,QAAA,MAAM,6BAA6B,eACnB,eAAe,EAAE,KAC9B,MAAM,EAWR,CAAC;AAIF,QAAA,MAAM,sBAAsB,WAAY,WAAW,EAAE,KAAG,MAAM,EAM7D,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,EAAE,aAAa,EAAE,CAAC;IACjC,MAAM,EAAE,WAAW,EAAE,CAAC;CACzB,CAAC;AAEF,eAAO,MAAM,eAAe,QACnB,MAAM,EAAE,QACP,mBAAmB,aACd,MAAM,eACJ,MAAM,eACN,MAAM,gBACN,OAAO,WACZ,OAAO,eACH,OAAO,qBACD,OAAO,eACb,OAAO,eACN,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,KACtC,OAAO,CAAC,qBAAqB,CAuJ/B,CAAC;AAsDF,QAAA,MAAM,8BAA8B,UAAW,OAAO,KAAG,OACmB,CAAC;AAE7E;;;;;GAKG;AACH,QAAA,MAAM,0BAA0B,cACjB,MAAM,cACL,MAAM,EAAE,eACP,MAAM,KACpB,OAAO,CAAC,MAAM,GAAG,IAAI,CAoCvB,CAAC;AAaF,QAAA,MAAM,kBAAkB,cACT,MAAM,SACV,MAAM,EAAE,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,SACxB,OAAO,aACH,OAAO,eACL,MAAM,KACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA6DhC,CAAC;AAEF,eAAO,MAAM,iCAAiC,SAAU,MAAM,KAAG,MAGzC,CAAC;AAQzB;;;;;;GAMG;AACH,QAAA,MAAM,qCAAqC,UAAW,OAAO,KAAG,OAElB,CAAC;AAE/C,QAAA,MAAM,0BAA0B,UAAW,OAAO,KAAG,OAEP,CAAC;AA8E/C,QAAA,MAAM,2BAA2B,gBAChB,MAAM,KACpB,OAAO,CAAC,wBAAwB,EAAE,CAOpC,CAAC;AAEF,QAAA,MAAM,yBAAyB,cAChB,MAAM,SACV,MAAM,EAAE,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,SACxB,OAAO,aACH,OAAO,eACL,MAAM,KACpB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA8EhC,CAAC;AAgDF,QAAA,MAAM,gBAAgB,cACP,MAAM,OACZ,MAAM,EAAE,YACH,OAAO,eACJ,MAAM,KACpB,OAAO,CAAC,MAAM,CAqDhB,CAAC;AAEF,QAAA,MAAM,gBAAgB,gBACL,MAAM,KACpB,OAAO,CAAC,WAAW,EAAE,CAOvB,CAAC;AAcF,QAAA,MAAM,cAAc,cACL,MAAM,SACV,MAAM,EAAE,WACN,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,SACxB,OAAO,eACD,MAAM,aACT,OAAO,KAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgDhC,CAAC;AAgLF,eAAO,MAAM,eAAe,YACf,sBAAsB,KAChC,OAAO,CAAC,IAAI,CAytBd,CAAC;AAiDF,QAAA,MAAM,sBAAsB,WAChB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,SACvB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC9B;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,0BAA0B,CAAA;CAoBxD,CAAC;AAyaF,QAAA,MAAM,sBAAsB,mBACR,MAAM,EAAE,eACX,MAAM,oBACF,CAAC,eAAe,GAAG;IAAE,aAAa,EAAE,OAAO,CAAA;CAAE,CAAC,EAAE,KAClE,OAAO,CAAC,MAAM,EAAE,CAId,CAAC;AAEN,QAAA,MAAM,oBAAoB,mBACN,MAAM,EAAE,eACX,MAAM,oBACF,CAAC,eAAe,GAAG;IAAE,aAAa,EAAE,OAAO,CAAA;CAAE,CAAC,EAAE,KAClE,OAAO,CAAC,MAAM,EAAE,CAId,CAAC;AAIN,QAAA,MAAM,0BAA0B,eAChB,OAAO,kBACH,MAAM,EAAE,KACzB,OAAoD,CAAC;AAExD,eAAO,MAAM,aAAa,YACb,sBAAsB,KAChC,OAAO,CAAC,IAAI,CAq2Bd,CAAC;AAEF,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8BvB,CAAC"}
|
|
@@ -341,6 +341,13 @@ const extractAppSlugsFromDashboards = (dashboards) => [
|
|
|
341
341
|
: acc;
|
|
342
342
|
}, []))),
|
|
343
343
|
];
|
|
344
|
+
// A virtual view's slug is the explore name charts store in tableName, so
|
|
345
|
+
// these names double as virtual view slug candidates.
|
|
346
|
+
const extractChartTableNames = (charts) => [
|
|
347
|
+
...new Set(charts
|
|
348
|
+
.map((chart) => chart.tableName)
|
|
349
|
+
.filter((tableName) => !!tableName)),
|
|
350
|
+
];
|
|
344
351
|
const downloadContent = async (ids, type, projectId, projectName, customPath, languageMap = false, nested = false, skipSpaces = false, stripPivotSeries = false, rootSpaces = false, onProgress) => {
|
|
345
352
|
const contentFilters = parseContentFilters(ids);
|
|
346
353
|
const folderScheme = nested ? 'nested' : 'flat';
|
|
@@ -348,6 +355,7 @@ const downloadContent = async (ids, type, projectId, projectName, customPath, la
|
|
|
348
355
|
let offset = 0;
|
|
349
356
|
let total = 0;
|
|
350
357
|
let chartSlugs = [];
|
|
358
|
+
let chartTableNames = [];
|
|
351
359
|
let appSlugs = [];
|
|
352
360
|
let allMetadataEntries = [];
|
|
353
361
|
let allSpaces = [];
|
|
@@ -432,6 +440,10 @@ const downloadContent = async (ids, type, projectId, projectName, customPath, la
|
|
|
432
440
|
});
|
|
433
441
|
allMetadataEntries = [...allMetadataEntries, ...entries];
|
|
434
442
|
}
|
|
443
|
+
chartTableNames = [
|
|
444
|
+
...chartTableNames,
|
|
445
|
+
...extractChartTableNames(results.charts),
|
|
446
|
+
];
|
|
435
447
|
}
|
|
436
448
|
// Accumulate space metadata from each page
|
|
437
449
|
if ('spaces' in results && results.spaces) {
|
|
@@ -450,6 +462,7 @@ const downloadContent = async (ids, type, projectId, projectName, customPath, la
|
|
|
450
462
|
return {
|
|
451
463
|
total,
|
|
452
464
|
chartSlugs: [...new Set(chartSlugs)],
|
|
465
|
+
chartTableNames: [...new Set(chartTableNames)],
|
|
453
466
|
appSlugs: [...new Set(appSlugs)],
|
|
454
467
|
metadataEntries: allMetadataEntries,
|
|
455
468
|
spaces: allSpaces,
|
|
@@ -476,6 +489,44 @@ const downloadVirtualViews = async (projectId, slugs, customPath) => {
|
|
|
476
489
|
results.missingSlugs.forEach((slug) => globalState_1.default.log(styles.warning(`Virtual view "${slug}" was not found`)));
|
|
477
490
|
return results.virtualViews.length;
|
|
478
491
|
};
|
|
492
|
+
// This download is implicit (derived from a dashboard's charts, not asked for
|
|
493
|
+
// by the user), so an older server without the endpoint (404) or a user
|
|
494
|
+
// without content-as-code access (403) must not fail the run.
|
|
495
|
+
const isVirtualViewsUnavailableError = (error) => error instanceof common_1.LightdashError && [403, 404].includes(error.statusCode);
|
|
496
|
+
/**
|
|
497
|
+
* Downloads the virtual views backing a dashboard's charts. The candidates are
|
|
498
|
+
* chart table names, so ones the server reports as missing are just regular
|
|
499
|
+
* dbt explores — expected, never warned. Returns null when virtual views are
|
|
500
|
+
* unavailable on the server.
|
|
501
|
+
*/
|
|
502
|
+
const downloadLinkedVirtualViews = async (projectId, tableNames, customPath) => {
|
|
503
|
+
const query = new URLSearchParams(tableNames.map((name) => ['slugs', name])).toString();
|
|
504
|
+
let results;
|
|
505
|
+
try {
|
|
506
|
+
results = await (0, apiClient_1.lightdashApi)({
|
|
507
|
+
method: 'GET',
|
|
508
|
+
url: `/api/v1/projects/${projectId}/code/virtualViews?${query}`,
|
|
509
|
+
body: undefined,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
catch (error) {
|
|
513
|
+
if (isVirtualViewsUnavailableError(error)) {
|
|
514
|
+
globalState_1.default.debug(`Could not download linked virtual views: ${(0, common_1.getErrorMessage)(error)}`);
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
throw error;
|
|
518
|
+
}
|
|
519
|
+
if (results.virtualViews.length > 0) {
|
|
520
|
+
await (0, resource_1.writeCodeResourceDocuments)({
|
|
521
|
+
definition: projectResources_1.VIRTUAL_VIEW_CODE_RESOURCE,
|
|
522
|
+
basePath: (0, contentAsCodePaths_1.getDownloadFolder)(customPath),
|
|
523
|
+
documents: results.virtualViews,
|
|
524
|
+
pruneOtherDocuments: false,
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
results.skipped.forEach(({ slug, reason }) => globalState_1.default.log(styles.warning(`Skipped virtual view "${slug}": ${reason}`)));
|
|
528
|
+
return results.virtualViews.length;
|
|
529
|
+
};
|
|
479
530
|
const readVirtualViewFiles = async (customPath) => {
|
|
480
531
|
const result = await (0, resource_1.readCodeResourceFiles)({
|
|
481
532
|
definition: projectResources_1.VIRTUAL_VIEW_CODE_RESOURCE,
|
|
@@ -1070,7 +1121,7 @@ const downloadHandler = async (options) => {
|
|
|
1070
1121
|
!options.skipCharts) {
|
|
1071
1122
|
output.startItem('Linked charts');
|
|
1072
1123
|
output.updateActive(`${chartSlugs.length} dashboard dependencies`);
|
|
1073
|
-
const { total: regularCharts, metadataEntries: linkedChartMeta, } = await (0, exports.downloadContent)(chartSlugs, 'charts', projectId, projectName, options.path, options.languageMap, options.nested, skipEmbeddedSpaces, options.stripPivotSeries, options.rootSpaces, output.updateActive);
|
|
1124
|
+
const { total: regularCharts, chartTableNames: linkedChartTableNames, metadataEntries: linkedChartMeta, } = await (0, exports.downloadContent)(chartSlugs, 'charts', projectId, projectName, options.path, options.languageMap, options.nested, skipEmbeddedSpaces, options.stripPivotSeries, options.rootSpaces, output.updateActive);
|
|
1074
1125
|
allMetadataEntries = [
|
|
1075
1126
|
...allMetadataEntries,
|
|
1076
1127
|
...linkedChartMeta,
|
|
@@ -1081,6 +1132,20 @@ const downloadHandler = async (options) => {
|
|
|
1081
1132
|
...linkedSqlMeta,
|
|
1082
1133
|
];
|
|
1083
1134
|
output.completeItem(`${regularCharts + sqlCharts} downloaded`);
|
|
1135
|
+
// Virtual views the linked charts are built on. Skipped
|
|
1136
|
+
// when a broader step already downloaded every one.
|
|
1137
|
+
if (linkedChartTableNames.length > 0 &&
|
|
1138
|
+
!includeAllOptionalContent &&
|
|
1139
|
+
!options.includeVirtualViews) {
|
|
1140
|
+
output.startItem('Linked virtual views');
|
|
1141
|
+
const linkedVirtualViews = await downloadLinkedVirtualViews(projectId, linkedChartTableNames, options.path);
|
|
1142
|
+
if (linkedVirtualViews === null) {
|
|
1143
|
+
output.completeItem('not available on this server', 'warning');
|
|
1144
|
+
}
|
|
1145
|
+
else {
|
|
1146
|
+
output.completeItem(`${linkedVirtualViews} downloaded`);
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1084
1149
|
}
|
|
1085
1150
|
// Consumed after the explicit apps step (see cappedAppSlugs).
|
|
1086
1151
|
dashboardAppSlugs = appSlugs;
|
|
@@ -2156,14 +2221,17 @@ const uploadHandler = async (options) => {
|
|
|
2156
2221
|
exports.uploadHandler = uploadHandler;
|
|
2157
2222
|
exports.testHelpers = {
|
|
2158
2223
|
assertUniqueSpacePaths: spacesAsCode_1.assertUniqueSpacePaths,
|
|
2224
|
+
downloadLinkedVirtualViews,
|
|
2159
2225
|
downloadSpaces: spacesAsCode_1.downloadSpaces,
|
|
2160
2226
|
extractAppSlugsFromDashboards,
|
|
2227
|
+
extractChartTableNames,
|
|
2161
2228
|
getFlatSpaceFileNames: spacesAsCode_1.getFlatSpaceFileNames,
|
|
2162
2229
|
getDashboardAppSlugs,
|
|
2163
2230
|
getDashboardChartSlugs,
|
|
2164
2231
|
hasContentFilters,
|
|
2165
2232
|
isAiAgentsUnavailableError,
|
|
2166
2233
|
isExternalConnectionsUnavailableError,
|
|
2234
|
+
isVirtualViewsUnavailableError,
|
|
2167
2235
|
downloadAiAgents,
|
|
2168
2236
|
isFilteredWithNoDashboards,
|
|
2169
2237
|
readAiAgentFiles,
|
|
@@ -38,7 +38,7 @@ vitest_1.vi.mock('./dbt/apiClient', async (importOriginal) => ({
|
|
|
38
38
|
getContentAsCodeUploadPermissions: vitest_1.vi.fn(),
|
|
39
39
|
lightdashApi: vitest_1.vi.fn(),
|
|
40
40
|
}));
|
|
41
|
-
const { assertUniqueSpacePaths, downloadSpaces, getDashboardAppSlugs, getDashboardChartSlugs, getFlatSpaceFileNames, hasContentFilters, isAiAgentsUnavailableError, isExternalConnectionsUnavailableError, downloadAiAgents, isFilteredWithNoDashboards, readAiAgentFiles, readSpaceFiles, readSpaceNames, sanitizeChartForDownload, shouldFallBackToEmbeddedSpaces, shouldDownloadAiAgents, summarizeUploadChanges, upsertAiAgents, upsertExternalConnections, upsertSpaces, upsertVirtualViews, validateSpaceIdentity, writeSpaceFiles, } = download_1.testHelpers;
|
|
41
|
+
const { assertUniqueSpacePaths, downloadLinkedVirtualViews, downloadSpaces, extractChartTableNames, getDashboardAppSlugs, getDashboardChartSlugs, getFlatSpaceFileNames, hasContentFilters, isAiAgentsUnavailableError, isExternalConnectionsUnavailableError, isVirtualViewsUnavailableError, downloadAiAgents, isFilteredWithNoDashboards, readAiAgentFiles, readSpaceFiles, readSpaceNames, sanitizeChartForDownload, shouldFallBackToEmbeddedSpaces, shouldDownloadAiAgents, summarizeUploadChanges, upsertAiAgents, upsertExternalConnections, upsertSpaces, upsertVirtualViews, validateSpaceIdentity, writeSpaceFiles, } = download_1.testHelpers;
|
|
42
42
|
const makeDownloadHandlerOptions = (overrides = {}) => ({
|
|
43
43
|
verbose: false,
|
|
44
44
|
charts: [],
|
|
@@ -327,6 +327,22 @@ describe('extractAppSlugsFromDashboards', () => {
|
|
|
327
327
|
])).toEqual(['one']);
|
|
328
328
|
});
|
|
329
329
|
});
|
|
330
|
+
describe('extractChartTableNames', () => {
|
|
331
|
+
const chart = (tableName) => ({
|
|
332
|
+
slug: 'a-chart',
|
|
333
|
+
tableName,
|
|
334
|
+
});
|
|
335
|
+
it('dedupes table names across charts', () => {
|
|
336
|
+
expect(extractChartTableNames([
|
|
337
|
+
chart('orders'),
|
|
338
|
+
chart('customers'),
|
|
339
|
+
chart('orders'),
|
|
340
|
+
])).toEqual(['orders', 'customers']);
|
|
341
|
+
});
|
|
342
|
+
it('drops missing table names (e.g. SQL charts)', () => {
|
|
343
|
+
expect(extractChartTableNames([chart(undefined), chart(''), chart('one')])).toEqual(['one']);
|
|
344
|
+
});
|
|
345
|
+
});
|
|
330
346
|
describe('getDashboardAppSlugs', () => {
|
|
331
347
|
let tmpDir;
|
|
332
348
|
beforeEach(async () => {
|
|
@@ -425,6 +441,85 @@ describe('sanitizeChartForDownload', () => {
|
|
|
425
441
|
});
|
|
426
442
|
});
|
|
427
443
|
});
|
|
444
|
+
describe('downloadLinkedVirtualViews', () => {
|
|
445
|
+
let tmpDir;
|
|
446
|
+
const virtualViewDocument = (slug) => ({
|
|
447
|
+
contentType: common_1.ContentAsCodeType.VIRTUAL_VIEW,
|
|
448
|
+
version: 1,
|
|
449
|
+
slug,
|
|
450
|
+
name: slug,
|
|
451
|
+
sql: 'SELECT 1 AS order_id',
|
|
452
|
+
columns: [{ reference: 'order_id', type: 'number' }],
|
|
453
|
+
parameters: null,
|
|
454
|
+
});
|
|
455
|
+
beforeEach(async () => {
|
|
456
|
+
vitest_1.vi.mocked(apiClient_1.lightdashApi).mockReset();
|
|
457
|
+
tmpDir = await fs_1.promises.mkdtemp(path.join(os.tmpdir(), 'linked-virtual-views-test-'));
|
|
458
|
+
});
|
|
459
|
+
afterEach(async () => {
|
|
460
|
+
vitest_1.vi.restoreAllMocks();
|
|
461
|
+
await fs_1.promises.rm(tmpDir, { recursive: true, force: true });
|
|
462
|
+
});
|
|
463
|
+
it('writes matched virtual views and stays silent on regular dbt table names', async () => {
|
|
464
|
+
const logSpy = vitest_1.vi
|
|
465
|
+
.spyOn(globalState_1.default, 'log')
|
|
466
|
+
.mockImplementation(() => undefined);
|
|
467
|
+
vitest_1.vi.mocked(apiClient_1.lightdashApi).mockResolvedValueOnce({
|
|
468
|
+
virtualViews: [virtualViewDocument('my_virtual_view')],
|
|
469
|
+
skipped: [],
|
|
470
|
+
missingSlugs: ['orders', 'customers'],
|
|
471
|
+
});
|
|
472
|
+
const count = await downloadLinkedVirtualViews('project-uuid', ['my_virtual_view', 'orders', 'customers'], tmpDir);
|
|
473
|
+
expect(count).toBe(1);
|
|
474
|
+
expect(apiClient_1.lightdashApi).toHaveBeenCalledExactlyOnceWith(expect.objectContaining({
|
|
475
|
+
url: '/api/v1/projects/project-uuid/code/virtualViews?slugs=my_virtual_view&slugs=orders&slugs=customers',
|
|
476
|
+
}));
|
|
477
|
+
await expect(fs_1.promises.readFile(path.join(tmpDir, 'virtual-views', 'my_virtual_view.yml'), 'utf-8')).resolves.toContain('slug: my_virtual_view');
|
|
478
|
+
expect(logSpy).not.toHaveBeenCalled();
|
|
479
|
+
});
|
|
480
|
+
it('warns about skipped virtual views', async () => {
|
|
481
|
+
const logSpy = vitest_1.vi
|
|
482
|
+
.spyOn(globalState_1.default, 'log')
|
|
483
|
+
.mockImplementation(() => undefined);
|
|
484
|
+
vitest_1.vi.mocked(apiClient_1.lightdashApi).mockResolvedValueOnce({
|
|
485
|
+
virtualViews: [],
|
|
486
|
+
skipped: [{ slug: 'broken_view', reason: 'malformed' }],
|
|
487
|
+
missingSlugs: [],
|
|
488
|
+
});
|
|
489
|
+
await expect(downloadLinkedVirtualViews('project-uuid', ['broken_view'], tmpDir)).resolves.toBe(0);
|
|
490
|
+
expect(logSpy).toHaveBeenCalledExactlyOnceWith(expect.stringContaining('Skipped virtual view "broken_view"'));
|
|
491
|
+
});
|
|
492
|
+
it('returns null when the server has no virtual views endpoint', async () => {
|
|
493
|
+
vitest_1.vi.mocked(apiClient_1.lightdashApi).mockRejectedValueOnce(new common_1.LightdashError({
|
|
494
|
+
message: 'Not found',
|
|
495
|
+
name: 'NotFoundError',
|
|
496
|
+
statusCode: 404,
|
|
497
|
+
data: {},
|
|
498
|
+
}));
|
|
499
|
+
await expect(downloadLinkedVirtualViews('project-uuid', ['orders'], tmpDir)).resolves.toBeNull();
|
|
500
|
+
});
|
|
501
|
+
it('rethrows unexpected errors', async () => {
|
|
502
|
+
const serverError = new common_1.LightdashError({
|
|
503
|
+
message: 'Boom',
|
|
504
|
+
name: 'UnexpectedServerError',
|
|
505
|
+
statusCode: 500,
|
|
506
|
+
data: {},
|
|
507
|
+
});
|
|
508
|
+
vitest_1.vi.mocked(apiClient_1.lightdashApi).mockRejectedValueOnce(serverError);
|
|
509
|
+
await expect(downloadLinkedVirtualViews('project-uuid', ['orders'], tmpDir)).rejects.toBe(serverError);
|
|
510
|
+
});
|
|
511
|
+
it('classifies permission and missing-route errors as unavailable', () => {
|
|
512
|
+
[403, 404].forEach((statusCode) => {
|
|
513
|
+
expect(isVirtualViewsUnavailableError(new common_1.LightdashError({
|
|
514
|
+
message: 'unavailable',
|
|
515
|
+
name: 'TestError',
|
|
516
|
+
statusCode,
|
|
517
|
+
data: {},
|
|
518
|
+
}))).toBe(true);
|
|
519
|
+
});
|
|
520
|
+
expect(isVirtualViewsUnavailableError(new Error('nope'))).toBe(false);
|
|
521
|
+
});
|
|
522
|
+
});
|
|
428
523
|
describe('upsertVirtualViews', () => {
|
|
429
524
|
let tmpDir;
|
|
430
525
|
const virtualView = (slug) => `columns:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightdash/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.69.1",
|
|
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.
|
|
45
|
-
"@lightdash/warehouses": "1.
|
|
44
|
+
"@lightdash/common": "1.69.1",
|
|
45
|
+
"@lightdash/warehouses": "1.69.1"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/inquirer": "8.2.4",
|