@lightdash/cli 2.194.2 → 2.195.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 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,cAAc,QAAsC,CAAC;AAElE,MAAM,MAAM,MAAM,GAAG;IACjB,IAAI,CAAC,EAAE;QACH,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,OAAO,CAAC,EAAE;QACN,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB;;;WAGG;QACH,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,OAAO,CAAC,EAAE;QACN,qCAAqC,CAAC,EAAE,OAAO,CAAC;QAChD,gCAAgC,CAAC,EAAE,OAAO,CAAC;KAC9C,CAAC;CACL,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,2BAA2B,aAC1B,MAAM,KACjB,OAAO,CAAC,IAAI,CAgBd,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,aAChB,MAAM,UACR,MAAM,KACf,OAAO,CAAC,IAAI,CAed,CAAC;AAiCF,eAAO,MAAM,SAAS,QAAa,OAAO,CAAC,MAAM,CAmBhD,CAAC;AAEF,eAAO,MAAM,UAAU,gBAAuB,MAAM,eAAe,MAAM,kBAUxE,CAAC;AAEF,eAAO,MAAM,YAAY,qBAUxB,CAAC;AAEF,eAAO,MAAM,iBAAiB,gBAAuB,MAAM,QAAQ,MAAM,kBAUxE,CAAC;AAEF,eAAO,MAAM,mBAAmB,qBAU/B,CAAC;AAEF,eAAO,MAAM,cAAc,aACb,MAAM,oBACE,MAAM,kBAW3B,CAAC;AAEF,eAAO,MAAM,UAAU,YAAmB,MAAM,CAAC,SAAS,CAAC,kBAM1D,CAAC;AAEF,eAAO,MAAM,SAAS,WAAkB,MAAM,CAAC,SAAS,CAAC,kBAMxD,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,cAAc,QAAsC,CAAC;AAElE,MAAM,MAAM,MAAM,GAAG;IACjB,IAAI,CAAC,EAAE;QACH,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,OAAO,CAAC,EAAE;QACN,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB;;;WAGG;QACH,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,kBAAkB,CAAC,EAAE,MAAM,CAAC;QAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,WAAW,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;IACF,OAAO,CAAC,EAAE;QACN,qCAAqC,CAAC,EAAE,OAAO,CAAC;QAChD,gCAAgC,CAAC,EAAE,OAAO,CAAC;KAC9C,CAAC;CACL,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,2BAA2B,aAC1B,MAAM,KACjB,OAAO,CAAC,IAAI,CAgBd,CAAC;AAEF,qEAAqE;AACrE,eAAO,MAAM,iBAAiB,aAChB,MAAM,UACR,MAAM,KACf,OAAO,CAAC,IAAI,CAed,CAAC;AAiCF,eAAO,MAAM,SAAS,QAAa,OAAO,CAAC,MAAM,CAwBhD,CAAC;AAEF,eAAO,MAAM,UAAU,gBAAuB,MAAM,eAAe,MAAM,kBAUxE,CAAC;AAEF,eAAO,MAAM,YAAY,qBAUxB,CAAC;AAEF,eAAO,MAAM,iBAAiB,gBAAuB,MAAM,QAAQ,MAAM,kBAUxE,CAAC;AAEF,eAAO,MAAM,mBAAmB,qBAU/B,CAAC;AAEF,eAAO,MAAM,cAAc,aACb,MAAM,oBACE,MAAM,kBAW3B,CAAC;AAEF,eAAO,MAAM,UAAU,YAAmB,MAAM,CAAC,SAAS,CAAC,kBAM1D,CAAC;AAEF,eAAO,MAAM,SAAS,WAAkB,MAAM,CAAC,SAAS,CAAC,kBAMxD,CAAC"}
package/dist/config.js CHANGED
@@ -84,6 +84,10 @@ const getConfig = async () => {
84
84
  ...(rawConfig.context || {}),
85
85
  apiKey: process.env.LIGHTDASH_API_KEY || rawConfig.context?.apiKey,
86
86
  project: process.env.LIGHTDASH_PROJECT || rawConfig.context?.project,
87
+ projectName: process.env.LIGHTDASH_PROJECT &&
88
+ process.env.LIGHTDASH_PROJECT !== rawConfig.context?.project
89
+ ? undefined
90
+ : rawConfig.context?.projectName,
87
91
  serverUrl: process.env.LIGHTDASH_URL || rawConfig.context?.serverUrl,
88
92
  proxyAuthorization: process.env.LIGHTDASH_PROXY_AUTHORIZATION ||
89
93
  rawConfig.context?.proxyAuthorization,
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const fs_1 = require("fs");
5
+ const yaml = tslib_1.__importStar(require("js-yaml"));
5
6
  const os = tslib_1.__importStar(require("os"));
6
7
  const path = tslib_1.__importStar(require("path"));
7
8
  const config_1 = require("./config");
@@ -38,3 +39,34 @@ describePosix('CLI config permissions', () => {
38
39
  expect(permissionBits((await fs_1.promises.stat(configPath)).mode)).toBe(0o600);
39
40
  });
40
41
  });
