@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.
@@ -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;AAE3B,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;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
+ {"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 UUIDs are never capped.',
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"}
@@ -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 --skip-warehouse-catalog was supplied'));
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((e) => (0, common_1.isExploreError)(e)).length;
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 --skip-warehouse-catalog was supplied';
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');
@@ -18,6 +18,7 @@ type DeployHandlerOptions = DbtCompileOptions & {
18
18
  parallelBatches?: string;
19
19
  gzip?: boolean;
20
20
  disableTimestampConversion?: boolean;
21
+ validateWarehouseColumns: boolean;
21
22
  };
22
23
  type DeployArgs = DeployHandlerOptions & {
23
24
  projectUuid: string;
@@ -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,EAUf,MAAM,mBAAmB,CAAC;AAuB3B,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;CACxC,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,CA2Id,CAAC;AAiGF,eAAO,MAAM,aAAa,GAAU,iBAAiB,oBAAoB,kBAmKxE,CAAC"}
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"}
@@ -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((e) => (0, common_1.isExploreError)(e)).length;
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(explores, options);
233
+ await deployBatched(deployableExplores, options);
233
234
  }
234
235
  else {
235
236
  const deployStartTime = Date.now();
236
- const deployPayload = JSON.stringify(explores);
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":"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"}
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 <appUuids...>, --include-apps, or --include-all.');
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 = !options.spacesOnly &&
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 <uuids...> or
1606
+ // Upload data apps (enterprise, opt-in via --apps <references...> or
1603
1607
  // --include-apps, fire-and-forget)
1604
- const explicitAppUuids = Array.isArray(options.apps)
1608
+ const explicitAppReferences = Array.isArray(options.apps)
1605
1609
  ? options.apps
1606
1610
  : [];
1607
- const shouldUploadApps = options.includeApps === true || explicitAppUuids.length > 0;
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 UUIDs
1624
+ // --include-apps uploads every folder on disk; explicit references
1621
1625
  // filter folders by their manifest appUuid
1622
- const filterUuids = options.includeApps === true ? null : new Set(explicitAppUuids);
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,
@@ -19,6 +19,7 @@ type PreviewHandlerOptions = DbtCompileOptions & {
19
19
  parallelBatches?: string;
20
20
  expiresIn?: string;
21
21
  disableTimestampConversion?: boolean;
22
+ validateWarehouseColumns: boolean;
22
23
  };
23
24
  type StopPreviewHandlerOptions = {
24
25
  name: string;
@@ -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;CACxC,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"}
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 <appUuids...>', 'Include specific data apps by UUID (enterprise). Works for apps not added to a space.')
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 <appUuids...>, --include-apps, or --include-all.', false)
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 <appUuids...>', 'Upload specific data apps by UUID (enterprise).')
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', `Check physical \${TABLE}.column references in dimensions and metrics by executing zero-row queries against the warehouse. Requires warehouse credentials and only applies when tables are validated.`, false)
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