Skip to content

Commit 3227169

Browse files
authored
Merge pull request #14267 from OfficeDev/qinzhouxu/envvar
fix: add fallback question for asking missing environment variables
2 parents c58a7e0 + 7c25d8a commit 3227169

File tree

5 files changed

+287
-202
lines changed

5 files changed

+287
-202
lines changed

packages/fx-core/resource/package.nls.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,7 @@
857857
"driver.file.createOrUpdateEnvironmentFile.OpenAIDeploymentName.validation": "Azure OpenAI deployment name cannot be empty.",
858858
"driver.file.createOrUpdateEnvironmentFile.OpenAIAssistantID.validation": "OpenAI assistant ID cannot be empty.",
859859
"driver.file.createOrUpdateEnvironmentFile.OpenAIEmbeddingDeploymentName.validation": "Azure OpenAI embedding deployment name cannot be empty.",
860+
"driver.file.createOrUpdateEnvironmentFile.genericEnvVar.validation": "Environment variable value cannot be empty.",
860861
"driver.file.createOrUpdateJsonFile.description": "Create or update JSON file.",
861862
"driver.file.createOrUpdateJsonFile.summary": "Json file has been successfully generated to %s.",
862863
"driver.file.progressBar.appsettings": "Generating json file...",

packages/fx-core/src/component/configManager/lifecycle.ts

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import {
2424
} from "./interface";
2525
import { MissingEnvironmentVariablesError } from "../../error";
2626
import { setErrorContext } from "../../common/globalVars";
27-
import { OpenAIEnvironmentVariables } from "../constants";
2827

