@lightdash/cli 0.3465.1 → 0.3467.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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/handlers/apps/appsDownload.d.ts +2 -0
- package/dist/handlers/apps/appsDownload.d.ts.map +1 -1
- package/dist/handlers/apps/appsDownload.js +23 -3
- package/dist/handlers/apps/appsDownload.test.js +44 -0
- package/dist/handlers/compile.d.ts +2 -0
- package/dist/handlers/compile.d.ts.map +1 -1
- package/dist/handlers/compile.js +20 -3
- package/dist/handlers/compileWarehouseColumnValidation.test.js +56 -2
- package/dist/handlers/deploy.d.ts +1 -0
- package/dist/handlers/deploy.d.ts.map +1 -1
- package/dist/handlers/deploy.js +4 -3
- package/dist/handlers/download.d.ts +1 -0
- package/dist/handlers/download.d.ts.map +1 -1
- package/dist/handlers/download.js +19 -14
- package/dist/handlers/download.test.js +27 -1
- package/dist/handlers/preview.d.ts +1 -0
- package/dist/handlers/preview.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/vendor/template/references/chart-references.md +9 -0
- package/dist/vendor/template/references/dashboard-blueprint.md +100 -0
- package/dist/vendor/template/skill.md +4 -0
- package/package.json +3 -3
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type DataAppCodeDownload } from '@lightdash/common';
|
|
2
2
|
export declare const DEFAULT_APPS_LIMIT = 50;
|
|
3
|
+
export declare const getDataAppUuidFromReference: (reference: string) => string;
|
|
4
|
+
export declare const getDataAppUploadFilter: (references: string[], includeApps: boolean) => Set<string> | null;
|
|
3
5
|
/**
|
|
4
6
|
* Resolves the --apps-limit flag. Commander passes the raw string (or
|
|
5
7
|
* undefined when the flag was not given, so an explicit flag is
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"appsDownload.d.ts","sourceRoot":"","sources":["../../../src/handlers/apps/appsDownload.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,KAAK,mBAAmB,EAC3B,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"appsDownload.d.ts","sourceRoot":"","sources":["../../../src/handlers/apps/appsDownload.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,KAAK,mBAAmB,EAC3B,MAAM,mBAAmB,CAAC;AAG3B,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,eAAO,MAAM,2BAA2B,GAAI,WAAW,MAAM,KAAG,MAoB/D,CAAC;AAEF,eAAO,MAAM,sBAAsB,GAC/B,YAAY,MAAM,EAAE,EACpB,aAAa,OAAO,KACrB,GAAG,CAAC,MAAM,CAAC,GAAG,IAC4D,CAAC;AAE9E;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GACzB,UAAU,MAAM,GAAG,SAAS,EAC5B,aAAa,OAAO,KACrB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;CAejD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,kCAAkC,GAAI,KAAK,OAAO,KAAG,OACP,CAAC;AAE5D,MAAM,MAAM,qBAAqB,GAC3B;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEpD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS;IAC1C,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB,KAAG,qBASH,CAAC;AAEF,eAAO,MAAM,aAAa,GACtB,aAAa,MAAM,EAAE,EACrB,OAAO,MAAM,KACd;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAG7C,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,0BAA0B,GACnC,SAAS,MAAM,EACf,MAAM,mBAAmB,KAC1B,mBAOF,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,uBAAuB,GAC7B;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GACjC,KAAK,OAAO,KACb,uBAoBF,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,MAAM;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACvB,KAAG,MAC0O,CAAC;AAE/O;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAG,MAGpB,CAAC;AAEpD;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAC7B,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAChC,OAKF,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,GAC1B,qBAAqB,MAAM,EAC3B,mBAAmB,MAAM,EACzB,WAAW,OAAO,KACnB,SAAS,GAAG,oBAKd,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC5B,cAAc,MAAM,EACpB,OAAO,MAAM,EACb,UAAU,kBAAkB,EAAE,EAC9B,SAAS,MAAM,EACf,sBAAsB,MAAM,KAC7B;IAAE,EAAE,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAiBxD,CAAC"}
|
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.appsDownloadSummary = exports.classifyAppUpload = exports.shouldWarnAllSkipped = exports.computeUpsertedTotal = exports.manifestRetargetHint = exports.classifyAppDownloadError = exports.ensureDownloadedAppContext = exports.capListedApps = exports.selectAppsToDownload = exports.shouldFallBackToSpaceScopedListing = exports.resolveAppsLimit = exports.DEFAULT_APPS_LIMIT = void 0;
|
|
3
|
+
exports.appsDownloadSummary = exports.classifyAppUpload = exports.shouldWarnAllSkipped = exports.computeUpsertedTotal = exports.manifestRetargetHint = exports.classifyAppDownloadError = exports.ensureDownloadedAppContext = exports.capListedApps = exports.selectAppsToDownload = exports.shouldFallBackToSpaceScopedListing = exports.resolveAppsLimit = exports.getDataAppUploadFilter = exports.getDataAppUuidFromReference = exports.DEFAULT_APPS_LIMIT = void 0;
|
|
4
4
|
const common_1 = require("@lightdash/common");
|
|
5
|
+
const uuid_1 = require("uuid");
|
|
5
6
|
exports.DEFAULT_APPS_LIMIT = 50;
|
|
7
|
+
const getDataAppUuidFromReference = (reference) => {
|
|
8
|
+
let url;
|
|
9
|
+
try {
|
|
10
|
+
url = new URL(reference);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return reference;
|
|
14
|
+
}
|
|
15
|
+
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
|
16
|
+
return reference;
|
|
17
|
+
}
|
|
18
|
+
const pathSegments = url.pathname.split('/').filter(Boolean);
|
|
19
|
+
const appSegmentIndex = pathSegments.findIndex((segment, index) => (segment === 'apps' || segment === 'app') &&
|
|
20
|
+
(0, uuid_1.validate)(pathSegments[index + 1]));
|
|
21
|
+
return appSegmentIndex >= 0 ? pathSegments[appSegmentIndex + 1] : reference;
|
|
22
|
+
};
|
|
23
|
+
exports.getDataAppUuidFromReference = getDataAppUuidFromReference;
|
|
24
|
+
const getDataAppUploadFilter = (references, includeApps) => includeApps ? null : new Set(references.map(exports.getDataAppUuidFromReference));
|
|
25
|
+
exports.getDataAppUploadFilter = getDataAppUploadFilter;
|
|
6
26
|
/**
|
|
7
27
|
* Resolves the --apps-limit flag. Commander passes the raw string (or
|
|
8
28
|
* undefined when the flag was not given, so an explicit flag is
|
|
@@ -20,7 +40,7 @@ const resolveAppsLimit = (rawLimit, includeApps) => {
|
|
|
20
40
|
limit: parseInt(rawLimit, 10),
|
|
21
41
|
noEffectWarning: includeApps
|
|
22
42
|
? null
|
|
23
|
-
: '--apps-limit only applies to --include-apps; explicit --apps
|
|
43
|
+
: '--apps-limit only applies to --include-apps; explicit --apps references are never capped.',
|
|
24
44
|
};
|
|
25
45
|
};
|
|
26
46
|
exports.resolveAppsLimit = resolveAppsLimit;
|
|
@@ -38,7 +58,7 @@ exports.shouldFallBackToSpaceScopedListing = shouldFallBackToSpaceScopedListing;
|
|
|
38
58
|
* the space-scoped content API on servers that predate it.
|
|
39
59
|
*/
|
|
40
60
|
const selectAppsToDownload = (request) => {
|
|
41
|
-
const explicitUuids = request.apps ?? [];
|
|
61
|
+
const explicitUuids = (request.apps ?? []).map(exports.getDataAppUuidFromReference);
|
|
42
62
|
if (request.includeApps) {
|
|
43
63
|
return { mode: 'list-all', extraAppUuids: explicitUuids };
|
|
44
64
|
}
|
|
@@ -2,6 +2,40 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const common_1 = require("@lightdash/common");
|
|
4
4
|
const appsDownload_1 = require("./appsDownload");
|
|
5
|
+
describe('getDataAppUuidFromReference', () => {
|
|
6
|
+
const appUuid = 'd3afc44c-6f0f-4d9f-a267-fb739efa31dd';
|
|
7
|
+
const projectUuid = 'd4c8dfd2-98c0-4eb2-8395-d924aee62611';
|
|
8
|
+
it('keeps a bare app UUID unchanged', () => {
|
|
9
|
+
expect((0, appsDownload_1.getDataAppUuidFromReference)(appUuid)).toBe(appUuid);
|
|
10
|
+
});
|
|
11
|
+
it.each([
|
|
12
|
+
`https://app.lightdash.cloud/projects/${projectUuid}/apps/${appUuid}`,
|
|
13
|
+
`https://app.lightdash.cloud/projects/${projectUuid}/apps/${appUuid}/view`,
|
|
14
|
+
`https://app.lightdash.cloud/projects/${projectUuid}/apps/${appUuid}/versions/2/view?state=filters#preview`,
|
|
15
|
+
`https://app.lightdash.cloud/embed/${projectUuid}/app/${appUuid}`,
|
|
16
|
+
])('extracts the app UUID from %s', (url) => {
|
|
17
|
+
expect((0, appsDownload_1.getDataAppUuidFromReference)(url)).toBe(appUuid);
|
|
18
|
+
});
|
|
19
|
+
it.each([
|
|
20
|
+
'my-app',
|
|
21
|
+
`https://app.lightdash.cloud/${appUuid}`,
|
|
22
|
+
`https://app.lightdash.cloud/projects/${projectUuid}/apps/generate`,
|
|
23
|
+
`ftp://app.lightdash.cloud/projects/${projectUuid}/apps/${appUuid}`,
|
|
24
|
+
])('leaves unsupported references unchanged: %s', (reference) => {
|
|
25
|
+
expect((0, appsDownload_1.getDataAppUuidFromReference)(reference)).toBe(reference);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('getDataAppUploadFilter', () => {
|
|
29
|
+
const appUuid = 'd3afc44c-6f0f-4d9f-a267-fb739efa31dd';
|
|
30
|
+
it('matches app manifests by UUID when a URL is passed', () => {
|
|
31
|
+
expect((0, appsDownload_1.getDataAppUploadFilter)([
|
|
32
|
+
`https://app.lightdash.cloud/projects/project-uuid/apps/${appUuid}/view`,
|
|
33
|
+
], false)).toEqual(new Set([appUuid]));
|
|
34
|
+
});
|
|
35
|
+
it('does not filter app folders when --include-apps is passed', () => {
|
|
36
|
+
expect((0, appsDownload_1.getDataAppUploadFilter)([appUuid], true)).toBeNull();
|
|
37
|
+
});
|
|
38
|
+
});
|
|
5
39
|
const makeDownload = () => ({
|
|
6
40
|
manifest: {
|
|
7
41
|
codeVersion: 1,
|
|
@@ -43,6 +77,16 @@ describe('selectAppsToDownload', () => {
|
|
|
43
77
|
it('combines --include-apps with explicitly passed uuids', () => {
|
|
44
78
|
expect((0, appsDownload_1.selectAppsToDownload)({ apps: ['uuid-a'], includeApps: true })).toEqual({ mode: 'list-all', extraAppUuids: ['uuid-a'] });
|
|
45
79
|
});
|
|
80
|
+
it('normalizes explicit app URLs without listing apps', () => {
|
|
81
|
+
expect((0, appsDownload_1.selectAppsToDownload)({
|
|
82
|
+
apps: [
|
|
83
|
+
'https://app.lightdash.cloud/projects/project-uuid/apps/d3afc44c-6f0f-4d9f-a267-fb739efa31dd/view',
|
|
84
|
+
],
|
|
85
|
+
})).toEqual({
|
|
86
|
+
mode: 'explicit',
|
|
87
|
+
appUuids: ['d3afc44c-6f0f-4d9f-a267-fb739efa31dd'],
|
|
88
|
+
});
|
|
89
|
+
});
|
|
46
90
|
it('skips apps when neither flag is passed', () => {
|
|
47
91
|
expect((0, appsDownload_1.selectAppsToDownload)({})).toEqual({ mode: 'none' });
|
|
48
92
|
expect((0, appsDownload_1.selectAppsToDownload)({ apps: [] })).toEqual({ mode: 'none' });
|
|
@@ -12,6 +12,8 @@ export type CompileHandlerOptions = DbtCompileOptions & {
|
|
|
12
12
|
disableTimestampConversion?: boolean;
|
|
13
13
|
validateWarehouseColumns?: boolean;
|
|
14
14
|
};
|
|
15
|
+
export declare const hasBlockingCompileError: (explore: Explore | ExploreError) => boolean;
|
|
16
|
+
export declare const stripWarehouseColumnErrors: (explore: Explore | ExploreError) => Explore | ExploreError;
|
|
15
17
|
export declare const compile: (options: CompileHandlerOptions) => Promise<(Explore | ExploreError)[]>;
|
|
16
18
|
export declare const compileHandler: (originalOptions: CompileHandlerOptions) => Promise<void>;
|
|
17
19
|
//# sourceMappingURL=compile.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/handlers/compile.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,OAAO,EACP,YAAY,EAgBf,MAAM,mBAAmB,CAAC;AAoB3B,OAAO,EAAE,iBAAiB,EAA8B,MAAM,eAAe,CAAC;AAI9E,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,GAAG;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACtC,CAAC;AAwNF,eAAO,MAAM,OAAO,GAAU,SAAS,qBAAqB,wCAgU3D,CAAC;AAEF,eAAO,MAAM,cAAc,GACvB,iBAAiB,qBAAqB,kBAmCzC,CAAC"}
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../../src/handlers/compile.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,OAAO,EACP,YAAY,EAgBf,MAAM,mBAAmB,CAAC;AAoB3B,OAAO,EAAE,iBAAiB,EAA8B,MAAM,eAAe,CAAC;AAI9E,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,GAAG;IACpD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACtC,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAChC,SAAS,OAAO,GAAG,YAAY,KAChC,OAIW,CAAC;AAEf,eAAO,MAAM,0BAA0B,GACnC,SAAS,OAAO,GAAG,YAAY,KAChC,OAAO,GAAG,YAiBZ,CAAC;AAwNF,eAAO,MAAM,OAAO,GAAU,SAAS,qBAAqB,wCAgU3D,CAAC;AAEF,eAAO,MAAM,cAAc,GACvB,iBAAiB,qBAAqB,kBAmCzC,CAAC"}
|
package/dist/handlers/compile.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.compileHandler = exports.compile = void 0;
|
|
3
|
+
exports.compileHandler = exports.compile = exports.stripWarehouseColumnErrors = exports.hasBlockingCompileError = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const common_1 = require("@lightdash/common");
|
|
6
6
|
const warehouses_1 = require("@lightdash/warehouses");
|
|
@@ -18,6 +18,23 @@ const styles = tslib_1.__importStar(require("../styles"));
|
|
|
18
18
|
const compile_1 = require("./dbt/compile");
|
|
19
19
|
const getDbtVersion_1 = require("./dbt/getDbtVersion");
|
|
20
20
|
const getWarehouseClient_1 = tslib_1.__importDefault(require("./dbt/getWarehouseClient"));
|
|
21
|
+
const hasBlockingCompileError = (explore) => (0, common_1.isExploreError)(explore) ||
|
|
22
|
+
explore.warnings?.some((warning) => warning.type === common_1.InlineErrorType.WAREHOUSE_COLUMN_ERROR) === true;
|
|
23
|
+
exports.hasBlockingCompileError = hasBlockingCompileError;
|
|
24
|
+
const stripWarehouseColumnErrors = (explore) => {
|
|
25
|
+
if ((0, common_1.isExploreError)(explore) || explore.warnings === undefined) {
|
|
26
|
+
return explore;
|
|
27
|
+
}
|
|
28
|
+
const { warnings: currentWarnings, ...exploreWithoutWarnings } = explore;
|
|
29
|
+
const warnings = currentWarnings.filter((warning) => warning.type !== common_1.InlineErrorType.WAREHOUSE_COLUMN_ERROR);
|
|
30
|
+
if (warnings.length === currentWarnings.length) {
|
|
31
|
+
return explore;
|
|
32
|
+
}
|
|
33
|
+
return warnings.length > 0
|
|
34
|
+
? { ...exploreWithoutWarnings, warnings }
|
|
35
|
+
: exploreWithoutWarnings;
|
|
36
|
+
};
|
|
37
|
+
exports.stripWarehouseColumnErrors = stripWarehouseColumnErrors;
|
|
21
38
|
const getDisplayableDiagnostics = (explore) => {
|
|
22
39
|
const diagnostics = explore.warnings ?? [];
|
|
23
40
|
const errors = diagnostics.filter((diagnostic) => diagnostic.type === common_1.InlineErrorType.WAREHOUSE_COLUMN_ERROR);
|
|
@@ -253,7 +270,7 @@ const compile = async (options) => {
|
|
|
253
270
|
else {
|
|
254
271
|
globalState_1.default.debug('> Skipping warehouse catalog');
|
|
255
272
|
if (options.validateWarehouseColumns === true) {
|
|
256
|
-
console.error(styles.warning('> Skipping warehouse column validation because
|
|
273
|
+
console.error(styles.warning('> Skipping warehouse column validation because the warehouse catalog is skipped'));
|
|
257
274
|
}
|
|
258
275
|
}
|
|
259
276
|
const validModelsWithTypes = applyMetricFlowMetrics((0, common_1.attachTypesToModels)(validModels, catalog, false,
|
|
@@ -373,7 +390,7 @@ const compileHandler = async (originalOptions) => {
|
|
|
373
390
|
options.skipWarehouseCatalog = projectTypeConfig.skipWarehouseCatalog;
|
|
374
391
|
globalState_1.default.setVerbose(options.verbose);
|
|
375
392
|
const explores = await (0, exports.compile)(options);
|
|
376
|
-
const errorsCount = explores.filter(
|
|
393
|
+
const errorsCount = explores.filter(exports.hasBlockingCompileError).length;
|
|
377
394
|
console.error('');
|
|
378
395
|
if (errorsCount > 0) {
|
|
379
396
|
console.error(styles.error(`Failed to compile project. Found ${errorsCount} error${errorsCount > 1 ? 's' : ''}`));
|
|
@@ -11,9 +11,11 @@ const manifest_1 = require("../dbt/manifest");
|
|
|
11
11
|
const validation_1 = require("../dbt/validation");
|
|
12
12
|
const loader_1 = require("../lightdash/loader");
|
|
13
13
|
const compile_1 = require("./compile");
|
|
14
|
+
const apiClient_1 = require("./dbt/apiClient");
|
|
14
15
|
const compile_2 = require("./dbt/compile");
|
|
15
16
|
const getDbtVersion_1 = require("./dbt/getDbtVersion");
|
|
16
17
|
const getWarehouseClient_1 = tslib_1.__importDefault(require("./dbt/getWarehouseClient"));
|
|
18
|
+
const deploy_1 = require("./deploy");
|
|
17
19
|
vi.mock('../analytics/analytics');
|
|
18
20
|
vi.mock('../config', () => ({
|
|
19
21
|
getConfig: vi.fn().mockResolvedValue({ user: null, context: null }),
|
|
@@ -23,6 +25,10 @@ vi.mock('../dbt/manifest');
|
|
|
23
25
|
vi.mock('../dbt/validation');
|
|
24
26
|
vi.mock('../lightdash/loader');
|
|
25
27
|
vi.mock('./dbt/compile');
|
|
28
|
+
vi.mock('./dbt/apiClient', async (importOriginal) => {
|
|
29
|
+
const original = await importOriginal();
|
|
30
|
+
return { ...original, lightdashApi: vi.fn() };
|
|
31
|
+
});
|
|
26
32
|
vi.mock('./dbt/getDbtVersion');
|
|
27
33
|
vi.mock('./dbt/getWarehouseClient');
|
|
28
34
|
vi.mock('@lightdash/warehouses', async (importOriginal) => {
|
|
@@ -116,7 +122,7 @@ const baseOptions = (projectDir) => ({
|
|
|
116
122
|
warehouseCredentials: false,
|
|
117
123
|
disableTimestampConversion: false,
|
|
118
124
|
});
|
|
119
|
-
const SKIP_WARNING = 'Skipping warehouse column validation because
|
|
125
|
+
const SKIP_WARNING = 'Skipping warehouse column validation because the warehouse catalog is skipped';
|
|
120
126
|
const YML_SKIP_WARNING = 'Skipping warehouse column validation because it is not supported for Lightdash YAML projects';
|
|
121
127
|
const DBT_CLOUD_SKIP_WARNING = 'Skipping warehouse column validation because dbt Cloud CLI cannot run warehouse queries';
|
|
122
128
|
const setupWarehouseClient = () => {
|
|
@@ -268,6 +274,49 @@ describe('compile warehouse column validation', () => {
|
|
|
268
274
|
expect(warningTypes).toEqual([
|
|
269
275
|
common_1.InlineErrorType.WAREHOUSE_COLUMN_ERROR,
|
|
270
276
|
]);
|
|
277
|
+
expect(result.filter(compile_1.hasBlockingCompileError)).toHaveLength(1);
|
|
278
|
+
expect(result
|
|
279
|
+
.map(compile_1.stripWarehouseColumnErrors)
|
|
280
|
+
.flatMap((explore) => (0, common_1.isExploreError)(explore) ? [] : (explore.warnings ?? []))).toEqual([]);
|
|
281
|
+
});
|
|
282
|
+
test('blocks deploy when the warehouse rejects a column', async () => {
|
|
283
|
+
setupWarehouseClient();
|
|
284
|
+
mockProberToAppendWarning(WAREHOUSE_WARNING);
|
|
285
|
+
const explores = await (0, compile_1.compile)({
|
|
286
|
+
...baseOptions(tempDir),
|
|
287
|
+
validateWarehouseColumns: true,
|
|
288
|
+
});
|
|
289
|
+
vi.spyOn(process, 'exit').mockImplementation(() => {
|
|
290
|
+
throw new Error('process.exit');
|
|
291
|
+
});
|
|
292
|
+
await expect((0, deploy_1.deploy)(explores, {
|
|
293
|
+
...baseOptions(tempDir),
|
|
294
|
+
projectUuid: 'projectUuid',
|
|
295
|
+
ignoreErrors: false,
|
|
296
|
+
validateWarehouseColumns: true,
|
|
297
|
+
})).rejects.toThrow('process.exit');
|
|
298
|
+
expect(process.exit).toHaveBeenCalledWith(1);
|
|
299
|
+
});
|
|
300
|
+
test('does not persist warehouse diagnostics when deploy errors are ignored', async () => {
|
|
301
|
+
setupWarehouseClient();
|
|
302
|
+
mockProberToAppendWarning(WAREHOUSE_WARNING);
|
|
303
|
+
const explores = await (0, compile_1.compile)({
|
|
304
|
+
...baseOptions(tempDir),
|
|
305
|
+
validateWarehouseColumns: true,
|
|
306
|
+
});
|
|
307
|
+
await (0, deploy_1.deploy)(explores, {
|
|
308
|
+
...baseOptions(tempDir),
|
|
309
|
+
projectUuid: 'projectUuid',
|
|
310
|
+
ignoreErrors: true,
|
|
311
|
+
validateWarehouseColumns: true,
|
|
312
|
+
});
|
|
313
|
+
const deployRequest = vi
|
|
314
|
+
.mocked(apiClient_1.lightdashApi)
|
|
315
|
+
.mock.calls.find(([request]) => request.url === '/api/v1/projects/projectUuid/explores');
|
|
316
|
+
if (deployRequest === undefined) {
|
|
317
|
+
throw new Error('Expected explores deploy request');
|
|
318
|
+
}
|
|
319
|
+
expect(deployRequest[0].body).not.toContain(common_1.InlineErrorType.WAREHOUSE_COLUMN_ERROR);
|
|
271
320
|
});
|
|
272
321
|
test('keeps generic warnings hidden when partial compilation is disabled', async () => {
|
|
273
322
|
vi.stubEnv('PARTIAL_COMPILATION_ENABLED', 'false');
|
|
@@ -317,7 +366,7 @@ describe('compile warehouse column validation', () => {
|
|
|
317
366
|
});
|
|
318
367
|
setupWarehouseClient();
|
|
319
368
|
mockProberToAppendWarning(WAREHOUSE_WARNING);
|
|
320
|
-
await (0, compile_1.compile)({
|
|
369
|
+
const result = await (0, compile_1.compile)({
|
|
321
370
|
...baseOptions(tempDir),
|
|
322
371
|
validateWarehouseColumns: true,
|
|
323
372
|
});
|
|
@@ -326,6 +375,11 @@ describe('compile warehouse column validation', () => {
|
|
|
326
375
|
expect.stringContaining('Skipped metric "id"'),
|
|
327
376
|
expect.stringContaining('Compiled 1 explores, SUCCESS=0 ERRORS=1'),
|
|
328
377
|
]));
|
|
378
|
+
expect(result
|
|
379
|
+
.map(compile_1.stripWarehouseColumnErrors)
|
|
380
|
+
.flatMap((explore) => (0, common_1.isExploreError)(explore)
|
|
381
|
+
? []
|
|
382
|
+
: (explore.warnings ?? []).map((warning) => warning.type))).toEqual([common_1.InlineErrorType.DUPLICATE_FIELD_NAME]);
|
|
329
383
|
});
|
|
330
384
|
test('renders only the warehouse error when partial compilation is disabled and generic warnings exist', async () => {
|
|
331
385
|
vi.stubEnv('PARTIAL_COMPILATION_ENABLED', 'false');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/handlers/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,wBAAwB,EAGxB,OAAO,EACP,YAAY,
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/handlers/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,wBAAwB,EAGxB,OAAO,EACP,YAAY,EASf,MAAM,mBAAmB,CAAC;AA2B3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AASlD,KAAK,oBAAoB,GAAG,iBAAiB,GAAG;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,wBAAwB,EAAE,OAAO,CAAC;CACrC,CAAC;AAEF,KAAK,UAAU,GAAG,oBAAoB,GAAG;IACrC,WAAW,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC1B,gBAAgB,wBAAwB,CAAC,UAAU,CAAC,GAAG,SAAS,SA0BnE,CAAC;AAiPF,eAAO,MAAM,MAAM,GACf,UAAU,CAAC,OAAO,GAAG,YAAY,CAAC,EAAE,EACpC,SAAS,UAAU,KACpB,OAAO,CAAC,IAAI,CA6Id,CAAC;AAiGF,eAAO,MAAM,aAAa,GAAU,iBAAiB,oBAAoB,kBAmKxE,CAAC"}
|
package/dist/handlers/deploy.js
CHANGED
|
@@ -191,7 +191,7 @@ const deploy = async (explores, options) => {
|
|
|
191
191
|
globalState_1.default.log(styles.warning('No explores found'));
|
|
192
192
|
process.exit(1);
|
|
193
193
|
}
|
|
194
|
-
const errors = explores.filter(
|
|
194
|
+
const errors = explores.filter(compile_1.hasBlockingCompileError).length;
|
|
195
195
|
if (errors > 0) {
|
|
196
196
|
if (options.ignoreErrors) {
|
|
197
197
|
console.error(styles.warning(`\nDeploying project with ${errors} errors\n`));
|
|
@@ -201,6 +201,7 @@ const deploy = async (explores, options) => {
|
|
|
201
201
|
process.exit(1);
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
|
+
const deployableExplores = explores.map(compile_1.stripWarehouseColumnErrors);
|
|
204
205
|
const lightdashProjectConfig = await (0, lightdash_config_1.readAndLoadLightdashProjectConfig)(path_1.default.resolve(options.projectDir), options.projectUuid);
|
|
205
206
|
// These two methods are not critical to the deployment process, so we can ignore errors and show warnings instead
|
|
206
207
|
try {
|
|
@@ -229,11 +230,11 @@ const deploy = async (explores, options) => {
|
|
|
229
230
|
}
|
|
230
231
|
// Use batched deploy if enabled
|
|
231
232
|
if (options.useBatchedDeploy) {
|
|
232
|
-
await deployBatched(
|
|
233
|
+
await deployBatched(deployableExplores, options);
|
|
233
234
|
}
|
|
234
235
|
else {
|
|
235
236
|
const deployStartTime = Date.now();
|
|
236
|
-
const deployPayload = JSON.stringify(
|
|
237
|
+
const deployPayload = JSON.stringify(deployableExplores);
|
|
237
238
|
try {
|
|
238
239
|
const deployResponse = await (0, apiClient_1.lightdashApi)({
|
|
239
240
|
method: 'PUT',
|
|
@@ -62,6 +62,7 @@ export declare const testHelpers: {
|
|
|
62
62
|
getDashboardChartSlugs: (dashboardSlugs: string[], customPath?: string, looseDashboards?: (DashboardAsCode & {
|
|
63
63
|
needsUpdating: boolean;
|
|
64
64
|
})[]) => Promise<string[]>;
|
|
65
|
+
hasUploadFilters: ({ spacesOnly, charts, dashboards, agents, alerts, googleSheets, scheduledDeliveries, virtualViews, apps, }: Pick<DownloadHandlerOptions, "spacesOnly" | "charts" | "dashboards" | "agents" | "alerts" | "googleSheets" | "scheduledDeliveries" | "virtualViews" | "apps">) => boolean;
|
|
65
66
|
readAiAgentFiles: (customPath?: string) => Promise<AgentAsCode[]>;
|
|
66
67
|
readSpaceFiles: (customPath?: string) => Promise<SpaceCodeFile[]>;
|
|
67
68
|
readSpaceNames: (customPath?: string) => Promise<Record<string, string>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/handlers/download.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,WAAW,EAuBX,WAAW,EAGX,eAAe,EAef,KAAK,WAAW,EACnB,MAAM,mBAAmB,CAAC;AAY3B,OAAO,EAIH,KAAK,0BAA0B,EAClC,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/handlers/download.ts"],"names":[],"mappings":"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"}
|
|
@@ -30,6 +30,17 @@ const selectProject_1 = require("./selectProject");
|
|
|
30
30
|
const spacesAsCode_1 = require("./spacesAsCode");
|
|
31
31
|
const shouldDownloadAiAgents = ({ includeAll, includeAgents, agents, appsOnly, }) => appsOnly !== true &&
|
|
32
32
|
(includeAll === true || includeAgents === true || agents.length > 0);
|
|
33
|
+
const hasUploadFilters = ({ spacesOnly, charts, dashboards, agents, alerts, googleSheets, scheduledDeliveries, virtualViews, apps, }) => !spacesOnly &&
|
|
34
|
+
[
|
|
35
|
+
charts,
|
|
36
|
+
dashboards,
|
|
37
|
+
agents,
|
|
38
|
+
alerts,
|
|
39
|
+
googleSheets,
|
|
40
|
+
scheduledDeliveries,
|
|
41
|
+
virtualViews,
|
|
42
|
+
apps ?? [],
|
|
43
|
+
].some((filters) => filters.length > 0);
|
|
33
44
|
/*
|
|
34
45
|
This function is used to parse the content filters.
|
|
35
46
|
It can be slugs, uuids or urls
|
|
@@ -712,7 +723,7 @@ const downloadHandler = async (options) => {
|
|
|
712
723
|
includeApps,
|
|
713
724
|
});
|
|
714
725
|
if (appsOnlySelection.mode === 'none') {
|
|
715
|
-
throw new common_1.ParameterError('Nothing to download: --apps-only requires --apps <
|
|
726
|
+
throw new common_1.ParameterError('Nothing to download: --apps-only requires --apps <appReferences...>, --include-apps, or --include-all.');
|
|
716
727
|
}
|
|
717
728
|
options.skipCharts = true;
|
|
718
729
|
options.skipDashboards = true;
|
|
@@ -745,14 +756,7 @@ const downloadHandler = async (options) => {
|
|
|
745
756
|
if (options.rootSpaces && options.nested) {
|
|
746
757
|
throw new common_1.ParameterError('--root-spaces cannot be combined with --nested');
|
|
747
758
|
}
|
|
748
|
-
const hasFilters =
|
|
749
|
-
(options.charts.length > 0 ||
|
|
750
|
-
options.dashboards.length > 0 ||
|
|
751
|
-
options.agents.length > 0 ||
|
|
752
|
-
options.alerts.length > 0 ||
|
|
753
|
-
options.googleSheets.length > 0 ||
|
|
754
|
-
options.scheduledDeliveries.length > 0 ||
|
|
755
|
-
options.virtualViews.length > 0);
|
|
759
|
+
const hasFilters = hasUploadFilters(options);
|
|
756
760
|
const shouldDownloadSpaces = !isOrganizationDownload && !options.skipSpaces && !hasFilters;
|
|
757
761
|
let skipEmbeddedSpaces = !hasFilters || options.skipSpaces;
|
|
758
762
|
if (shouldDownloadSpaces) {
|
|
@@ -1599,12 +1603,12 @@ const uploadHandler = async (options) => {
|
|
|
1599
1603
|
});
|
|
1600
1604
|
}
|
|
1601
1605
|
}
|
|
1602
|
-
// Upload data apps (enterprise, opt-in via --apps <
|
|
1606
|
+
// Upload data apps (enterprise, opt-in via --apps <references...> or
|
|
1603
1607
|
// --include-apps, fire-and-forget)
|
|
1604
|
-
const
|
|
1608
|
+
const explicitAppReferences = Array.isArray(options.apps)
|
|
1605
1609
|
? options.apps
|
|
1606
1610
|
: [];
|
|
1607
|
-
const shouldUploadApps = options.includeApps === true ||
|
|
1611
|
+
const shouldUploadApps = options.includeApps === true || explicitAppReferences.length > 0;
|
|
1608
1612
|
let appsCreated = 0;
|
|
1609
1613
|
let appsUpdated = 0;
|
|
1610
1614
|
let appsFailed = 0;
|
|
@@ -1617,9 +1621,9 @@ const uploadHandler = async (options) => {
|
|
|
1617
1621
|
}
|
|
1618
1622
|
else if (shouldUploadApps) {
|
|
1619
1623
|
output.startItem('Data apps');
|
|
1620
|
-
// --include-apps uploads every folder on disk; explicit
|
|
1624
|
+
// --include-apps uploads every folder on disk; explicit references
|
|
1621
1625
|
// filter folders by their manifest appUuid
|
|
1622
|
-
const filterUuids = options.includeApps === true
|
|
1626
|
+
const filterUuids = (0, appsDownload_1.getDataAppUploadFilter)(explicitAppReferences, options.includeApps === true);
|
|
1623
1627
|
const baseDir = (0, contentAsCodePaths_1.getDownloadFolder)(options.path);
|
|
1624
1628
|
const appsDir = path.join(baseDir, 'apps');
|
|
1625
1629
|
let appFolderEntries;
|
|
@@ -1856,6 +1860,7 @@ exports.testHelpers = {
|
|
|
1856
1860
|
downloadSpaces: spacesAsCode_1.downloadSpaces,
|
|
1857
1861
|
getFlatSpaceFileNames: spacesAsCode_1.getFlatSpaceFileNames,
|
|
1858
1862
|
getDashboardChartSlugs,
|
|
1863
|
+
hasUploadFilters,
|
|
1859
1864
|
readAiAgentFiles,
|
|
1860
1865
|
readSpaceFiles: spacesAsCode_1.readSpaceFiles,
|
|
1861
1866
|
readSpaceNames: spacesAsCode_1.readSpaceNames,
|
|
@@ -13,7 +13,33 @@ vitest_1.vi.mock('./dbt/apiClient', async (importOriginal) => ({
|
|
|
13
13
|
...(await importOriginal()),
|
|
14
14
|
lightdashApi: vitest_1.vi.fn(),
|
|
15
15
|
}));
|
|
16
|
-
const { assertUniqueSpacePaths, downloadSpaces, getDashboardChartSlugs, getFlatSpaceFileNames, readAiAgentFiles, readSpaceFiles, readSpaceNames, sanitizeChartForDownload, shouldFallBackToEmbeddedSpaces, shouldDownloadAiAgents, summarizeUploadChanges, upsertSpaces, upsertVirtualViews, validateSpaceIdentity, writeSpaceFiles, } = download_1.testHelpers;
|
|
16
|
+
const { assertUniqueSpacePaths, downloadSpaces, getDashboardChartSlugs, getFlatSpaceFileNames, hasUploadFilters, readAiAgentFiles, readSpaceFiles, readSpaceNames, sanitizeChartForDownload, shouldFallBackToEmbeddedSpaces, shouldDownloadAiAgents, summarizeUploadChanges, upsertSpaces, upsertVirtualViews, validateSpaceIdentity, writeSpaceFiles, } = download_1.testHelpers;
|
|
17
|
+
const makeUploadFilterOptions = (overrides = {}) => ({
|
|
18
|
+
spacesOnly: false,
|
|
19
|
+
charts: [],
|
|
20
|
+
dashboards: [],
|
|
21
|
+
agents: [],
|
|
22
|
+
alerts: [],
|
|
23
|
+
googleSheets: [],
|
|
24
|
+
scheduledDeliveries: [],
|
|
25
|
+
virtualViews: [],
|
|
26
|
+
apps: [],
|
|
27
|
+
...overrides,
|
|
28
|
+
});
|
|
29
|
+
describe('hasUploadFilters', () => {
|
|
30
|
+
it('treats explicit apps as a filtered upload', () => {
|
|
31
|
+
expect(hasUploadFilters(makeUploadFilterOptions({ apps: ['app-reference'] }))).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
it('keeps an unfiltered upload unfiltered', () => {
|
|
34
|
+
expect(hasUploadFilters(makeUploadFilterOptions())).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
it('does not treat content selections as filters in spaces-only mode', () => {
|
|
37
|
+
expect(hasUploadFilters(makeUploadFilterOptions({
|
|
38
|
+
spacesOnly: true,
|
|
39
|
+
apps: ['app-reference'],
|
|
40
|
+
}))).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
17
43
|
const makeLooseDashboard = (slug, chartSlugs) => ({
|
|
18
44
|
slug,
|
|
19
45
|
name: slug,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/handlers/preview.ts"],"names":[],"mappings":"AACA,OAAO,EACH,+BAA+B,EAKlC,MAAM,mBAAmB,CAAC;AAgB3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAOlD,KAAK,qBAAqB,GAAG,iBAAiB,GAAG;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,kBAAkB,EAAE,+BAA+B,CAAC;IACpD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0BAA0B,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/handlers/preview.ts"],"names":[],"mappings":"AACA,OAAO,EACH,+BAA+B,EAKlC,MAAM,mBAAmB,CAAC;AAgB3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAOlD,KAAK,qBAAqB,GAAG,iBAAiB,GAAG;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,kBAAkB,EAAE,+BAA+B,CAAC;IACpD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0BAA0B,CAAC,EAAE,OAAO,CAAC;IACrC,wBAAwB,EAAE,OAAO,CAAC;CACrC,CAAC;AAEF,KAAK,yBAAyB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;CACpB,CAAC;AA2GF,eAAO,MAAM,cAAc,GACvB,iBAAiB,qBAAqB,KACvC,OAAO,CAAC,IAAI,CAmSd,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAC5B,iBAAiB,qBAAqB,KACvC,OAAO,CAAC,IAAI,CA8Kd,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC3B,SAAS,yBAAyB,KACnC,OAAO,CAAC,IAAI,CA0Cd,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -63,6 +63,7 @@ function parseDisableTimestampConversionOption(value) {
|
|
|
63
63
|
}
|
|
64
64
|
return value.toLowerCase() === 'true';
|
|
65
65
|
}
|
|
66
|
+
const validateWarehouseColumnsDescription = `Check physical \${TABLE}.column references in dimensions and metrics by executing zero-row queries against the warehouse. Requires warehouse credentials.`;
|
|
66
67
|
function parseProjectArgument(value) {
|
|
67
68
|
if (value === undefined) {
|
|
68
69
|
throw new commander_1.InvalidArgumentError('No project argument provided.');
|
|
@@ -231,6 +232,7 @@ commander_1.program
|
|
|
231
232
|
.option('--no-warehouse-credentials', 'Compile without any warehouse credentials. Skips dbt compile + warehouse catalog')
|
|
232
233
|
.option('--use-dbt-list [true|false]', 'Use `dbt list` instead of `dbt compile` to generate dbt manifest.json', parseUseDbtListOption, true)
|
|
233
234
|
.option('--disable-timestamp-conversion [true|false]', 'Disable timestamp conversion to UTC for Snowflake warehouses. Only use this if your timestamp values are already in UTC.', parseDisableTimestampConversionOption)
|
|
235
|
+
.option('--validate-warehouse-columns', validateWarehouseColumnsDescription, false)
|
|
234
236
|
.action(compile_1.compileHandler);
|
|
235
237
|
commander_1.program
|
|
236
238
|
.command('preview')
|
|
@@ -269,6 +271,7 @@ commander_1.program
|
|
|
269
271
|
.option('--batch-size <number>', 'Number of explores to deploy in each batch (default: 50)', '50')
|
|
270
272
|
.option('--parallel-batches <number>', 'Number of batches to send in parallel (default: 1, use higher values with caution)', '1')
|
|
271
273
|
.option('--expires-in <hours>', 'Number of hours until the preview project auto-expires (default: 720, i.e. 30 days)')
|
|
274
|
+
.option('--validate-warehouse-columns', validateWarehouseColumnsDescription, false)
|
|
272
275
|
.action(preview_1.previewHandler);
|
|
273
276
|
commander_1.program
|
|
274
277
|
.command('start-preview')
|
|
@@ -307,6 +310,7 @@ commander_1.program
|
|
|
307
310
|
.option('--batch-size <number>', 'Number of explores to deploy in each batch (default: 50)', '50')
|
|
308
311
|
.option('--parallel-batches <number>', 'Number of batches to send in parallel (default: 1, use higher values with caution)', '1')
|
|
309
312
|
.option('--expires-in <hours>', 'Number of hours until the preview project auto-expires (default: 720, i.e. 30 days)')
|
|
313
|
+
.option('--validate-warehouse-columns', validateWarehouseColumnsDescription, false)
|
|
310
314
|
.action(preview_1.startPreviewHandler);
|
|
311
315
|
commander_1.program
|
|
312
316
|
.command('stop-preview')
|
|
@@ -365,10 +369,10 @@ const downloadCommand = commander_1.program
|
|
|
365
369
|
.option('--include-scheduled-deliveries', 'include all scheduled deliveries in the download', false)
|
|
366
370
|
.option('--include-all', 'include all optional content in the download', false)
|
|
367
371
|
.option('--strip-pivot-series', 'strip per-value pivot series config from chart YAML for portable downloads', false)
|
|
368
|
-
.option('--apps <
|
|
372
|
+
.option('--apps <appReferences...>', 'Include specific data apps by UUID or URL (enterprise). Works for apps not added to a space.')
|
|
369
373
|
.option('--include-apps', "Include all of the project's data apps (enterprise), capped at --apps-limit (default: 50)", false)
|
|
370
374
|
.option('--apps-limit <number>', 'Maximum number of data apps downloaded by --include-apps or --include-all (default: 50)', undefined)
|
|
371
|
-
.option('--apps-only', 'Download only data apps (implies --skip-charts --skip-dashboards --skip-spaces). Requires --apps <
|
|
375
|
+
.option('--apps-only', 'Download only data apps (implies --skip-charts --skip-dashboards --skip-spaces). Requires --apps <appReferences...>, --include-apps, or --include-all.', false)
|
|
372
376
|
.option('--organization', 'download all organization-scoped resources without selecting a project', false);
|
|
373
377
|
downloadCommand.action(withOrganizationMode(download_1.downloadHandler));
|
|
374
378
|
const uploadCommand = commander_1.program
|
|
@@ -398,7 +402,7 @@ const uploadCommand = commander_1.program
|
|
|
398
402
|
.option('--include-charts', 'Include charts updates when uploading dashboards', false)
|
|
399
403
|
.option('--validate', 'Validate charts and dashboards after upload', false)
|
|
400
404
|
.option('--gzip', 'Enable gzip compression for request bodies', false)
|
|
401
|
-
.option('--apps <
|
|
405
|
+
.option('--apps <appReferences...>', 'Upload specific data apps by UUID or URL (enterprise).')
|
|
402
406
|
.option('--include-apps', 'Upload all app folders on disk (enterprise).', false)
|
|
403
407
|
.option('--create-new', 'Always create a new app from the uploaded code instead of updating the app referenced by lightdash-app.yml.', false)
|
|
404
408
|
.option('--organization', 'upload all organization-scoped resources without selecting a project', false)
|
|
@@ -441,6 +445,7 @@ commander_1.program
|
|
|
441
445
|
.option('--batch-size <number>', 'Number of explores per batch (default: 50)', '50')
|
|
442
446
|
.option('--parallel-batches <number>', 'Number of batches to send in parallel (default: 1, use higher values with caution)', '1')
|
|
443
447
|
.option('--gzip', 'Enable gzip compression for request bodies', false)
|
|
448
|
+
.option('--validate-warehouse-columns', validateWarehouseColumnsDescription, false)
|
|
444
449
|
.action(deploy_1.deployHandler);
|
|
445
450
|
commander_1.program
|
|
446
451
|
.command('refresh')
|
|
@@ -481,7 +486,7 @@ commander_1.program
|
|
|
481
486
|
.addOption(new commander_1.Option('--only <elems...>', 'Specify project elements to validate')
|
|
482
487
|
.choices(Object.values(common_1.ValidationTarget))
|
|
483
488
|
.default(Object.values(common_1.ValidationTarget)))
|
|
484
|
-
.option('--validate-warehouse-columns',
|
|
489
|
+
.option('--validate-warehouse-columns', `${validateWarehouseColumnsDescription} Only applies when tables are validated.`, false)
|
|
485
490
|
.option('--include-spaces <spaceSlugs...>', 'Only report validation errors for charts and dashboards in these spaces (and their sub-spaces). Spaces must be visible to your credentials')
|
|
486
491
|
.option('--exclude-spaces <spaceSlugs...>', 'Skip validation errors for charts and dashboards in these spaces (and their sub-spaces). Spaces must be visible to your credentials')
|
|
487
492
|
.action(validate_1.validateHandler);
|
|
@@ -22,6 +22,15 @@ Each file contains:
|
|
|
22
22
|
- `metricQuery.sorts` — sort order
|
|
23
23
|
- `metricQuery.limit` — row limit
|
|
24
24
|
- `metricQuery.tableCalculations` — computed columns (may be empty)
|
|
25
|
+
- `chartConfig` — the saved visualization config. `chartConfig.type` tells you
|
|
26
|
+
what to render: `big_number` (KPI stat), `cartesian` (bar/line/area/scatter —
|
|
27
|
+
the series type is inside `chartConfig.config.eChartsConfig.series`), `table`,
|
|
28
|
+
`pie`, `funnel`, `treemap`, `gauge`, `map`, `sankey`, or `custom`. Reproduce
|
|
29
|
+
the chart type instead of guessing one from the data shape; deeper styling
|
|
30
|
+
details inside the config are optional inspiration, not a spec.
|
|
31
|
+
- `pivotConfig` — pivot dimension field IDs when the saved chart pivots its
|
|
32
|
+
results (`null` otherwise). A pivoted table/bar means one series per value of
|
|
33
|
+
the pivot dimension.
|
|
25
34
|
|
|
26
35
|
**How to use these:**
|
|
27
36
|
1. Read the JSON file(s) to understand what data the user wants in their app
|