@lightdash/cli 0.3466.0 → 0.3468.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,6 +1,7 @@
1
1
  import { type DataAppCodeDownload } from '@lightdash/common';
2
2
  export declare const DEFAULT_APPS_LIMIT = 50;
3
3
  export declare const getDataAppUuidFromReference: (reference: string) => string;
4
+ export declare const getDataAppUploadFilter: (references: string[], includeApps: boolean) => Set<string> | null;
4
5
  /**
5
6
  * Resolves the --apps-limit flag. Commander passes the raw string (or
6
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;AAG3B,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,eAAO,MAAM,2BAA2B,GAAI,WAAW,MAAM,KAAG,MAoB/D,CAAC;AAEF;;;;;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,6 +1,6 @@
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.getDataAppUuidFromReference = 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
5
  const uuid_1 = require("uuid");
6
6
  exports.DEFAULT_APPS_LIMIT = 50;
@@ -21,6 +21,8 @@ const getDataAppUuidFromReference = (reference) => {
21
21
  return appSegmentIndex >= 0 ? pathSegments[appSegmentIndex + 1] : reference;
22
22
  };
23
23
  exports.getDataAppUuidFromReference = getDataAppUuidFromReference;
24
+ const getDataAppUploadFilter = (references, includeApps) => includeApps ? null : new Set(references.map(exports.getDataAppUuidFromReference));
25
+ exports.getDataAppUploadFilter = getDataAppUploadFilter;
24
26
  /**
25
27
  * Resolves the --apps-limit flag. Commander passes the raw string (or
26
28
  * undefined when the flag was not given, so an explicit flag is
@@ -38,7 +40,7 @@ const resolveAppsLimit = (rawLimit, includeApps) => {
38
40
  limit: parseInt(rawLimit, 10),
39
41
  noEffectWarning: includeApps
40
42
  ? null
41
- : '--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.',
42
44
  };
43
45
  };
44
46
  exports.resolveAppsLimit = resolveAppsLimit;
@@ -25,6 +25,17 @@ describe('getDataAppUuidFromReference', () => {
25
25
  expect((0, appsDownload_1.getDataAppUuidFromReference)(reference)).toBe(reference);
26
26
  });
27
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
+ });
28
39
  const makeDownload = () => ({
29
40
  manifest: {
30
41
  codeVersion: 1,
@@ -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
@@ -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')
@@ -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
@@ -0,0 +1,100 @@
1
+ # Dashboard blueprint (dashboard attached to the prompt)
2
+
3
+ > Read this when the prompt announces an attached dashboard with a blueprint at
4
+ > `/tmp/dashboard/blueprint.json`.
5
+
6
+ ## Contents
7
+
8
+ - What the blueprint JSON contains
9
+ - How to map tabs, the tile grid, and tile types to app structure
10
+ - How to wire dashboard filters as interactive controls
11
+ - What to skip and when to deviate
12
+
13
+ The user attached a Lightdash dashboard. The blueprint is its full structural
14
+ definition — use it as the layout spec and recreate the dashboard's design,
15
+ instead of inventing a layout from scratch. The chart queries themselves live in
16
+ `/tmp/metric-queries/*.json` (see `chart-references.md`); the blueprint tells you
17
+ how those charts were arranged and filtered.
18
+
19
+ ## Blueprint shape
20
+
21
+ ```jsonc
22
+ {
23
+ "dashboardUuid": "…",
24
+ "name": "Sales overview", // use as the app title unless the user names it
25
+ "description": "…", // may be null
26
+ "tabs": [ // sorted by order; [] when the dashboard has no tabs
27
+ { "uuid": "tab-1", "name": "Summary", "order": 0, "hidden": false }
28
+ ],
29
+ "tiles": [ /* see below */ ],
30
+ "filters": { // dashboard-level filters
31
+ "dimensions": [ /* DashboardFilterRule */ ],
32
+ "metrics": [],
33
+ "tableCalculations": []
34
+ },
35
+ "parameters": { /* default parameter values, or null */ },
36
+ "config": { /* date-zoom settings etc., or null */ }
37
+ }
38
+ ```
39
+
40
+ ### Tiles
41
+
42
+ Every tile has: `uuid`, `type`, `x`, `y`, `w`, `h`, `tabUuid`, `properties`.
43
+
44
+ **Grid geometry:** positions are on a **36-column grid** — `x`/`w` are column
45
+ units (a full-width tile is `w: 36`, a half-width tile `w: 18`), `y`/`h` are row
46
+ units of roughly 55px. Translate proportionally into your layout (e.g. CSS grid
47
+ with 12 columns: divide `x`/`w` by 3). Preserve the relative arrangement — what
48
+ sits side by side, what spans full width, the top-to-bottom order — rather than
49
+ the pixel-exact math. `tabUuid` says which tab the tile belongs to (`null` when
50
+ the dashboard has no tabs).
51
+
52
+ Tile types and their `properties`:
53
+
54
+ - `saved_chart` — `savedChartUuid`, `chartName`, optional `title` (falls back to
55
+ the chart name) and `hideTitle`. **Match it to its metric-query file via
56
+ `savedChartUuid` = the `chartUuid` field inside each
57
+ `/tmp/metric-queries/*.json`.** Build the tile's visualization from that
58
+ file's `metricQuery` + `chartConfig` (or `savedChart(uuid)` when the file says
59
+ LINKED).
60
+ - `markdown` — `title`, `content` (markdown source). Recreate as a text/prose
61
+ section.
62
+ - `heading` — `text`, `showDivider`. Recreate as a section header.
63
+ - `loom` — `title`, `url`. Recreate as a link out to the video (do not embed
64
+ arbitrary iframes).
65
+ - `sql_chart` — `savedSqlUuid`, `chartName`. Its query is NOT available in
66
+ `/tmp/metric-queries/`. Keep its place in the layout: render a clearly
67
+ labelled placeholder card with the tile's title, or rebuild it from catalog
68
+ fields if the prompt makes the intent obvious.
69
+ - `data_app` — an embedded data app. Skip it; note it in a comment.
70
+
71
+ ### Tabs
72
+
73
+ Recreate tabs as top-level navigation (tab bar / segmented control), one view
74
+ per tab, tiles assigned by `tabUuid`, ordered by `order`. Skip tabs with
75
+ `hidden: true`. Single implicit tab (empty `tabs`) → a single page, no tab
76
+ chrome.
77
+
78
+ ### Dashboard filters
79
+
80
+ Each rule in `filters.dimensions` is a `DashboardFilterRule`: `target`
81
+ (`fieldId`, `tableName`), `operator`, `values`, `label`, and `tileTargets`.
82
+ Recreate these as the app's interactive filter controls (see the global-filters
83
+ guidance in `skill.md`):
84
+
85
+ - One control per rule; `label` (or the target field's label) is the control's
86
+ caption; `values` are the default selection; `disabled: true` means "no
87
+ default value" — start unfiltered.
88
+ - `tileTargets` maps tile `uuid` → the field to filter on for that tile, or
89
+ `false` when the rule must NOT apply to that tile. A missing entry means
90
+ "apply with the rule's own target field". Respect exclusions — that a chart is
91
+ exempt from a filter is deliberate dashboard design.
92
+ - Apply selections via `.filters()` — inline convention for `query(...)`,
93
+ qualified ids for `savedChart(...)` (rules in `chart-references.md`).
94
+
95
+ ## When to deviate
96
+
97
+ The blueprint wins on structure (tabs, arrangement, which filters exist) unless
98
+ the user's prompt asks for something different — the prompt always wins. You may
99
+ modernize presentation (spacing, typography, card styling per the design skill)
100
+ while keeping the structure recognizably the same dashboard.
@@ -148,6 +148,10 @@ These metrics are **not additive over time**. A balance on Monday plus the balan
148
148
 
149
149
  If the prompt lists referenced charts (files under `/tmp/metric-queries/*.json`), read `/app/references/chart-references.md` before writing any query code — it defines the JSON shape, linked-vs-copied chart semantics (`savedChart`), and the field-id mapping rules.
150
150
 
151
+ ## Attached dashboard blueprint
152
+
153
+ If the prompt announces an attached dashboard (blueprint at `/tmp/dashboard/blueprint.json`), read `/app/references/dashboard-blueprint.md` before designing any layout — the blueprint defines the dashboard's tabs, tile grid, and filters, and it is the layout spec to recreate unless the user asks for a different design.
154
+
151
155
  ## Linked external connections
152
156
 
153
157
  If the prompt lists external connections (files under `/tmp/external-data/`), read `/app/references/external-apis.md` before calling any external API — it documents each connection file and the `externalFetch` rules.