2928
function resolveDriverDef(
3029
def: DriverDefinition,
@@ -102,16 +101,7 @@ export function resolveString(
102101
resolved.push(envVar);
103102
newVal = newVal.replace(matches[0], envVal);
104103
}
105-
} else if (
106-
envVar === OpenAIEnvironmentVariables.SECRET_AZURE_OPENAI_API_KEY ||
107-
envVar === OpenAIEnvironmentVariables.AZURE_OPENAI_ENDPOINT ||
108-
envVar === OpenAIEnvironmentVariables.AZURE_OPENAI_DEPLOYMENT_NAME ||
109-
envVar === OpenAIEnvironmentVariables.SECRET_OPENAI_API_KEY ||
110-
envVar === OpenAIEnvironmentVariables.AZURE_OPENAI_MODEL_DEPLOYMENT_NAME ||
111-
envVar === OpenAIEnvironmentVariables.OPENAI_ASSISTANT_ID ||
112-
envVar === OpenAIEnvironmentVariables.AZURE_OPENAI_ASSISTANT_ID ||
113-
envVar === OpenAIEnvironmentVariables.AZURE_OPENAI_EMBEDDING_DEPLOYMENT
114-
) {
104+
} else if (envVar.includes("AZURE") || envVar.includes("OPENAI")) {
115105
if (envVal) {
116106
resolved.push(envVar);
117107
newVal = newVal.replace(matches[0], envVal);

packages/fx-core/src/component/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,5 @@ export const OpenAIEnvironmentVariables = {
128128
OPENAI_ASSISTANT_ID: "OPENAI_ASSISTANT_ID",
129129
AZURE_OPENAI_ASSISTANT_ID: "AZURE_OPENAI_ASSISTANT_ID",
130130
AZURE_OPENAI_EMBEDDING_DEPLOYMENT: "AZURE_OPENAI_EMBEDDING_DEPLOYMENT",
131+
AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME: "AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME",
131132
};

packages/fx-core/src/component/driver/file/createOrUpdateEnvironmentFile.ts

Lines changed: 140 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,15 @@
22
// Licensed under the MIT license.
33

44
import { hooks } from "@feathersjs/hooks/lib";
5-
import { FxError, Result, SystemError, UserError, Void, ok } from "@microsoft/teamsfx-api";
5+
import {
6+
FxError,
7+
Result,
8+
SystemError,
9+
UserError,
10+
Void,
11+
ok,
12+
TextInputQuestion,
13+
} from "@microsoft/teamsfx-api";
614
import * as dotenv from "dotenv";
715
import * as fs from "fs-extra";
816
import * as os from "os";
@@ -132,205 +140,48 @@ export class CreateOrUpdateEnvironmentFileDriver implements StepDriver {
132140
args: GenerateEnvArgs,
133141
envOutput: Map<string, string>
134142
): Promise<Result<Void, FxError>> {
135-
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/;
136-
if (args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_API_KEY]) {
137-
const matches = placeHolderReg.exec(
138-
args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_API_KEY]
139-
);
140-
if (matches != null && matches.length > 1) {
141-
const result = await ctx.ui!.inputText({
142-
name: azureOpenAIKeyQuestion().name,
143-
title: azureOpenAIKeyQuestion().title as string,
144-
password: azureOpenAIKeyQuestion().password,
145-
validation: (input: string): string | undefined => {
146-
if (input.length < 1) {
147-
return getLocalizedString(
148-
"driver.file.createOrUpdateEnvironmentFile.OpenAIKey.validation"
149-
);
150-
}
151-
},
152-
});
153-
if (result.isErr()) {
154-
return result;
155-
} else {
156-
envOutput.set(matches[1], result.value.result!);
157-
args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_API_KEY] = result.value.result!;
158-
}
159-
}
160-
}
161-
162-
if (args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_ENDPOINT]) {
163-
const matches = placeHolderReg.exec(
164-
args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_ENDPOINT]
143+
for (const [envKey, envValue] of Object.entries(args.envs)) {
144+
const result = await this.processOpenAIEnvironmentVariable(
145+
ctx,
146+
args,
147+
envOutput,
148+
envKey,
149+
envValue
165150
);
166-
if (matches != null && matches.length > 1) {
167-
const result = await ctx.ui!.inputText({
168-
name: azureOpenAIEndpointQuestion().name,
169-
title: azureOpenAIEndpointQuestion().title as string,
170-
validation: (input: string): string | undefined => {
171-
if (!input.startsWith("https://") && !input.startsWith("http://")) {
172-
return getLocalizedString(
173-
"driver.file.createOrUpdateEnvironmentFile.OpenAIDeploymentEndpoint.validation"
174-
);
175-
}
176-
},
177-
});
178-
if (result.isErr()) {
179-
return result;
180-
} else {
181-
envOutput.set(matches[1], result.value.result!);
182-
args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_ENDPOINT] = result.value.result!;
183-
}
184-
}
185-
}
186-
187-
if (args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_DEPLOYMENT_NAME]) {
188-
const matches = placeHolderReg.exec(
189-
args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_DEPLOYMENT_NAME]
190-
);
191-
if (matches != null && matches.length > 1) {
192-
const result = await ctx.ui!.inputText({
193-
name: azureOpenAIDeploymentNameQuestion().name,
194-
title: azureOpenAIDeploymentNameQuestion().title as string,
195-
validation: (input: string): string | undefined => {
196-
if (input.length < 1) {
197-
return getLocalizedString(
198-
"driver.file.createOrUpdateEnvironmentFile.OpenAIDeploymentName.validation"
199-
);
200-
}
201-
},
202-
});
203-
if (result.isErr()) {
204-
return result;
205-
} else {
206-
envOutput.set(matches[1], result.value.result!);
207-
args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_DEPLOYMENT_NAME] = result.value.result!;
208-
}
209-
}
210-
}
211-
212-
if (args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_MODEL_DEPLOYMENT_NAME]) {
213-
const matches = placeHolderReg.exec(
214-
args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_MODEL_DEPLOYMENT_NAME]
215-
);
216-
if (matches != null && matches.length > 1) {
217-
const result = await ctx.ui!.inputText({
218-
name: azureOpenAIDeploymentNameQuestion().name,
219-
title: azureOpenAIDeploymentNameQuestion().title as string,
220-
validation: (input: string): string | undefined => {
221-
if (input.length < 1) {
222-
return getLocalizedString(
223-
"driver.file.createOrUpdateEnvironmentFile.OpenAIDeploymentName.validation"
224-
);
225-
}
226-
},
227-
});
228-
if (result.isErr()) {
229-
return result;
230-
} else {
231-
envOutput.set(matches[1], result.value.result!);
232-
args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_MODEL_DEPLOYMENT_NAME] =
233-
result.value.result!;
234-
}
235-
}
236-
}
237-
238-
if (args.envs[OpenAIEnvironmentVariables.OPENAI_API_KEY]) {
239-
const matches = placeHolderReg.exec(args.envs[OpenAIEnvironmentVariables.OPENAI_API_KEY]);
240-
if (matches != null && matches.length > 1) {
241-
const result = await ctx.ui!.inputText({
242-
name: openAIKeyQuestion().name,
243-
title: openAIKeyQuestion().title as string,
244-
password: true,
245-
validation: (input: string): string | undefined => {
246-
if (input.length < 1) {
247-
return getLocalizedString(
248-
"driver.file.createOrUpdateEnvironmentFile.OpenAIKey.validation"
249-
);
250-
}
251-
},
252-
});
253-
if (result.isErr()) {
254-
return result;
255-
} else {
256-
envOutput.set(matches[1], result.value.result!);
257-
args.envs[OpenAIEnvironmentVariables.OPENAI_API_KEY] = result.value.result!;
258-
}
151+
if (result.isErr()) {
152+
return result;
259153
}
260154
}
155+
return ok(Void);
156+
}
261157

