@lightdash/cli 0.2653.0 → 0.2654.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,4 +1,4 @@
1
- import { CreateProjectTableConfiguration, ProjectType, type ApiCreateProjectResults } from '@lightdash/common';
1
+ import { CreateProjectTableConfiguration, ProjectType, type ApiCreateProjectResults, type CreateWarehouseCredentials, type DbtVersionOption } from '@lightdash/common';
2
2
  export declare const resolveOrganizationCredentialsName: (name: string) => Promise<string>;
3
3
  type CreateProjectOptions = {
4
4
  name: string;
@@ -16,6 +16,22 @@ type CreateProjectOptions = {
16
16
  targetPath?: string;
17
17
  assumeYes?: boolean;
18
18
  };
19
+ type LoadWarehouseCredentialsOptions = {
20
+ projectDir: string;
21
+ profilesDir: string;
22
+ target?: string;
23
+ profile?: string;
24
+ startOfWeek?: number;
25
+ assumeYes?: boolean;
26
+ targetPath?: string;
27
+ };
28
+ type LoadWarehouseCredentialsResult = {
29
+ credentials: CreateWarehouseCredentials;
30
+ targetName: string;
31
+ dbtVersionOption: DbtVersionOption;
32
+ isDbtCloudCLI: boolean;
33
+ };
34
+ export declare const loadWarehouseCredentialsFromProfiles: (options: LoadWarehouseCredentialsOptions) => Promise<LoadWarehouseCredentialsResult | null>;
19
35
  export declare const createProject: (options: CreateProjectOptions) => Promise<ApiCreateProjectResults | undefined>;
20
36
  export {};
21
37
  //# sourceMappingURL=createProject.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createProject.d.ts","sourceRoot":"","sources":["../../src/handlers/createProject.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,+BAA+B,EAI/B,WAAW,EAGX,KAAK,uBAAuB,EAI/B,MAAM,mBAAmB,CAAC;AAgE3B,eAAO,MAAM,kCAAkC,GAC3C,MAAM,MAAM,KACb,OAAO,CAAC,MAAM,CAyChB,CAAC;AAEF,KAAK,oBAAoB,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,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,WAAW,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,+BAA+B,CAAC;IACrD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAYF,eAAO,MAAM,aAAa,GACtB,SAAS,oBAAoB,KAC9B,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAsM7C,CAAC"}
1
+ {"version":3,"file":"createProject.d.ts","sourceRoot":"","sources":["../../src/handlers/createProject.ts"],"names":[],"mappings":"AAAA,OAAO,EAEH,+BAA+B,EAI/B,WAAW,EAGX,KAAK,uBAAuB,EAC5B,KAAK,0BAA0B,EAC/B,KAAK,gBAAgB,EAExB,MAAM,mBAAmB,CAAC;AAgE3B,eAAO,MAAM,kCAAkC,GAC3C,MAAM,MAAM,KACb,OAAO,CAAC,MAAM,CAyChB,CAAC;AAEF,KAAK,oBAAoB,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,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,WAAW,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,+BAA+B,CAAC;IACrD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAqDF,KAAK,+BAA+B,GAAG;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,KAAK,8BAA8B,GAAG;IAClC,WAAW,EAAE,0BAA0B,CAAC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,eAAO,MAAM,oCAAoC,GAC7C,SAAS,+BAA+B,KACzC,OAAO,CAAC,8BAA8B,GAAG,IAAI,CA2F/C,CAAC;AAEF,eAAO,MAAM,aAAa,GACtB,SAAS,oBAAoB,KAC9B,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAsG7C,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createProject = exports.resolveOrganizationCredentialsName = void 0;
3
+ exports.createProject = exports.loadWarehouseCredentialsFromProfiles = exports.resolveOrganizationCredentialsName = 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"));
@@ -93,6 +93,99 @@ const isSnowflakeSsoEnabled = async () => {
93
93
  });
94
94
  return response?.auth?.snowflake?.enabled ?? false;
95
95
  };
