Initial commit

This commit is contained in:
2025-03-07 19:22:02 +01:00
commit 4a98255d83
55743 changed files with 5280367 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
import { ConditionFunc, Inputs, MultiSelectQuestion, OptionItem, Question, SingleSelectQuestion, StaticOptions, ValidationSchema } from "@microsoft/teamsfx-api";
declare class ValidationUtils {
validateInputForSingleSelectQuestion(question: SingleSelectQuestion, value: string | OptionItem, inputs: Inputs): Promise<string | undefined>;
validateInputForMultipleSelectQuestion(question: MultiSelectQuestion, value: string[] | OptionItem[], inputs: Inputs): Promise<string | undefined>;
formatInvalidInputError(key: string, value: string | OptionItem, options: StaticOptions): string;
formatInvalidOptionsError(options: StaticOptions): string;
isAllowedValue(key: string, value: string | OptionItem, options: StaticOptions, returnObject?: boolean): string | undefined;
/**
* validate value against question model definition
*/
validateInputs(question: Question, value: string | string[] | OptionItem | OptionItem[], inputs: Inputs): Promise<string | undefined>;
}
export declare const validationUtils: ValidationUtils;
/**
* A function to return a validation function according the validation schema
* @param validation validation schema
* @param inputs object to carry all user inputs
* @returns a validation function
*/
export declare function getValidationFunction<T extends string | string[] | undefined>(validation: ValidationSchema, inputs: Inputs): (input: T) => string | undefined | Promise<string | undefined>;
/**
* Implementation of validation function
* @param validSchema validation schema
* @param value value to validate
* @param inputs user inputs object, which works as the context of the validation
* @returns A human-readable string which is presented as diagnostic message.
* Return `undefined` when 'value' is valid.
*/
export declare function validate<T extends string | string[] | OptionItem | OptionItem[] | undefined>(validSchema: ValidationSchema | ConditionFunc, value: T, inputs?: Inputs): Promise<string | undefined>;
export {};
//# sourceMappingURL=validationUtils.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"validationUtils.d.ts","sourceRoot":"","sources":["../../src/ui/validationUtils.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,aAAa,EAEb,MAAM,EACN,mBAAmB,EACnB,UAAU,EACV,QAAQ,EACR,oBAAoB,EACpB,aAAa,EAIb,gBAAgB,EACjB,MAAM,wBAAwB,CAAC;AAIhC,cAAM,eAAe;IACb,oCAAoC,CACxC,QAAQ,EAAE,oBAAoB,EAC9B,KAAK,EAAE,MAAM,GAAG,UAAU,EAC1B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAQxB,sCAAsC,CAC1C,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE,EAC9B,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAW9B,uBAAuB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM;IAKhG,yBAAyB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM;IAGzD,cAAc,CACZ,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,GAAG,UAAU,EAC1B,OAAO,EAAE,aAAa,EACtB,YAAY,CAAC,EAAE,OAAO,GACrB,MAAM,GAAG,SAAS;IA6BrB;;OAEG;IACG,cAAc,CAClB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,UAAU,GAAG,UAAU,EAAE,EACpD,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;CAgC/B;AAED,eAAO,MAAM,eAAe,iBAAwB,CAAC;AAErD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,EAC3E,UAAU,EAAE,gBAAgB,EAC5B,MAAM,EAAE,MAAM,GACb,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAIhE;AACD;;;;;;;GAOG;AACH,wBAAsB,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,GAAG,UAAU,GAAG,UAAU,EAAE,GAAG,SAAS,EAChG,WAAW,EAAE,gBAAgB,GAAG,aAAa,EAC7C,KAAK,EAAE,CAAC,EACR,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAqL7B"}
+297
View File
@@ -0,0 +1,297 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
Object.defineProperty(exports, "__esModule", { value: true });
exports.validate = exports.getValidationFunction = exports.validationUtils = void 0;
const tslib_1 = require("tslib");
const common_1 = require("../error/common");
const jsonschema = tslib_1.__importStar(require("jsonschema"));
class ValidationUtils {
async validateInputForSingleSelectQuestion(question, value, inputs) {
let options = question.staticOptions;
if (question.dynamicOptions) {
options = await question.dynamicOptions(inputs);
}
return this.isAllowedValue(question.name, value, options, question.returnObject);
}
async validateInputForMultipleSelectQuestion(question, value, inputs) {
let options = question.staticOptions;
if (question.dynamicOptions) {
options = await question.dynamicOptions(inputs);
}
for (const item of value) {
const error = this.isAllowedValue(question.name, item, options, question.returnObject);
if (error)
return error;
}
return undefined;
}
formatInvalidInputError(key, value, options) {
return `Invalid input '${key}':${JSON.stringify(value)}, allowed value: ${JSON.stringify(options)}`;
}
formatInvalidOptionsError(options) {
return `Invalid question, expect input object, but allowed value: ${JSON.stringify(options)}`;
}
isAllowedValue(key, value, options, returnObject) {
if (options.length === 0) {
return new common_1.EmptyOptionError(key, "validationUtils").message;
}
const optionIsStringArray = typeof options[0] === "string";
if (returnObject) {
if (optionIsStringArray) {
return this.formatInvalidOptionsError(options);
}
if (!value || typeof value === "string") {
return this.formatInvalidInputError(key, value, options);
}
if (!options.find((item) => item.id === value.id)) {
return this.formatInvalidInputError(key, value, options);
}
}
else {
if (!value || typeof value !== "string") {
return this.formatInvalidInputError(key, value, options);
}
// value is string here
const foundOption = optionIsStringArray
? options.find((item) => item === value)
: options.find((item) => item.id === value);
if (!foundOption) {
return this.formatInvalidInputError(key, value, options);
}
}
}
/**
* validate value against question model definition
*/
async validateInputs(question, value, inputs) {
if (question.type === "singleSelect") {
if (question.skipValidation)
return undefined;
return await this.validateInputForSingleSelectQuestion(question, value, inputs);
}
else if (question.type === "multiSelect") {
if (question.skipValidation)
return undefined;
return await this.validateInputForMultipleSelectQuestion(question, value, inputs);
}
else {
if (question.validation) {
const vFunc = getValidationFunction(question.validation, inputs);
const res = await vFunc(value);
if (res)
return res;
}
if (question.type === "text" && question.additionalValidationOnAccept) {
const vFunc = getValidationFunction(question.additionalValidationOnAccept, inputs);
const res = await vFunc(value);
if (res)
return res;
}
return undefined;
}
}
}
exports.validationUtils = new ValidationUtils();
/**
* A function to return a validation function according the validation schema
* @param validation validation schema
* @param inputs object to carry all user inputs
* @returns a validation function
*/
function getValidationFunction(validation, inputs) {
return function (input) {
return validate(validation, input, inputs);
};
}
exports.getValidationFunction = getValidationFunction;
/**
* Implementation of validation function
* @param validSchema validation schema
* @param value value to validate
* @param inputs user inputs object, which works as the context of the validation
* @returns A human-readable string which is presented as diagnostic message.
* Return `undefined` when 'value' is valid.
*/
async function validate(validSchema, value, inputs) {
{
//FuncValidation
const funcValidation = validSchema;
if (funcValidation.validFunc) {
const res = await funcValidation.validFunc(value, inputs);
return res;
}
else if (typeof funcValidation === "function") {
const res = await funcValidation(inputs);
if (res)
return undefined;
return "condition function is not met.";
}
}
if (!value) {
if (validSchema.required === true)
return `input value is required.`;
}
const noneEmptyKeyNum = Object.keys(validSchema).filter((key) => validSchema[key] !== undefined).length;
if (noneEmptyKeyNum === 0) {
return undefined;
}
if (value === undefined &&
(validSchema.required ||
validSchema.equals ||
validSchema.maxLength ||
validSchema.minLength ||
validSchema.pattern ||
validSchema.enum ||
validSchema.startsWith ||
validSchema.endsWith ||
validSchema.includes ||
validSchema.maxItems ||
validSchema.minItems ||
validSchema.uniqueItems ||
validSchema.contains ||
validSchema.containsAll ||
validSchema.containsAny)) {
return `'undefined' does not meet condition:'${JSON.stringify(validSchema)}'`;
}
const jsonValue = JSON.stringify(value);
{
// StringValidation
const stringValidation = validSchema;
const strToValidate = value;
if (strToValidate === undefined || typeof strToValidate === "string") {
const schema = {};
if (stringValidation.equals && typeof stringValidation.equals === "string") {
if (strToValidate === undefined) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `${jsonValue} does not meet equals:'${stringValidation.equals}'`;
}
schema.const = stringValidation.equals;
}
if (stringValidation.enum &&
stringValidation.enum.length > 0 &&
typeof stringValidation.enum[0] === "string")
schema.enum = stringValidation.enum;
if (stringValidation.minLength)
schema.minLength = stringValidation.minLength;
if (stringValidation.maxLength)
schema.maxLength = stringValidation.maxLength;
if (stringValidation.pattern)
schema.pattern = stringValidation.pattern;
if (Object.keys(schema).length > 0) {
const validateResult = jsonschema.validate(strToValidate, schema);
if (validateResult.errors && validateResult.errors.length > 0) {
return `${jsonValue} ${validateResult.errors[0].message}`;
}
}
if (stringValidation.startsWith) {
if (!strToValidate.startsWith(stringValidation.startsWith)) {
return `${jsonValue} does not meet startsWith:'${stringValidation.startsWith}'`;
}
}
if (stringValidation.endsWith) {
if (!strToValidate.endsWith(stringValidation.endsWith)) {
return `${jsonValue} does not meet endsWith:'${stringValidation.endsWith}'`;
}
}
if (stringValidation.includes) {
if (!strToValidate.includes(stringValidation.includes)) {
return `${jsonValue} does not meet includes:'${stringValidation.includes}'`;
}
}
if (stringValidation.notEquals) {
if (strToValidate === stringValidation.notEquals) {
return `${jsonValue} does not meet notEquals:'${stringValidation.notEquals}'`;
}
}
if (stringValidation.excludesEnum) {
if (stringValidation.excludesEnum.includes(strToValidate)) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `${jsonValue} does not meet excludesEnum:'${stringValidation.excludesEnum}'`;
}
}
}
}
//StringArrayValidation
{
const stringArrayValidation = validSchema;
const arrayToValidate = value;
if (arrayToValidate === undefined || arrayToValidate instanceof Array) {
const schema = {};
if (stringArrayValidation.maxItems)
schema.maxItems = stringArrayValidation.maxItems;
if (stringArrayValidation.minItems)
schema.minItems = stringArrayValidation.minItems;
if (stringArrayValidation.uniqueItems)
schema.uniqueItems = stringArrayValidation.uniqueItems;
if (Object.keys(schema).length > 0) {
const validateResult = jsonschema.validate(arrayToValidate, schema);
if (validateResult.errors && validateResult.errors.length > 0) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `${jsonValue} ${validateResult.errors[0].message}`;
}
}
if (stringArrayValidation.equals) {
if (stringArrayValidation.equals instanceof Array) {
stringArrayValidation.enum = stringArrayValidation.equals;
stringArrayValidation.containsAll = stringArrayValidation.equals;
}
else {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `${jsonValue} does not equals to:'${stringArrayValidation.equals}'`;
}
}
if (stringArrayValidation.enum && arrayToValidate) {
for (const item of arrayToValidate) {
if (!stringArrayValidation.enum.includes(item)) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `${jsonValue} does not meet with enum:'${stringArrayValidation.enum}'`;
}
}
}
if (stringArrayValidation.excludes) {
if (arrayToValidate && arrayToValidate.includes(stringArrayValidation.excludes)) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `${jsonValue} does not meet with excludes:'${stringArrayValidation.excludes}'`;
}
}
if (stringArrayValidation.contains) {
if (arrayToValidate && !arrayToValidate.includes(stringArrayValidation.contains)) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `${jsonValue} does not meet with contains:'${stringArrayValidation.contains}'`;
}
}
if (stringArrayValidation.containsAll) {
const containsAll = stringArrayValidation.containsAll;
if (containsAll.length > 0) {
for (const i of containsAll) {
if (arrayToValidate && !arrayToValidate.includes(i)) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `${jsonValue} does not meet with containsAll:'${containsAll}'`;
}
}
}
}
if (stringArrayValidation.containsAny) {
const containsAny = stringArrayValidation.containsAny;
if (containsAny.length > 0) {
// let array = valueToValidate as string[];
let found = false;
for (const i of containsAny) {
if (arrayToValidate && arrayToValidate.includes(i)) {
found = true;
break;
}
}
if (!found) {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
return `${jsonValue} does not meet containsAny:'${containsAny}'`;
}
}
}
}
}
return undefined;
}
exports.validate = validate;
//# sourceMappingURL=validationUtils.js.map
File diff suppressed because one or more lines are too long
+17
View File
@@ -0,0 +1,17 @@
import { FxError, IQTreeNode, InputResult, Inputs, MultiSelectQuestion, Question, Result, SingleSelectQuestion, StaticOptions, TelemetryReporter, UserInteraction, Void } from "@microsoft/teamsfx-api";
export declare function getSingleOption(q: SingleSelectQuestion | MultiSelectQuestion, option?: StaticOptions): any;
export declare function loadOptions(question: SingleSelectQuestion | MultiSelectQuestion, inputs: Inputs): Promise<StaticOptions>;
export declare type QuestionTreeVisitor = (question: Question, ui: UserInteraction, inputs: Inputs, step?: number, totalSteps?: number) => Promise<Result<InputResult<any>, FxError>>;
/**
* ask question when visiting the question tree
* @param question
* @param core
* @param inputs
*/
export declare const questionVisitor: QuestionTreeVisitor;
/**
* serialize the tree node into array in DFS order
*/
export declare function collect(node: IQTreeNode, list: IQTreeNode[], parentMap: Map<IQTreeNode, IQTreeNode>): void;
export declare function traverse(root: IQTreeNode, inputs: Inputs, ui: UserInteraction, telemetryReporter?: TelemetryReporter, visitor?: QuestionTreeVisitor): Promise<Result<Void, FxError>>;
//# sourceMappingURL=visitor.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"visitor.d.ts","sourceRoot":"","sources":["../../src/ui/visitor.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,OAAO,EACP,UAAU,EACV,WAAW,EACX,MAAM,EACN,mBAAmB,EAEnB,QAAQ,EACR,MAAM,EACN,oBAAoB,EACpB,aAAa,EAGb,iBAAiB,EAEjB,eAAe,EACf,IAAI,EAGL,MAAM,wBAAwB,CAAC;AAyBhC,wBAAgB,eAAe,CAC7B,CAAC,EAAE,oBAAoB,GAAG,mBAAmB,EAC7C,MAAM,CAAC,EAAE,aAAa,GACrB,GAAG,CAWL;AASD,wBAAsB,WAAW,CAC/B,QAAQ,EAAE,oBAAoB,GAAG,mBAAmB,EACpD,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,aAAa,CAAC,CAMxB;AAED,oBAAY,mBAAmB,GAAG,CAChC,QAAQ,EAAE,QAAQ,EAClB,EAAE,EAAE,eAAe,EACnB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,MAAM,KAChB,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,EAAE,mBAkQ7B,CAAC;AAEF;;GAEG;AACH,wBAAgB,OAAO,CACrB,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,UAAU,EAAE,EAClB,SAAS,EAAE,GAAG,CAAC,UAAU,EAAE,UAAU,CAAC,GACrC,IAAI,CAUN;AAED,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,UAAU,EAChB,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,eAAe,EACnB,iBAAiB,CAAC,EAAE,iBAAiB,EACrC,OAAO,GAAE,mBAAqC,GAC7C,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAmGhC"}
+454
View File
@@ -0,0 +1,454 @@
"use strict";
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
Object.defineProperty(exports, "__esModule", { value: true });
exports.traverse = exports.collect = exports.questionVisitor = exports.loadOptions = exports.getSingleOption = void 0;
const teamsfx_api_1 = require("@microsoft/teamsfx-api");
const lodash_1 = require("lodash");
const error_1 = require("../error");
const validationUtils_1 = require("./validationUtils");
async function isAutoSkipSelect(q, inputs) {
let skipSingle = false;
if (q.type === "singleSelect" || q.type === "multiSelect") {
if (q.skipSingleOption !== undefined) {
if (typeof q.skipSingleOption === "function") {
skipSingle = await q.skipSingleOption(inputs);
}
else {
skipSingle = q.skipSingleOption;
}
}
}
return skipSingle;
}
function getSingleOption(q, option) {
if (!option)
option = q.staticOptions;
const optionIsString = typeof option[0] === "string";
let returnResult;
if (optionIsString)
returnResult = option[0];
else {
if (q.returnObject === true)
returnResult = option[0];
else
returnResult = option[0].id;
}
if (q.type === "singleSelect")
return returnResult;
else
return [returnResult];
}
exports.getSingleOption = getSingleOption;
async function getCallFuncValue(inputs, raw) {
if (raw && typeof raw === "function") {
return await raw(inputs);
}
return raw;
}
async function loadOptions(question, inputs) {
let options = question.staticOptions;
if (question.dynamicOptions) {
options = await question.dynamicOptions(inputs);
}
return options;
}
exports.loadOptions = loadOptions;
/**
* ask question when visiting the question tree
* @param question
* @param core
* @param inputs
*/
const questionVisitor = async function (question, ui, inputs, step, totalSteps) {
// check and validate preset answer
if (inputs[question.name] !== undefined) {
// validate existing answer in inputs object
const res = await validationUtils_1.validationUtils.validateInputs(question, inputs[question.name], inputs);
if (res)
return (0, teamsfx_api_1.err)(new error_1.InputValidationError(question.name, res, "questionVisitor"));
return (0, teamsfx_api_1.ok)({ type: "skip", result: inputs[question.name] });
}
const skipSingle = await isAutoSkipSelect(question, inputs);
// non-interactive mode
if (inputs.nonInteractive) {
// first priority: use single option as value
if (question.type === "singleSelect" || question.type === "multiSelect") {
if (skipSingle) {
const options = await loadOptions(question, inputs);
if (options.length === 0) {
return (0, teamsfx_api_1.err)(new error_1.EmptyOptionError(question.name, "questionVisitor"));
}
if (options.length === 1) {
const value = getSingleOption(question, options);
if (value) {
return (0, teamsfx_api_1.ok)({ type: "skip", result: value });
}
}
}
}
// second priority: use default as value
if (question.default) {
const value = (await getCallFuncValue(inputs, question.default));
if (value) {
const validateRes = await validationUtils_1.validationUtils.validateInputs(question, value, inputs);
if (validateRes) {
return (0, teamsfx_api_1.err)(new error_1.InputValidationError(question.name, validateRes, "questionVisitor"));
}
else {
return (0, teamsfx_api_1.ok)({ type: "skip", result: value });
}
}
}
if (question.required)
return (0, teamsfx_api_1.err)(new error_1.MissingRequiredInputError(question.name, "questionVisitor"));
else
return (0, teamsfx_api_1.ok)({ type: "skip", result: undefined });
}
// interactive mode
const title = (await getCallFuncValue(inputs, question.title));
let defaultValue = undefined;
if (question.forgetLastValue !== true && question.value)
defaultValue = question.value;
else {
if (question.default) {
if (typeof question.default === "function") {
defaultValue = async () => {
return await question.default(inputs);
};
}
else {
defaultValue = question.default;
}
}
}
const placeholder = (await getCallFuncValue(inputs, question.placeholder));
const prompt = (await getCallFuncValue(inputs, question.prompt));
if (question.type === "text") {
const validationFunc = question.validation
? (0, validationUtils_1.getValidationFunction)(question.validation, inputs)
: undefined;
const additionalValidationOnAcceptFunc = question.additionalValidationOnAccept
? (0, validationUtils_1.getValidationFunction)(question.additionalValidationOnAccept, inputs)
: undefined;
return await ui.inputText({
name: question.name,
title: title,
password: question.password,
default: defaultValue,
placeholder: placeholder,
prompt: prompt,
validation: validationFunc,
step: step,
totalSteps: totalSteps,
additionalValidationOnAccept: additionalValidationOnAcceptFunc,
});
}
else if (question.type === "singleSelect" || question.type === "multiSelect") {
let options = undefined;
if (question.dynamicOptions) {
options = async () => {
return question.dynamicOptions(inputs);
};
}
else {
if (!question.staticOptions || question.staticOptions.length === 0) {
return (0, teamsfx_api_1.err)(new error_1.EmptyOptionError(question.name, "questionVisitor"));
}
if (skipSingle && question.staticOptions.length === 1) {
const returnResult = getSingleOption(question, question.staticOptions);
return (0, teamsfx_api_1.ok)({ type: "skip", result: returnResult });
}
options = question.staticOptions;
}
if (question.type === "singleSelect") {
const validationFunc = question.validation
? (0, validationUtils_1.getValidationFunction)(question.validation, inputs)
: undefined;
return await ui.selectOption({
name: question.name,
title: title,
options: options,
returnObject: question.returnObject,
default: defaultValue,
placeholder: placeholder,
prompt: prompt,
step: step,
totalSteps: totalSteps,
buttons: question.buttons,
validation: validationFunc,
skipSingleOption: skipSingle,
});
}
else {
const validationFunc = question.validation
? (0, validationUtils_1.getValidationFunction)(question.validation, inputs)
: undefined;
return await ui.selectOptions({
name: question.name,
title: title,
options: options,
returnObject: question.returnObject,
default: defaultValue,
placeholder: placeholder,
prompt: prompt,
onDidChangeSelection: question.onDidChangeSelection,
step: step,
totalSteps: totalSteps,
validation: validationFunc,
skipSingleOption: skipSingle,
});
}
}
else if (question.type === "multiFile") {
const validationFunc = question.validation
? (0, validationUtils_1.getValidationFunction)(question.validation, inputs)
: undefined;
return await ui.selectFiles({
name: question.name,
title: title,
placeholder: placeholder,
prompt: prompt,
default: defaultValue,
step: step,
totalSteps: totalSteps,
validation: validationFunc,
});
}
else if (question.type === "singleFile") {
const validationFunc = question.validation
? (0, validationUtils_1.getValidationFunction)(question.validation, inputs)
: undefined;
let defaultFolder;
if (question.defaultFolder) {
if (typeof question.defaultFolder === "function") {
defaultFolder = async () => {
return await question.defaultFolder(inputs);
};
}
else {
defaultFolder = question.defaultFolder;
}
}
return await ui.selectFile({
name: question.name,
title: title,
placeholder: placeholder,
prompt: prompt,
default: defaultValue,
step: step,
totalSteps: totalSteps,
validation: validationFunc,
filters: question.filters,
innerStep: question.innerStep,
innerTotalStep: question.innerTotalStep,
defaultFolder,
});
}
else if (question.type === "folder") {
const validationFunc = question.validation
? (0, validationUtils_1.getValidationFunction)(question.validation, inputs)
: undefined;
return await ui.selectFolder({
name: question.name,
title: title,
placeholder: placeholder,
prompt: prompt,
default: defaultValue,
step: step,
totalSteps: totalSteps,
validation: validationFunc,
});
}
else if (question.type === "singleFileOrText" && !!ui.selectFileOrInput) {
const validationFunc = question.validation
? (0, validationUtils_1.getValidationFunction)(question.validation, inputs)
: undefined;
const inputValidationFunc = question.inputBoxConfig.validation
? (0, validationUtils_1.getValidationFunction)(question.inputBoxConfig.validation, inputs)
: undefined;
const innerTitle = (await getCallFuncValue(inputs, question.inputBoxConfig.title));
const innerPlaceholder = (await getCallFuncValue(inputs, question.inputBoxConfig.placeholder));
const innerPrompt = (await getCallFuncValue(inputs, question.inputBoxConfig.prompt));
const res = await ui.selectFileOrInput({
name: question.name,
title: title,
placeholder: placeholder,
prompt: prompt,
inputOptionItem: question.inputOptionItem,
inputBoxConfig: {
name: question.inputBoxConfig.name,
title: innerTitle,
placeholder: innerPlaceholder,
prompt: innerPrompt,
validation: inputValidationFunc,
step: question.inputBoxConfig.step,
},
filters: question.filters,
step: step,
totalSteps: totalSteps,
validation: validationFunc,
});
return res;
}
else if (question.type === "confirm" && ui.confirm) {
const res = await ui.confirm({
name: question.name,
title: title,
default: defaultValue,
step: step,
totalSteps: totalSteps,
});
return res;
}
return (0, teamsfx_api_1.err)(new teamsfx_api_1.UserError("API", "UnsupportedNodeType", `Unsupported question node type:${JSON.stringify(question)}`, `Unsupported question node type:${JSON.stringify(question)}`));
};
exports.questionVisitor = questionVisitor;
/**
* serialize the tree node into array in DFS order
*/
function collect(node, list, parentMap) {
list.push(node);
if (node.children) {
for (const child of node.children) {
if (child) {
parentMap.set(child, node);
collect(child, list, parentMap);
}
}
}
}
exports.collect = collect;
async function traverse(root, inputs, ui, telemetryReporter, visitor = exports.questionVisitor) {
// The reason to clone is that we don't want to change the original inputs if user cancel the process
const clonedInputs = (0, lodash_1.cloneDeep)(inputs);
// 1. collect all nodes into array
const parentMap = new Map();
const nodeList = [];
collect(root, nodeList, parentMap);
const visitedNodeSet = new Set();
const visitedInputNodeArray = [];
let i = 0;
for (; i < nodeList.length; ++i) {
const node = nodeList[i];
// if parent node is not visited, current node should not be visited
const parent = parentMap.get(node);
if (parent) {
if (!visitedNodeSet.has(parent)) {
continue;
}
}
// 1. check condition
if (node.condition) {
let parentValue = undefined;
// const parent = parentMap.get(node);
if (parent) {
parentValue = findValue(parent, parentMap);
}
const validRes = await (0, validationUtils_1.validate)(node.condition, parentValue, clonedInputs);
if (validRes !== undefined) {
continue;
}
}
// 2. visit node if not group
if (node.data.type !== "group") {
const question = node.data;
let res;
try {
res = await visitor(question, ui, clonedInputs, visitedInputNodeArray.length + 1, undefined);
sendTelemetryEvent(telemetryReporter, res, question, clonedInputs);
}
catch (e) {
return (0, teamsfx_api_1.err)((0, error_1.assembleError)(e));
}
if (res.isErr()) {
// Cancel or Error
return (0, teamsfx_api_1.err)(res.error);
}
const inputResult = res.value;
if (inputResult.type === "back") {
const prevNode = visitedInputNodeArray.pop();
if (!prevNode) {
return (0, teamsfx_api_1.err)(new error_1.UserCancelError());
}
for (--i; i >= 0; --i) {
const tmpNode = nodeList[i];
visitedNodeSet.delete(tmpNode);
// clear prevNode data
if (tmpNode.data.type !== "group") {
delete tmpNode.data.value;
delete tmpNode.data.valueType;
delete clonedInputs[tmpNode.data.name];
}
if (tmpNode === prevNode) {
break;
}
}
--i;
continue;
}
else {
//success or skip: set value
question.value = inputResult.result;
question.valueType = inputResult.type;
clonedInputs[question.name] = question.value;
visitedNodeSet.add(node);
if (question.valueType === "success") {
visitedInputNodeArray.push(node);
}
}
}
else {
visitedNodeSet.add(node);
}
}
(0, lodash_1.assign)(inputs, clonedInputs);
return (0, teamsfx_api_1.ok)(teamsfx_api_1.Void);
}
exports.traverse = traverse;
function findValue(curr, parentMap) {
if (curr.data.type !== "group") {
// need to convert OptionItem value into id for validation
if (curr.data.type === "singleSelect") {
const sq = curr.data;
if (sq.value && typeof sq.value !== "string" && sq.value.id) {
return sq.value.id;
}
}
else if (curr.data.type === "multiSelect") {
const mq = curr.data;
if (mq.value && typeof mq.value[0] !== "string") {
return mq.value.map((i) => i.id);
}
}
return curr.data.value;
}
const parent = parentMap.get(curr);
if (parent) {
return findValue(parent, parentMap);
}
return undefined;
}
function sendTelemetryEvent(telemetryReporter, qvres, question, inputs) {
var _a;
if (qvres.isErr()) {
telemetryReporter === null || telemetryReporter === void 0 ? void 0 : telemetryReporter.sendTelemetryEvent(teamsfx_api_1.TelemetryEvent.askQuestion, {
[teamsfx_api_1.TelemetryProperty.answerType]: qvres.error.name,
[teamsfx_api_1.TelemetryProperty.question]: question.name,
[teamsfx_api_1.TelemetryProperty.platform]: inputs.platform,
[teamsfx_api_1.TelemetryProperty.stage]: inputs.stage ? inputs.stage : "",
});
}
else {
telemetryReporter === null || telemetryReporter === void 0 ? void 0 : telemetryReporter.sendTelemetryEvent(teamsfx_api_1.TelemetryEvent.askQuestion, {
[teamsfx_api_1.TelemetryProperty.answerType]: qvres.value.type,
[teamsfx_api_1.TelemetryProperty.question]: question.name,
[teamsfx_api_1.TelemetryProperty.answer]: question.type == "singleSelect" || question.type == "multiSelect"
? (_a = qvres.value.result) === null || _a === void 0 ? void 0 : _a.toString()
: "",
[teamsfx_api_1.TelemetryProperty.platform]: inputs.platform,
[teamsfx_api_1.TelemetryProperty.stage]: inputs.stage ? inputs.stage : "",
});
}
}
//# sourceMappingURL=visitor.js.map
File diff suppressed because one or more lines are too long