262-
if (args.envs[OpenAIEnvironmentVariables.OPENAI_ASSISTANT_ID]) {
263-
const matches = placeHolderReg.exec(
264-
args.envs[OpenAIEnvironmentVariables.OPENAI_ASSISTANT_ID]
265-
);
158+
private async processOpenAIEnvironmentVariable(
159+
ctx: DriverContext,
160+
args: GenerateEnvArgs,
161+
envOutput: Map<string, string>,
162+
envKey: string,
163+
envValue: string
164+
): Promise<Result<Void, FxError>> {
165+
if (envValue) {
166+
const placeHolderReg = /\${{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*}}/;
167+
const matches = placeHolderReg.exec(envValue);
266168
if (matches != null && matches.length > 1) {
267-
const result = await ctx.ui!.inputText({
268-
name: openAIAssistantIdQuestion().name,
269-
title: openAIAssistantIdQuestion().title as string,
270-
validation: (input: string): string | undefined => {
271-
if (input.length < 1) {
272-
return getLocalizedString(
273-
"driver.file.createOrUpdateEnvironmentFile.OpenAIAssistantID.validation"
274-
);
275-
}
276-
},
277-
});
278-
if (result.isErr()) {
279-
return result;
280-
} else {
281-
envOutput.set(matches[1], result.value.result!);
282-
args.envs[OpenAIEnvironmentVariables.OPENAI_ASSISTANT_ID] = result.value.result!;
283-
}
284-
}
285-
}
169+
const envVarName = matches[1];
170+
const config =
171+
envVarConfigs.find((c) => c.envKey === envKey) || getGenericEnvVarConfig(envKey);
286172

287-
if (args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_ASSISTANT_ID]) {
288-
const matches = placeHolderReg.exec(
289-
args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_ASSISTANT_ID]
290-
);
291-
if (matches != null && matches.length > 1) {
292-
const result = await ctx.ui!.inputText({
293-
name: azureOpenAIAssistantIdQuestion().name,
294-
title: azureOpenAIAssistantIdQuestion().title as string,
295-
validation: (input: string): string | undefined => {
296-
if (input.length < 1) {
297-
return getLocalizedString(
298-
"driver.file.createOrUpdateEnvironmentFile.OpenAIAssistantID.validation"
299-
);
300-
}
301-
},
173+
const result = await ctx.ui?.inputText({
174+
name: config.question.name,
175+
title: config.question.title as string,
176+
password: config.isPassword || config.question.password,
177+
validation: config.validation,
302178
});
303-
if (result.isErr()) {
304-
return result;
305-
} else {
306-
envOutput.set(matches[1], result.value.result!);
307-
args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_ASSISTANT_ID] = result.value.result!;
308-
}
309-
}
310-
}
311179

312-
if (args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_EMBEDDING_DEPLOYMENT]) {
313-
const matches = placeHolderReg.exec(
314-
args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_EMBEDDING_DEPLOYMENT]
315-
);
316-
if (matches != null && matches.length > 1) {
317-
const result = await ctx.ui!.inputText({
318-
name: azureOpenAIEmbeddingDeploymentNameQuestion().name,
319-
title: azureOpenAIEmbeddingDeploymentNameQuestion().title as string,
320-
validation: (input: string): string | undefined => {
321-
if (input.length < 1) {
322-
return getLocalizedString(
323-
"driver.file.createOrUpdateEnvironmentFile.OpenAIEmbeddingDeploymentName.validation"
324-
);
325-
}
326-
},
327-
});
328-
if (result.isErr()) {
180+
if (result?.isErr()) {
329181
return result;
330-
} else {
331-
envOutput.set(matches[1], result.value.result!);
332-
args.envs[OpenAIEnvironmentVariables.AZURE_OPENAI_EMBEDDING_DEPLOYMENT] =
333-
result.value.result!;
182+
} else if (result?.isOk() && result.value.result) {
183+
envOutput.set(envVarName, result.value.result);
184+
args.envs[envKey] = result.value.result;
334185
}
335186
}
336187
}
@@ -358,3 +209,101 @@ export class CreateOrUpdateEnvironmentFileDriver implements StepDriver {
358209
}
359210
}
360211
}
212+
213+
interface OpenAIEnvVarConfig {
214+
envKey: string;
215+
question: TextInputQuestion;
216+
validation: (input: string) => string | undefined;
217+
isPassword?: boolean;
218+
}
219+
220+
const getNonEmptyStringValidation = (key: string): ((input: string) => string | undefined) => {
221+
return (input: string) => (input.length < 1 ? getLocalizedString(key) : undefined);
222+
};
223+
224+
const getEndpointValidation = (key: string): ((input: string) => string | undefined) => {
225+
return (input: string) =>
226+
!input.startsWith("https://") && !input.startsWith("http://")
227+
? getLocalizedString(key)
228+
: undefined;
229+
};
230+
231+
const getGenericEnvVarConfig = (envKey: string): OpenAIEnvVarConfig => ({
232+
envKey,
233+
question: {
234+
type: "text",
235+
name: envKey,
236+
title: envKey,
237+
},
238+
validation: getNonEmptyStringValidation(
239+
"driver.file.createOrUpdateEnvironmentFile.genericEnvVar.validation"
240+
),
241+
});
242+
243+
const envVarConfigs: OpenAIEnvVarConfig[] = [
244+
{
245+
envKey: OpenAIEnvironmentVariables.AZURE_OPENAI_API_KEY,
246+
question: azureOpenAIKeyQuestion(),
247+
validation: getNonEmptyStringValidation(
248+
"driver.file.createOrUpdateEnvironmentFile.OpenAIKey.validation"
249+
),
250+
isPassword: true,
251+
},
252+
{
253+
envKey: OpenAIEnvironmentVariables.AZURE_OPENAI_ENDPOINT,
254+
question: azureOpenAIEndpointQuestion(),
255+
validation: getEndpointValidation(
256+
"driver.file.createOrUpdateEnvironmentFile.OpenAIDeploymentEndpoint.validation"
257+
),
258+
},
259+
{
260+
envKey: OpenAIEnvironmentVariables.AZURE_OPENAI_DEPLOYMENT_NAME,
261+
question: azureOpenAIDeploymentNameQuestion(),
262+
validation: getNonEmptyStringValidation(
263+
"driver.file.createOrUpdateEnvironmentFile.OpenAIDeploymentName.validation"
264+
),
265+
},
266+
{
267+
envKey: OpenAIEnvironmentVariables.AZURE_OPENAI_MODEL_DEPLOYMENT_NAME,
268+
question: azureOpenAIDeploymentNameQuestion(),
269+
validation: getNonEmptyStringValidation(
270+
"driver.file.createOrUpdateEnvironmentFile.OpenAIDeploymentName.validation"
271+
),
272+
},
273+
{
274+
envKey: OpenAIEnvironmentVariables.OPENAI_API_KEY,
275+
question: openAIKeyQuestion(),
276+
validation: getNonEmptyStringValidation(
277+
"driver.file.createOrUpdateEnvironmentFile.OpenAIKey.validation"
278+
),
279+
isPassword: true,
280+
},
281+
{
282+
envKey: OpenAIEnvironmentVariables.OPENAI_ASSISTANT_ID,
283+
question: openAIAssistantIdQuestion(),
284+
validation: getNonEmptyStringValidation(
285+
"driver.file.createOrUpdateEnvironmentFile.OpenAIAssistantID.validation"
286+
),
287+
},
288+
{
289+
envKey: OpenAIEnvironmentVariables.AZURE_OPENAI_ASSISTANT_ID,
290+
question: azureOpenAIAssistantIdQuestion(),
291+
validation: getNonEmptyStringValidation(
292+
"driver.file.createOrUpdateEnvironmentFile.OpenAIAssistantID.validation"
293+
),
294+
},
295+
{
296+
envKey: OpenAIEnvironmentVariables.AZURE_OPENAI_EMBEDDING_DEPLOYMENT,
297+
question: azureOpenAIEmbeddingDeploymentNameQuestion(),
298+
validation: getNonEmptyStringValidation(
299+
"driver.file.createOrUpdateEnvironmentFile.OpenAIEmbeddingDeploymentName.validation"
300+
),
301+
},
302+
{
303+
envKey: OpenAIEnvironmentVariables.AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME,
304+
question: azureOpenAIEmbeddingDeploymentNameQuestion(),
305+
validation: getNonEmptyStringValidation(
306+
"driver.file.createOrUpdateEnvironmentFile.OpenAIEmbeddingDeploymentName.validation"
307+
),
308+
},
309+
];

0 commit comments

Comments
 (0)