42
+ describe('CLI project environment override', () => {
43
+ afterEach(() => {
44
+ vi.restoreAllMocks();
45
+ vi.unstubAllEnvs();
46
+ });
47
+ it.each([
48
+ ['other-project', undefined],
49
+ ['stored-project', 'Stored project'],
50
+ ['', 'Stored project'],
51
+ [undefined, 'Stored project'],
52
+ ])('only retains the cached name for its own project (override: %s)', async (projectOverride, expectedName) => {
53
+ vi.stubEnv('LIGHTDASH_PROJECT', projectOverride);
54
+ vi.spyOn(fs_1.promises, 'chmod').mockResolvedValue(undefined);
55
+ vi.spyOn(fs_1.promises, 'readFile').mockResolvedValue(yaml.dump({
56
+ user: { anonymousUuid: 'anonymous-user' },
57
+ context: {
58
+ project: 'stored-project',
59
+ projectName: 'Stored project',
60
+ previewProject: 'preview-project',
61
+ previewName: 'Preview project',
62
+ },
63
+ }));
64
+ const config = await (0, config_1.getConfig)();
65
+ expect(config.context).toMatchObject({
66
+ project: projectOverride || 'stored-project',
67
+ projectName: expectedName,
68
+ previewProject: 'preview-project',
69
+ previewName: 'Preview project',
70
+ });
71
+ });
72
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"renameHandler.d.ts","sourceRoot":"","sources":["../../src/handlers/renameHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,UAAU,EAEb,MAAM,mBAAmB,CAAC;AAmB3B,KAAK,oBAAoB,GAAG;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;CACrB,CAAC;AAwCF,eAAO,MAAM,aAAa,YAAmB,oBAAoB,kBAmNhE,CAAC"}
1
+ {"version":3,"file":"renameHandler.d.ts","sourceRoot":"","sources":["../../src/handlers/renameHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,UAAU,EAEb,MAAM,mBAAmB,CAAC;AAmB3B,KAAK,oBAAoB,GAAG;IACxB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,IAAI,EAAE,OAAO,CAAC;IACd,QAAQ,EAAE,OAAO,CAAC;CACrB,CAAC;AAwCF,eAAO,MAAM,aAAa,YAAmB,oBAAoB,kBAwNhE,CAAC"}
@@ -13,7 +13,7 @@ const globalState_1 = tslib_1.__importDefault(require("../globalState"));
13
13
  const styles = tslib_1.__importStar(require("../styles"));
14
14
  const apiClient_1 = require("./dbt/apiClient");
15
15
  const refresh_1 = require("./dbt/refresh");
16
- const resolveProjectFlag_1 = require("./resolveProjectFlag");
16
+ const selectProject_1 = require("./selectProject");
17
17
  const validate_1 = require("./validate");
18
18
  const REFETCH_JOB_INTERVAL = 2000;
19
19
  const waitUntilRenameFinished = (jobUuid) => (0, validate_1.waitUntilFinished)({
@@ -45,9 +45,10 @@ const renameHandler = async (options) => {
45
45
  if (!config.context?.apiKey || !config.context.serverUrl) {
46
46
  throw new common_1.AuthorizationError(`Not logged in. Run 'lightdash login --help'`);
47
47
  }
48
- const projectUuid = options.project
49
- ? await (0, resolveProjectFlag_1.resolveProjectFlag)(options.project)
50
- : config.context.previewProject || config.context.project;
48
+ const overrideProjectUuid = await (0, selectProject_1.resolveProjectOverride)(config, options.project);
49
+ const projectUuid = overrideProjectUuid ||
50
+ config.context.previewProject ||
51
+ config.context.project;
51
52
  if (!projectUuid) {
52
53
  throw new common_1.LightdashError({
53
54
  message: 'No project selected. Run lightdash config set-project',
@@ -57,7 +58,7 @@ const renameHandler = async (options) => {
57
58
  });
58
59
  }
59
60
  // Log current project info
60
- if (options.project) {
61
+ if (overrideProjectUuid) {
61
62
  console.error(`\n${styles.success('Renaming in project:')} ${projectUuid}\n`);
62
63
  }
63
64
  else {
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
3
4
  const common_1 = require("@lightdash/common");
4
5
  const analytics_1 = require("../analytics/analytics");
5
6
  const config_1 = require("../config");
7
+ const globalState_1 = tslib_1.__importDefault(require("../globalState"));
6
8
  const apiClient_1 = require("./dbt/apiClient");
7
9
  const refresh_1 = require("./dbt/refresh");
8
10
  const renameHandler_1 = require("./renameHandler");
@@ -29,6 +31,7 @@ const emptyResults = {
29
31
  };
30
32
  const RENAME_JOB_ID = 'rename-job-id';
31
33
  const VALIDATION_JOB_ID = 'validation-job-id';
34
+ const PREVIEW_PROJECT = '33333333-3333-4333-8333-333333333333';
32
35
  describe('renameHandler follow-up validation', () => {
33
36
  let errorOutput;
34
37
  const mockApi = (failingJobId) => {
@@ -54,11 +57,19 @@ describe('renameHandler follow-up validation', () => {
54
57
  if (url.includes('/validate?jobId=')) {
55
58
  return [];
56
59
  }
60
+ if (method === 'GET' &&
61
+ url === `/api/v1/projects/${PREVIEW_PROJECT}`) {
62
+ return {
63
+ projectUuid: PREVIEW_PROJECT,
64
+ type: common_1.ProjectType.PREVIEW,
65
+ };
66
+ }
57
67
  throw new Error(`Unexpected API call: ${method} ${url}`);
58
68
  });
59
69
  };
60
70
  beforeEach(() => {
61
71
  vi.clearAllMocks();
72
+ vi.stubEnv('LIGHTDASH_PROJECT', undefined);
62
73
  vi.mocked(apiClient_1.checkLightdashVersion).mockResolvedValue(undefined);
63
74
  vi.mocked(config_1.getConfig).mockResolvedValue({
64
75
  context: {
@@ -78,11 +89,41 @@ describe('renameHandler follow-up validation', () => {
78
89
  });
79
90
  afterEach(() => {
80
91
  vi.restoreAllMocks();
92
+ vi.unstubAllEnvs();
81
93
  });
82
94
  const trackedEvents = () => vi.mocked(analytics_1.LightdashAnalytics.track).mock.calls.map(([payload]) => ({
83
95
  event: payload.event,
84
96
  properties: payload.properties,
85
97
  }));
98
+ test.each([undefined, baseOptions.project])('targets the environment project unless --project overrides it (%s)', async (explicitProject) => {
99
+ const envProject = '22222222-2222-4222-8222-222222222222';
100
+ vi.stubEnv('LIGHTDASH_PROJECT', envProject);
101
+ vi.spyOn(globalState_1.default, 'isNonInteractive').mockReturnValue(true);
102
+ vi.mocked(config_1.getConfig).mockResolvedValue({
103
+ context: {
104
+ apiKey: 'test-key',
105
+ serverUrl: 'http://localhost',
106
+ project: envProject,
107
+ previewProject: PREVIEW_PROJECT,
108
+ previewName: 'Stale preview',
109
+ },
110
+ });
111
+ mockApi(null);
112
+ await (0, renameHandler_1.renameHandler)({ ...baseOptions, project: explicitProject });
113
+ const expectedProject = explicitProject || envProject;
114
+ expect(apiClient_1.lightdashApi).toHaveBeenCalledWith(expect.objectContaining({
115
+ method: 'POST',
116
+ url: `/api/v1/projects/${expectedProject}/rename`,
117
+ }));
118
+ const output = errorOutput.join('\n');
119
+ expect(output).toContain(`Renaming in project: ${expectedProject}`);
120
+ if (explicitProject) {
121
+ expect(output).not.toContain('Stale preview');
122
+ }
123
+ else {
124
+ expect(output).toContain('active preview "Stale preview" ignored');
125
+ }
126
+ });
86
127
  test('reports a failed validation job as a validation failure, not a rename failure', async () => {
87
128
  mockApi(VALIDATION_JOB_ID);
88
129
  await (0, renameHandler_1.renameHandler)({ ...baseOptions });
@@ -3,14 +3,21 @@ export type ProjectSelection = {
3
3
  projectUuid: string;
4
4
  isPreview: boolean;
5
5
  };
6
+ /**
7
+ * Resolves an explicit project override: `--project` wins over
8
+ * LIGHTDASH_PROJECT, and either one bypasses an active preview.
9
+ * Returns undefined when neither is set.
10
+ */
11
+ export declare const resolveProjectOverride: (config: Config, explicitProject: string | undefined) => Promise<string | undefined>;
6
12
  /**
7
13
  * Resolves which project to use, prompting the user if there's an active preview.
8
14
  *
9
15
  * Priority:
10
16
  * 1. If `explicitProject` is provided (via --project flag), use it
11
- * 2. If there's an active preview and we're in interactive mode, ask the user
12
- * 3. In non-interactive mode with active preview, use preview project
13
- * 4. Fall back to the main project
17
+ * 2. If LIGHTDASH_PROJECT is set, use it (warns when a preview is ignored)
18
+ * 3. If there's an active preview and we're in interactive mode, ask the user
19
+ * 4. In non-interactive mode with active preview, use preview project
20
+ * 5. Fall back to the main project
14
21
  */
15
22
  export declare const selectProject: (config: Config, explicitProject?: string) => Promise<ProjectSelection | undefined>;
16
23
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"selectProject.d.ts","sourceRoot":"","sources":["../../src/handlers/selectProject.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAuB,MAAM,WAAW,CAAC;AAMxD,MAAM,MAAM,gBAAgB,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACtB,CAAC;AAmCF;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,WACd,MAAM,oBACI,MAAM,KACzB,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAmFtC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,cAChB,gBAAgB,UACnB,MAAM,UACN,MAAM,KACf,IAsBF,CAAC"}
1
+ {"version":3,"file":"selectProject.d.ts","sourceRoot":"","sources":["../../src/handlers/selectProject.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAuB,MAAM,WAAW,CAAC;AAMxD,MAAM,MAAM,gBAAgB,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;CACtB,CAAC;AAsDF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,WACvB,MAAM,mBACG,MAAM,GAAG,SAAS,KACpC,OAAO,CAAC,MAAM,GAAG,SAAS,CAW5B,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,WACd,MAAM,oBACI,MAAM,KACzB,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAmFtC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,cAChB,gBAAgB,UACnB,MAAM,UACN,MAAM,KACf,IAsBF,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.logSelectedProject = exports.selectProject = void 0;
3
+ exports.logSelectedProject = exports.selectProject = exports.resolveProjectOverride = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const common_1 = require("@lightdash/common");
6
6
  const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
@@ -34,22 +34,47 @@ const validatePreviewProject = async (previewProjectUuid) => {
34
34
  return previewProjectUuid;
35
35
  }
36
36
  };
37
+ const warnIgnoredPreview = async (config, projectUuid) => {
38
+ const previewProject = await validatePreviewProject(config.context?.previewProject);
39
+ if (!previewProject || previewProject === projectUuid) {
40
+ return;
41
+ }
42
+ const previewName = config.context?.previewName;
43
+ const previewLabel = previewName ? `"${previewName}"` : previewProject;
44
+ globalState_1.default.log(styles.warning(`\nUsing project ${projectUuid} from LIGHTDASH_PROJECT; active preview ${previewLabel} ignored.\nPass --project ${previewProject} or unset LIGHTDASH_PROJECT to target the preview.\n`));
45
+ };
46
+ /**
47
+ * Resolves an explicit project override: `--project` wins over
48
+ * LIGHTDASH_PROJECT, and either one bypasses an active preview.
49
+ * Returns undefined when neither is set.
50
+ */
51
+ const resolveProjectOverride = async (config, explicitProject) => {
52
+ if (explicitProject) {
53
+ return (0, resolveProjectFlag_1.resolveProjectFlag)(explicitProject);
54
+ }
55
+ const envProject = process.env.LIGHTDASH_PROJECT;
56
+ if (!envProject) {
57
+ return undefined;
58
+ }
59
+ const projectUuid = await (0, resolveProjectFlag_1.resolveProjectFlag)(envProject);
60
+ await warnIgnoredPreview(config, projectUuid);
61
+ return projectUuid;
62
+ };
63
+ exports.resolveProjectOverride = resolveProjectOverride;
37
64
  /**
38
65
  * Resolves which project to use, prompting the user if there's an active preview.
39
66
  *
40
67
  * Priority:
41
68
  * 1. If `explicitProject` is provided (via --project flag), use it
42
- * 2. If there's an active preview and we're in interactive mode, ask the user
43
- * 3. In non-interactive mode with active preview, use preview project
44
- * 4. Fall back to the main project
69
+ * 2. If LIGHTDASH_PROJECT is set, use it (warns when a preview is ignored)
70
+ * 3. If there's an active preview and we're in interactive mode, ask the user
71
+ * 4. In non-interactive mode with active preview, use preview project
72
+ * 5. Fall back to the main project
45
73
  */
46
74
  const selectProject = async (config, explicitProject) => {
47
- // If explicit project provided via --project flag, use it
48
- if (explicitProject) {
49
- return {
50
- projectUuid: await (0, resolveProjectFlag_1.resolveProjectFlag)(explicitProject),
51
- isPreview: false,
52
- };
75
+ const overrideProjectUuid = await (0, exports.resolveProjectOverride)(config, explicitProject);
76
+ if (overrideProjectUuid) {
77
+ return { projectUuid: overrideProjectUuid, isPreview: false };
53
78
  }
54
79
  const mainProject = config.context?.project;
55
80
  const previewProject = await validatePreviewProject(config.context?.previewProject);
@@ -1,6 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
3
4
  const common_1 = require("@lightdash/common");
5
+ const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
6
+ const config_1 = require("../config");
7
+ const globalState_1 = tslib_1.__importDefault(require("../globalState"));
4
8
  const apiClient_1 = require("./dbt/apiClient");
5
9
  const selectProject_1 = require("./selectProject");
6
10
  vi.mock('inquirer');
@@ -22,8 +26,70 @@ const mockPreviewProjectResponse = (uuid) => {
22
26
  });
23
27
  };
24
28
  describe('selectProject', () => {
29
+ beforeEach(() => {
30
+ vi.stubEnv('LIGHTDASH_PROJECT', undefined);
31
+ });
25
32
  afterEach(() => {
26
- vi.clearAllMocks();
33
+ vi.resetAllMocks();
34
+ vi.restoreAllMocks();
35
+ vi.unstubAllEnvs();
36
+ });
37
+ it.each([true, false])('uses LIGHTDASH_PROJECT instead of an active preview and warns (non-interactive: %s)', async (nonInteractive) => {
38
+ vi.stubEnv('LIGHTDASH_PROJECT', MAIN_UUID);
39
+ vi.spyOn(globalState_1.default, 'isNonInteractive').mockReturnValue(nonInteractive);
40
+ const log = vi
41
+ .spyOn(globalState_1.default, 'log')
42
+ .mockImplementation(() => { });
43
+ mockPreviewProjectResponse(PREVIEW_UUID);
44
+ const config = {
45
+ context: {
46
+ project: MAIN_UUID,
47
+ previewProject: PREVIEW_UUID,
48
+ previewName: 'My preview',
49
+ },
50
+ };
51
+ expect(await (0, selectProject_1.selectProject)(config)).toEqual({
52
+ projectUuid: MAIN_UUID,
53
+ isPreview: false,
54
+ });
55
+ expect(inquirer_1.default.prompt).not.toHaveBeenCalled();
56
+ expect(log).toHaveBeenCalledTimes(1);
57
+ const warning = String(log.mock.calls[0][0]);
58
+ expect(warning).toContain(`Using project ${MAIN_UUID} from LIGHTDASH_PROJECT`);
59
+ expect(warning).toContain('active preview "My preview" ignored');
60
+ expect(warning).toContain(`--project ${PREVIEW_UUID}`);
61
+ });
62
+ it('clears a deleted preview instead of warning when LIGHTDASH_PROJECT is set', async () => {
63
+ vi.stubEnv('LIGHTDASH_PROJECT', MAIN_UUID);
64
+ const log = vi.spyOn(globalState_1.default, 'log').mockImplementation(() => { });
65
+ mockLightdashApi.mockRejectedValueOnce(new common_1.LightdashError({
66
+ message: 'Not found',
67
+ name: 'NotFoundError',
68
+ statusCode: 404,
69
+ data: {},
70
+ }));
71
+ expect(await (0, selectProject_1.selectProject)({
72
+ context: { project: MAIN_UUID, previewProject: PREVIEW_UUID },
73
+ })).toEqual({ projectUuid: MAIN_UUID, isPreview: false });
74
+ expect(config_1.unsetPreviewProject).toHaveBeenCalledTimes(1);
75
+ expect(log).not.toHaveBeenCalled();
76
+ });
77
+ it.each([
78
+ ['no preview is active', { project: MAIN_UUID }],
79
+ [
80
+ 'LIGHTDASH_PROJECT points at the preview itself',
81
+ { project: MAIN_UUID, previewProject: PREVIEW_UUID },
82
+ ],
83
+ ])('does not warn when %s', async (_label, context) => {
84
+ const envProject = context.previewProject ?? MAIN_UUID;
85
+ vi.stubEnv('LIGHTDASH_PROJECT', envProject);
86
+ const log = vi.spyOn(globalState_1.default, 'log').mockImplementation(() => { });
87
+ mockPreviewProjectResponse(PREVIEW_UUID);
88
+ expect(await (0, selectProject_1.selectProject)({ context })).toEqual({
89
+ projectUuid: envProject,
90
+ isPreview: false,
91
+ });
92
+ expect(log).not.toHaveBeenCalled();
27
93
  });
28
94
  it('returns the preview project (without prompting) when the stored main and preview UUIDs are the same', async () => {
29
95
  // This is the misconfigured state produced by an older `set-project`
@@ -45,7 +111,8 @@ describe('selectProject', () => {
45
111
  isPreview: true,
46
112
  });
47
113
  });
48
- it('uses an explicit --project UUID as-is, without any API call, even when a preview is active', async () => {
114
+ it.each([undefined, MAIN_UUID])('uses an explicit --project UUID ahead of the environment (%s) and active preview', async (envProject) => {
115
+ vi.stubEnv('LIGHTDASH_PROJECT', envProject);
49
116
  const config = {
50
117
  context: {
51
118
  project: MAIN_UUID,
@@ -60,7 +127,9 @@ describe('selectProject', () => {
60
127
  });
61
128
  expect(mockLightdashApi).not.toHaveBeenCalled();
62
129
  });
63
- it('resolves an explicit --project slug through the org projects list', async () => {
130
+ it.each(['flag', 'env'])('resolves a project slug from the %s through the org projects list', async (source) => {
131
+ if (source === 'env')
132
+ vi.stubEnv('LIGHTDASH_PROJECT', 'jaffle-shop');
64
133
  const config = {
65
134
  context: { project: MAIN_UUID },
66
135
  };
@@ -68,8 +137,23 @@ describe('selectProject', () => {
68
137
  { projectUuid: PREVIEW_UUID, slug: 'other', name: 'Other' },
69
138
  { projectUuid: MAIN_UUID, slug: 'jaffle-shop', name: 'Jaffle' },
70
139
  ]);
71
- const selection = await (0, selectProject_1.selectProject)(config, 'jaffle-shop');
72
- expect(selection).toEqual({ projectUuid: MAIN_UUID, isPreview: false });
140
+ const selection = await (0, selectProject_1.selectProject)(config, source === 'flag' ? 'jaffle-shop' : undefined);
141
+ expect(selection).toEqual({
142
+ projectUuid: MAIN_UUID,
143
+ isPreview: false,
144
+ });
145
+ });
146
+ it.each([undefined, ''])('keeps the active preview default without an environment override (%s)', async (envProject) => {
147
+ vi.stubEnv('LIGHTDASH_PROJECT', envProject);
148
+ vi.spyOn(globalState_1.default, 'isNonInteractive').mockReturnValue(true);
149
+ mockPreviewProjectResponse(PREVIEW_UUID);
150
+ expect(await (0, selectProject_1.selectProject)({
151
+ context: {
152
+ project: MAIN_UUID,
153
+ previewProject: PREVIEW_UUID,
154
+ },
155
+ })).toEqual({ projectUuid: PREVIEW_UUID, isPreview: true });
156
+ expect(inquirer_1.default.prompt).not.toHaveBeenCalled();
73
157
  });
74
158
  it('returns the main project when only the main project is configured', async () => {
75
159
  const config = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightdash/cli",
3
- "version": "2.194.2",
3
+ "version": "2.195.0",
4
4
  "description": "Lightdash CLI tool",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -22,8 +22,8 @@
22
22
  "@actions/core": "1.11.1",
23
23
  "@aws-sdk/credential-providers": "3.995.0",
24
24
  "@casl/ability": "6.8.0",
25
- "@lightdash/common": "2.194.2",
26
- "@lightdash/warehouses": "2.194.2",
25
+ "@lightdash/common": "2.195.0",
26
+ "@lightdash/warehouses": "2.195.0",
27
27
  "@sidvind/better-ajv-errors": "7.0.0",
28
28
  "@types/columnify": "1.5.1",
29
29
  "ajv": "8.18.0",