96
+ const applySnowflakeSsoHandling = async (credentials) => {
97
+ if (credentials.type !== common_1.WarehouseTypes.SNOWFLAKE ||
98
+ credentials.authenticationType !==
99
+ common_1.SnowflakeAuthenticationType.EXTERNAL_BROWSER) {
100
+ return credentials;
101
+ }
102
+ const snowflakeSsoEnabled = await isSnowflakeSsoEnabled();
103
+ if (snowflakeSsoEnabled) {
104
+ console.error(styles.info(`\nLightdash server has Snowflake OAuth authentication enabled.
105
+ We will ask for user credentials again on the Lightdash UI.\n`));
106
+ return {
107
+ ...credentials,
108
+ requireUserCredentials: true,
109
+ };
110
+ }
111
+ console.error(styles.warning(`\nUser has externalbrowser snowflake authentication.
112
+ We will generate programatically a temporary PAT to enable access on Lightdash which expires in 1 day.
113
+ For a better user experience, we recommend enabling Snowflake OAuth authentication on the server.\n`));
114
+ const patToken = await (0, getWarehouseClient_1.createProgramaticallySnowflakePat)(credentials);
115
+ return {
116
+ ...credentials,
117
+ authenticationType: common_1.SnowflakeAuthenticationType.PASSWORD,
118
+ password: patToken,
119
+ };
120
+ };
121
+ const loadWarehouseCredentialsFromProfiles = async (options) => {
122
+ const absoluteProjectPath = path_1.default.resolve(options.projectDir);
123
+ const dbtVersionResult = await (0, getDbtVersion_1.tryGetDbtVersion)();
124
+ if (!dbtVersionResult.success) {
125
+ throw dbtVersionResult.error;
126
+ }
127
+ const { isDbtCloudCLI } = dbtVersionResult.version;
128
+ const dbtVersionOption = dbtVersionResult.version.versionOption;
129
+ const context = await (0, context_1.getDbtContext)({
130
+ projectDir: absoluteProjectPath,
131
+ targetPath: options.targetPath,
132
+ });
133
+ globalState_1.default.debug(`> Using profiles dir ${options.profilesDir} and profile ${options.profile || context.profileName}`);
134
+ const targetName = await (0, getDbtProfileTargetName_1.default)({
135
+ isDbtCloudCLI,
136
+ profilesDir: options.profilesDir,
137
+ profile: options.profile || context.profileName,
138
+ target: options.target,
139
+ });
140
+ globalState_1.default.debug(`> Using target name ${targetName}`);
141
+ const canStoreWarehouseCredentials = await askPermissionToStoreWarehouseCredentials(options.assumeYes);
142
+ if (!canStoreWarehouseCredentials) {
143
+ return null;
144
+ }
145
+ const result = await (0, getWarehouseClient_1.default)({
146
+ isDbtCloudCLI,
147
+ profilesDir: options.profilesDir,
148
+ profile: options.profile || context.profileName,
149
+ target: options.target,
150
+ startOfWeek: options.startOfWeek,
151
+ });
152
+ const { credentials } = result;
153
+ if (credentials.type === common_1.WarehouseTypes.BIGQUERY &&
154
+ credentials.keyfileContents.project_id &&
155
+ credentials.keyfileContents.project_id !== credentials.project) {
156
+ if (globalState_1.default.isNonInteractive() && !options.assumeYes) {
157
+ throw new Error(`BigQuery project mismatch: credentials file uses "${credentials.keyfileContents.project_id}" ` +
158
+ `but profiles.yml specifies "${credentials.project}". ` +
159
+ 'This may cause permission issues. Use --assume-yes to bypass this warning.');
160
+ }
161
+ if (options.assumeYes) {
162
+ globalState_1.default.debug(`> Auto-accepting BigQuery project mismatch (credentials: ${credentials.keyfileContents.project_id}, profiles: ${credentials.project})`);
163
+ }
164
+ else {
165
+ const spinner = globalState_1.default.getActiveSpinner();
166
+ spinner?.stop();
167
+ const answers = await inquirer_1.default.prompt([
168
+ {
169
+ type: 'confirm',
170
+ name: 'isConfirm',
171
+ message: `${styles.title('Warning')}: Your project on your credentials file ${styles.title(credentials.keyfileContents.project_id)} does not match your project on your profiles.yml ${styles.title(credentials.project)}, this might cause permission issues when accessing data on the warehouse. Are you sure you want to continue?`,
172
+ },
173
+ ]);
174
+ if (!answers.isConfirm) {
175
+ process.exit(1);
176
+ }
177
+ spinner?.start();
178
+ }
179
+ }
180
+ const finalCredentials = await applySnowflakeSsoHandling(credentials);
181
+ return {
182
+ credentials: finalCredentials,
183
+ targetName,
184
+ dbtVersionOption,
185
+ isDbtCloudCLI,
186
+ };
187
+ };
188
+ exports.loadWarehouseCredentialsFromProfiles = loadWarehouseCredentialsFromProfiles;
96
189
  const createProject = async (options) => {
97
190
  await (0, apiClient_1.checkProjectCreationPermission)(options.upstreamProjectUuid, options.type);
98
191
  // Resolve organization credentials early before doing any heavy work
@@ -134,88 +227,24 @@ const createProject = async (options) => {
134
227
  // No dbt needed - use defaults set above
135
228
  }
136
229
  else {
137
- const dbtVersionResult = await (0, getDbtVersion_1.tryGetDbtVersion)();
138
- if (!dbtVersionResult.success) {
139
- throw dbtVersionResult.error;
140
- }
141
- isDbtCloudCLI = dbtVersionResult.version.isDbtCloudCLI;
142
- dbtVersionOption = dbtVersionResult.version.versionOption;
143
- const context = await (0, context_1.getDbtContext)({
144
- projectDir: absoluteProjectPath,
145
- targetPath: options.targetPath,
146
- });
147
- globalState_1.default.debug(`> Using profiles dir ${options.profilesDir} and profile ${options.profile || context.profileName}`);
148
- targetName = await (0, getDbtProfileTargetName_1.default)({
149
- isDbtCloudCLI,
230
+ const loaded = await (0, exports.loadWarehouseCredentialsFromProfiles)({
231
+ projectDir: options.projectDir,
150
232
  profilesDir: options.profilesDir,
151
- profile: options.profile || context.profileName,
152
233
  target: options.target,
234
+ profile: options.profile,
235
+ startOfWeek: options.startOfWeek,
236
+ assumeYes: options.assumeYes,
237
+ targetPath: options.targetPath,
153
238
  });
154
- globalState_1.default.debug(`> Using target name ${targetName}`);
155
- const canStoreWarehouseCredentials = await askPermissionToStoreWarehouseCredentials(options.assumeYes);
156
- if (!canStoreWarehouseCredentials) {
239
+ if (!loaded) {
240
+ // User declined to store warehouse credentials
157
241
  globalState_1.default.debug('> User declined to store warehouse credentials use --no-warehouse-credentials to create a project without warehouse credentials');
158
242
  return undefined;
159
243
  }
160
- const result = await (0, getWarehouseClient_1.default)({
161
- isDbtCloudCLI,
162
- profilesDir: options.profilesDir,
163
- profile: options.profile || context.profileName,
164
- target: options.target,
165
- startOfWeek: options.startOfWeek,
166
- });
167
- credentials = result.credentials;
168
- }
169
- if (credentials?.type === common_1.WarehouseTypes.BIGQUERY &&
170
- credentials.keyfileContents.project_id &&
171
- credentials.keyfileContents.project_id !== credentials.project) {
172
- if (globalState_1.default.isNonInteractive() && !options.assumeYes) {
173
- throw new Error(`BigQuery project mismatch: credentials file uses "${credentials.keyfileContents.project_id}" ` +
174
- `but profiles.yml specifies "${credentials.project}". ` +
175
- 'This may cause permission issues. Use --assume-yes to bypass this warning.');
176
- }
177
- if (options.assumeYes) {
178
- globalState_1.default.debug(`> Auto-accepting BigQuery project mismatch (credentials: ${credentials.keyfileContents.project_id}, profiles: ${credentials.project})`);
179
- }
180
- else {
181
- const spinner = globalState_1.default.getActiveSpinner();
182
- spinner?.stop();
183
- const answers = await inquirer_1.default.prompt([
184
- {
185
- type: 'confirm',
186
- name: 'isConfirm',
187
- message: `${styles.title('Warning')}: Your project on your credentials file ${styles.title(credentials.keyfileContents.project_id)} does not match your project on your profiles.yml ${styles.title(credentials.project)}, this might cause permission issues when accessing data on the warehouse. Are you sure you want to continue?`,
188
- },
189
- ]);
190
- if (!answers.isConfirm) {
191
- process.exit(1);
192
- }
193
- spinner?.start();
194
- }
195
- }
196
- if (credentials?.type === common_1.WarehouseTypes.SNOWFLAKE &&
197
- credentials?.authenticationType ===
198
- common_1.SnowflakeAuthenticationType.EXTERNAL_BROWSER) {
199
- const snowflakeSsoEnabled = await isSnowflakeSsoEnabled();
200
- if (snowflakeSsoEnabled) {
201
- console.error(styles.info(`\nLightdash server has Snowflake OAuth authentication enabled.
202
- We will ask for user credentials again on the Lightdash UI.\n`));
203
- credentials = {
204
- ...credentials,
205
- requireUserCredentials: true,
206
- };
207
- }
208
- else {
209
- console.error(styles.warning(`\nUser has externalbrowser snowflake authentication.
210
- We will generate programatically a temporary PAT to enable access on Lightdash which expires in 1 day.
211
- For a better user experience, we recommend enabling Snowflake OAuth authentication on the server.\n`));
212
- const patToken = await (0, getWarehouseClient_1.createProgramaticallySnowflakePat)(credentials);
213
- credentials = {
214
- ...credentials,
215
- authenticationType: common_1.SnowflakeAuthenticationType.PASSWORD,
216
- password: patToken,
217
- };
218
- }
244
+ credentials = loaded.credentials;
245
+ targetName = loaded.targetName;
246
+ isDbtCloudCLI = loaded.isDbtCloudCLI;
247
+ dbtVersionOption = loaded.dbtVersionOption;
219
248
  }
220
249
  const project = {
221
250
  name: options.name,
@@ -1,7 +1,8 @@
1
- import { JobStep, Project } from '@lightdash/common';
1
+ import { Job, JobStep, Project } from '@lightdash/common';
2
2
  export declare const getProject: (projectUuid: string) => Promise<Project>;
3
3
  export declare const getRunningStepsMessage: (steps: JobStep[]) => string;
4
4
  export declare const getErrorStepsMessage: (steps: JobStep[]) => string;
5
+ export declare const getFinalJobState: (jobUuid: string, spinnerPrefix?: string) => Promise<Job>;
5
6
  type RefreshHandlerOptions = {
6
7
  verbose: boolean;
7
8
  };
@@ -1 +1 @@
1
- {"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../../src/handlers/dbt/refresh.ts"],"names":[],"mappings":"AAAA,OAAO,EAMH,OAAO,EAGP,OAAO,EACV,MAAM,mBAAmB,CAAC;AAQ3B,eAAO,MAAM,UAAU,GAAU,aAAa,MAAM,qBAK9C,CAAC;AAsBP,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,EAAE,KAAG,MAUzD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,EAAE,KAAG,MAUvD,CAAC;AAoBF,KAAK,qBAAqB,GAAG;IACzB,OAAO,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,SAAS,qBAAqB,kBAgElE,CAAC"}
1
+ {"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../../src/handlers/dbt/refresh.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,GAAG,EAEH,OAAO,EAGP,OAAO,EACV,MAAM,mBAAmB,CAAC;AAQ3B,eAAO,MAAM,UAAU,GAAU,aAAa,MAAM,qBAK9C,CAAC;AAsBP,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,EAAE,KAAG,MAUzD,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,EAAE,KAAG,MAUvD,CAAC;AAIF,eAAO,MAAM,gBAAgB,GACzB,SAAS,MAAM,EACf,gBAAe,MAAiC,KACjD,OAAO,CAAC,GAAG,CAcb,CAAC;AAEF,KAAK,qBAAqB,GAAG;IACzB,OAAO,EAAE,OAAO,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,SAAS,qBAAqB,kBAgElE,CAAC"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.refreshHandler = exports.getErrorStepsMessage = exports.getRunningStepsMessage = exports.getProject = void 0;
3
+ exports.refreshHandler = exports.getFinalJobState = exports.getErrorStepsMessage = exports.getRunningStepsMessage = exports.getProject = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const common_1 = require("@lightdash/common");
6
6
  const uuid_1 = require("uuid");
@@ -43,7 +43,7 @@ const getErrorStepsMessage = (steps) => {
43
43
  };
44
44
  exports.getErrorStepsMessage = getErrorStepsMessage;
45
45
  const REFETCH_JOB_INTERVAL = 3000;
46
- const getFinalJobState = async (jobUuid) => {
46
+ const getFinalJobState = async (jobUuid, spinnerPrefix = 'Refreshing dbt project') => {
47
47
  const job = await getJobState(jobUuid);
48
48
  if (job.jobStatus === common_1.JobStatusType.DONE) {
49
49
  return job;
@@ -52,9 +52,10 @@ const getFinalJobState = async (jobUuid) => {
52
52
  throw new Error((0, exports.getErrorStepsMessage)(job.steps));
53
53
  }
54
54
  const spinner = globalState_1.default.getActiveSpinner();
55
- spinner?.start(` Refreshing dbt project, ${(0, exports.getRunningStepsMessage)(job.steps)}`);
56
- return delay(REFETCH_JOB_INTERVAL).then(() => getFinalJobState(jobUuid));
55
+ spinner?.start(` ${spinnerPrefix}, ${(0, exports.getRunningStepsMessage)(job.steps)}`);
56
+ return delay(REFETCH_JOB_INTERVAL).then(() => (0, exports.getFinalJobState)(jobUuid, spinnerPrefix));
57
57
  };
58
+ exports.getFinalJobState = getFinalJobState;
58
59
  const refreshHandler = async (options) => {
59
60
  globalState_1.default.setVerbose(options.verbose);
60
61
  await (0, apiClient_1.checkLightdashVersion)();
@@ -81,7 +82,7 @@ const refreshHandler = async (options) => {
81
82
  },
82
83
  });
83
84
  const refreshResults = await refreshProject(projectUuid);
84
- await getFinalJobState(refreshResults.jobUuid);
85
+ await (0, exports.getFinalJobState)(refreshResults.jobUuid);
85
86
  await analytics_1.LightdashAnalytics.track({
86
87
  event: 'refresh.completed',
87
88
  properties: {
@@ -0,0 +1,14 @@
1
+ type SetWarehouseHandlerOptions = {
2
+ projectDir: string;
3
+ profilesDir: string;
4
+ target?: string;
5
+ profile?: string;
6
+ targetPath?: string;
7
+ project?: string;
8
+ startOfWeek?: number;
9
+ assumeYes: boolean;
10
+ verbose: boolean;
11
+ };
12
+ export declare const setWarehouseHandler: (options: SetWarehouseHandlerOptions) => Promise<void>;
13
+ export {};
14
+ //# sourceMappingURL=setWarehouse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setWarehouse.d.ts","sourceRoot":"","sources":["../../src/handlers/setWarehouse.ts"],"names":[],"mappings":"AAaA,KAAK,0BAA0B,GAAG;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,OAAO,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CACpB,CAAC;AAeF,eAAO,MAAM,mBAAmB,GAC5B,SAAS,0BAA0B,kBAyFtC,CAAC"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setWarehouseHandler = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@lightdash/common");
6
+ const inquirer_1 = tslib_1.__importDefault(require("inquirer"));
7
+ const config_1 = require("../config");
8
+ const globalState_1 = tslib_1.__importDefault(require("../globalState"));
9
+ const styles = tslib_1.__importStar(require("../styles"));
10
+ const createProject_1 = require("./createProject");
11
+ const apiClient_1 = require("./dbt/apiClient");
12
+ const refresh_1 = require("./dbt/refresh");
13
+ const resolveProjectUuid = async (projectOption) => {
14
+ if (projectOption) {
15
+ return projectOption;
16
+ }
17
+ const config = await (0, config_1.getConfig)();
18
+ if (!config.context?.project) {
19
+ throw new common_1.AuthorizationError(`No project selected. Run 'lightdash config set-project' first or pass '--project <uuid>'.`);
20
+ }
21
+ return config.context.project;
22
+ };
23
+ const setWarehouseHandler = async (options) => {
24
+ globalState_1.default.setVerbose(options.verbose);
25
+ await (0, apiClient_1.checkLightdashVersion)();
26
+ const projectUuid = await resolveProjectUuid(options.project);
27
+ // Load credentials from profiles.yml
28
+ const loaded = await (0, createProject_1.loadWarehouseCredentialsFromProfiles)({
29
+ projectDir: options.projectDir,
30
+ profilesDir: options.profilesDir,
31
+ target: options.target,
32
+ profile: options.profile,
33
+ startOfWeek: options.startOfWeek,
34
+ assumeYes: options.assumeYes,
35
+ targetPath: options.targetPath,
36
+ });
37
+ if (!loaded) {
38
+ console.error(styles.warning('User declined to store warehouse credentials. Use --assume-yes to bypass.'));
39
+ return;
40
+ }
41
+ const { credentials } = loaded;
42
+ // Fetch existing project to preserve its settings
43
+ const existingProject = await (0, refresh_1.getProject)(projectUuid);
44
+ // Confirmation prompt
45
+ if (!options.assumeYes && !globalState_1.default.isNonInteractive()) {
46
+ const spinner = globalState_1.default.getActiveSpinner();
47
+ spinner?.stop();
48
+ const answers = await inquirer_1.default.prompt([
49
+ {
50
+ type: 'confirm',
51
+ name: 'isConfirm',
52
+ message: `This will update the warehouse connection on project '${existingProject.name}' to ${credentials.type} and trigger a recompile. Continue?`,
53
+ default: false,
54
+ },
55
+ ]);
56
+ if (!answers.isConfirm) {
57
+ console.error(styles.warning('Aborted.'));
58
+ return;
59
+ }
60
+ spinner?.start();
61
+ }
62
+ const spinner = globalState_1.default.startSpinner(' Updating warehouse connection...');
63
+ try {
64
+ // Build UpdateProject body — preserve existing fields, override warehouseConnection.
65
+ // Note: dbtConnection from GET response may have stripped secrets, but the backend's
66
+ // mergeMissingProjectConfigSecrets fills them back in from the saved project before persisting.
67
+ const updateBody = {
68
+ name: existingProject.name,
69
+ dbtConnection: existingProject.dbtConnection,
70
+ dbtVersion: existingProject.dbtVersion,
71
+ warehouseConnection: credentials,
72
+ };
73
+ // PATCH project — triggers adaptor test + recompile
74
+ const result = await (0, apiClient_1.lightdashApi)({
75
+ method: 'PATCH',
76
+ url: `/api/v1/projects/${projectUuid}`,
77
+ body: JSON.stringify(updateBody),
78
+ });
79
+ // Poll until job completes (custom spinner prefix)
80
+ await (0, refresh_1.getFinalJobState)(result.jobUuid, 'Updating warehouse connection');
81
+ spinner.stop();
82
+ }
83
+ catch (e) {
84
+ spinner.fail();
85
+ throw e;
86
+ }
87
+ const config = await (0, config_1.getConfig)();
88
+ const displayUrl = `${config.context?.serverUrl}/projects/${projectUuid}/home`;
89
+ console.error(`${styles.bold('Successfully updated warehouse connection:')}`);
90
+ console.error('');
91
+ console.error(` ${styles.bold(`⚡️ ${displayUrl}`)}`);
92
+ console.error('');
93
+ };
94
+ exports.setWarehouseHandler = setWarehouseHandler;
package/dist/index.js CHANGED
@@ -25,6 +25,7 @@ const preview_1 = require("./handlers/preview");
25
25
  const renameHandler_1 = require("./handlers/renameHandler");
26
26
  const runChart_1 = require("./handlers/runChart");
27
27
  const setProject_1 = require("./handlers/setProject");
28
+ const setWarehouse_1 = require("./handlers/setWarehouse");
28
29
  const sql_1 = require("./handlers/sql");
29
30
  const validate_1 = require("./handlers/validate");
30
31
  const styles = tslib_1.__importStar(require("./styles"));
@@ -485,6 +486,19 @@ commander_1.program
485
486
  .option('--page-size <number>', 'Number of rows per page (default: 500)', parseIntArgument)
486
487
  .option('--verbose', 'Show detailed output', false)
487
488
  .action(runChart_1.runChartHandler);
489
+ commander_1.program
490
+ .command('set-warehouse')
491
+ .description("Update a project's warehouse connection from dbt profiles.yml. Use --assume-yes for non-interactive/CI usage. For organization-managed credentials, use the Lightdash UI.")
492
+ .option('--project-dir <path>', 'The directory of the dbt project', env_1.DEFAULT_DBT_PROJECT_DIR)
493
+ .option('--profiles-dir <path>', 'The directory of the dbt profiles', env_1.DEFAULT_DBT_PROFILES_DIR)
494
+ .option('--profile <name>', 'The name of the profile to use (defaults to profile name in dbt_project.yml)', undefined)
495
+ .option('--target <name>', 'target to use in profiles.yml file', undefined)
496
+ .option('--target-path <path>', 'The target directory for dbt (overrides DBT_TARGET_PATH and dbt_project.yml)', undefined)
497
+ .option('--project <uuid>', 'Lightdash project UUID to update (defaults to currently selected project)', undefined)
498
+ .option('--start-of-week <number>', 'Specifies the first day of the week (used by week-related date functions). 0 (Monday) to 6 (Sunday)', parseStartOfWeekArgument)
499
+ .option('-y, --assume-yes', 'assume yes to prompts', false)
500
+ .option('--verbose', 'Show detailed output', false)
501
+ .action(setWarehouse_1.setWarehouseHandler);
488
502
  commander_1.program
489
503
  .command('export-chart-image')
490
504
  .description('Export a deployed chart as a PNG image. The chart must already exist on the server.')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightdash/cli",
3
- "version": "0.2653.0",
3
+ "version": "0.2654.0",
4
4
  "description": "Lightdash CLI tool",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -40,8 +40,8 @@
40
40
  "unique-names-generator": "^4.7.1",
41
41
  "uuid": "^11.0.3",
42
42
  "yaml": "^2.7.0",
43
- "@lightdash/common": "0.2653.0",
44
- "@lightdash/warehouses": "0.2653.0"
43
+ "@lightdash/common": "0.2654.0",
44
+ "@lightdash/warehouses": "0.2654.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@types/inquirer": "^8.2.1",