Skip to content

Commit

Permalink
fix: split PrerequisitesValidationError into Error types of finer gra…
Browse files Browse the repository at this point in the history
…nularity (#12522)

* feat: refactor dep checker errors

* feat: refactor dep checker errors

* refactor: clean up

* refactor: clean up

* refactor: clean up

* refactor: clean up

* refactor: clean up

* test: ut

* test: ut

* test: ut

* test: ut

* test: ut

* test: ut

* test: ut

* test: ut

* test: ut

* test: ut
  • Loading branch information
jayzhang authored Oct 17, 2024
1 parent fe7eea6 commit 66e1a3b
Show file tree
Hide file tree
Showing 29 changed files with 394 additions and 297 deletions.
9 changes: 9 additions & 0 deletions packages/api/src/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export interface FxError extends Error {

categories?: string[];

telemetryProperties?: Record<string, string>;

/**
* recommended operation for user to fix the error
* e.g. "debug-in-test-tool"
Expand All @@ -37,6 +39,7 @@ export interface ErrorOptionBase {
userData?: any;
displayMessage?: string;
categories?: string[];
telemetryProperties?: Record<string, string>;
/**
* whether to skip process (such as mask secret tokens) in telemetry collection
*/
Expand Down Expand Up @@ -81,6 +84,8 @@ export class UserError extends Error implements FxError {

categories?: string[];

telemetryProperties?: Record<string, string>;

/**
* whether to skip process (such as mask secret tokens) in telemetry collection
*/
Expand Down Expand Up @@ -138,6 +143,7 @@ export class UserError extends Error implements FxError {
this.timestamp = new Date();
this.categories = option.categories;
this.skipProcessInTelemetry = option.skipProcessInTelemetry;
this.telemetryProperties = option.telemetryProperties;
}
}

Expand Down Expand Up @@ -173,6 +179,8 @@ export class SystemError extends Error implements FxError {

categories?: string[];

telemetryProperties?: Record<string, string>;

/**
* whether to skip process (such as mask secret tokens) in telemetry collection
*/
Expand Down Expand Up @@ -230,5 +238,6 @@ export class SystemError extends Error implements FxError {
this.timestamp = new Date();
this.categories = option.categories;
this.skipProcessInTelemetry = option.skipProcessInTelemetry;
this.telemetryProperties = option.telemetryProperties;
}
}
20 changes: 11 additions & 9 deletions packages/fx-core/resource/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -641,8 +641,6 @@
"plugins.bot.CheckLogAndFix": "Please check log-in Output panel and try to fix this issue.",
"plugins.bot.AppStudioBotRegistration": "Developer Portal bot registration",
"plugins.function.getTemplateFromLocal": "Unable to get latest template from GitHub, trying to use the local template.",
"error.depChecker.DefaultErrorMessage": "Install required dependencies manually.",
"depChecker.learnMoreButtonText": "Get more info",
"depChecker.needInstallNpm": "You must have NPM installed to debug your local functions.",
"depChecker.failToValidateFuncCoreTool": "Unable to validate Azure Functions Core Tools after installation.",
"depChecker.symlinkDirAlreadyExist": "Symlink (%s) destination already exists, remove it and try again.",
Expand All @@ -657,13 +655,9 @@
"depChecker.useGlobalDotnet": "Using dotnet from PATH:",
"depChecker.dotnetInstallStderr": "dotnet-install command failed without error exit code but with non-empty standard error.",
"depChecker.dotnetInstallErrorCode": "dotnet-install command failed.",
"depChecker.NodeNotFound": "Cannot find Node.js. The supported node versions are specified in the package.json. Go to %s to install a supported Node.js. Restart all your Visual Studio Code instances after the installation is finished.",
"depChecker.V3NodeNotSupported": "Node.js (%s) is not the officially supported version (%s). Your project may continue to work but we recommend to install the supported version. The supported node versions are specified in the package.json. Go to %s to install a supported Node.js.",
"depChecker.NodeNotLts": "Node.js (%s) is not a LTS version (%s). Go to %s to install a LTS Node.js.",
"depChecker.dotnetNotFound": "Unable to find @NameVersion. To know why .NET SDK is needed, refer @HelpLink",
"depChecker.depsNotFound": "Unable to find @SupportedPackages.\n\nTeams Toolkit requires these dependencies.\n\nClick \"Install\" to install @InstallPackages.",
"depChecker.linuxDepsNotFound": "Unable to find @SupportedPackages. Install @SupportedPackages manually and restart Visual Studio Code.",
"depChecker.linuxDepsNotFoundHelpLinkMessage": "Unable to find @SupportedPackages.\n\nTeams Toolkit requires these dependencies.",
"depChecker.failToDownloadFromUrl": "Unable to download file from '@Url', HTTP status '@Status'.",
"depChecker.failToValidateVxTestAppInstallOptions": "Invalid argument for video extensibility test app prerequisites checker. Please review tasks.json file to ensure all arguments are correctly formatted and valid.",
"depChecker.failToValidateVxTestApp": "Unable to validate video extensibility test app after installation.",
Expand Down Expand Up @@ -821,7 +815,7 @@
"error.yaml.InvalidYmlActionNameError": "Action '%s' not found, yaml file: %s",
"error.yaml.LifeCycleUndefinedError": "Lifecycle '%s' is undefined, yaml file: %s",
"error.yaml.InvalidActionInputError": "The '%s' action cannot be completed as the following parameter(s): %s, are either missing or have an invalid value in the provided yaml file: %s. Ensure that the required parameters are provided and have valid values and try again.",
"error.common.InstallSoftwareError": "Unable to install %s. You can install it manually and restart Visual Studio Code if you are using the Toolkit in Visual Studio Code.",
"error.common.InstallSoftwareError": "Unable to install %s. You can install manually and restart Visual Studio Code if you are using the Toolkit in Visual Studio Code.",
"error.common.VersionError": "Unable to find a version satisfying the version range %s.",
"error.common.MissingEnvironmentVariablesError": "Missing environment variables '%s' for file: %s. Please edit the .env file '%s' or '%s', or adjust system environment variables. For new Teams Toolkit projects, make sure you've run provision or debug to set these variables correctly.",
"error.common.InvalidProjectError": "This command only works for project created by Teams Toolkit. 'teamsapp.yml' or 'teamsapp.local.yml' not found",
Expand Down Expand Up @@ -921,5 +915,13 @@
"driver.oauth.log.skipUpdateOauth": "Skip updating OAuth registration as the same property exists.",
"driver.oauth.confirm.update": "The following parameters will be updated:\n%s\nDo you want to continue?",
"driver.oauth.log.successUpdateOauth": "OAuth registration updated successfully!",
"driver.oauth.info.update": "OAuth registration updated successfully! The following parameters have been updated:\n%s"
}
"driver.oauth.info.update": "OAuth registration updated successfully! The following parameters have been updated:\n%s",
"error.dep.PortsConflictError": "Port occupation check failed. Candidate ports to check: %s. The following ports are occupied: %s. Please close them and try again.",
"error.dep.SideloadingDisabledError": "Your Microsoft 365 account admin hasn't enabled custom app upload permission.\n· Contact your Teams admin to fix this. Visit: https://docs.microsoft.com/en-us/microsoftteams/platform/m365-apps/prerequisites\n· For help, visit the Microsoft Teams documentation. To create a free testing tenant, click \"Custom App Upload Disabled\" label under your account.",
"error.dep.CopilotDisabledError": "Microsoft 365 account administrator hasn't enabled Copilot access for this account. Contact your Teams administrator to resolve this issue by enrolling in Microsoft 365 Copilot Early Access program. Visit: https://aka.ms/PluginsEarlyAccess",
"error.dep.NodejsNotFoundError": "Unable to find Node.js. Go to https://nodejs.org to install LTS Node.js.",
"error.dep.NodejsNotLtsError": "Node.js (%s) is not a LTS version (%s). Go to https://nodejs.org to install LTS Node.js.",
"error.dep.NodejsNotRecommendedError": "Node.js (%s) is not the officially supported version (%s). Your project may continue to work but we recommend to install the supported version. The supported node versions are specified in the package.json. Go to https://nodejs.org to install a supported Node.js.",
"error.dep.VxTestAppInvalidInstallOptionsError": "Invalid argument for video extensibility test app prerequisites checker. Please review tasks.json file to ensure all arguments are correctly formatted and valid.",
"error.dep.VxTestAppValidationError": "Unable to validate video extensibility test app after installation."
}
4 changes: 4 additions & 0 deletions packages/fx-core/src/common/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@ class TelemetryUtils {
props[TelemetryProperty.ErrorCat2] = error.categories[1];
props[TelemetryProperty.ErrorCat3] = error.categories[2];
}

if (error.telemetryProperties) {
assign(props, error.telemetryProperties);
}
}

fillinProjectTypeProperties(props: Record<string, string>, projectTypeRes: ProjectTypeResult) {
Expand Down
28 changes: 0 additions & 28 deletions packages/fx-core/src/component/deps-checker/constant/message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@ import { getDefaultString, getLocalizedString } from "../../../common/localizeUt
import { nodeInstallationLink } from "./helpLink";

export const Messages = {
// learnMoreButtonText: getLocalizedString("depChecker.learnMoreButtonText"),
defaultErrorMessage: () => [
getDefaultString("error.depChecker.DefaultErrorMessage"),
getLocalizedString("error.depChecker.DefaultErrorMessage"),
],
needInstallNpm: () => getLocalizedString("depChecker.needInstallNpm"),
failToValidateFuncCoreTool: () => getLocalizedString("depChecker.failToValidateFuncCoreTool"),
portableFuncNodeNotMatched: (nodeVersion: string, funcVersion: string) =>
Expand All @@ -30,25 +25,6 @@ export const Messages = {
dotnetInstallStderr: () => getLocalizedString("depChecker.dotnetInstallStderr"),
dotnetInstallErrorCode: () => getLocalizedString("depChecker.dotnetInstallErrorCode"),

NodeNotFound: () => getLocalizedString("depChecker.NodeNotFound", nodeInstallationLink),

// In v3, the message will be displayed in the output.
// TODO: add localized string to FxError.displayMessage
V3NodeNotSupported: (currentVersion: string, supportedVersions: string) =>
getDefaultString(
"depChecker.V3NodeNotSupported",
currentVersion,
supportedVersions,
nodeInstallationLink
),
NodeNotLts: (currentVersion: string, supportedVersions: string) =>
getDefaultString(
"depChecker.NodeNotLts",
currentVersion,
supportedVersions,
nodeInstallationLink
),

dotnetNotFound: () => getLocalizedString("depChecker.dotnetNotFound"),
// depsNotFound: () => getLocalizedString("depChecker.depsNotFound"),

Expand All @@ -64,8 +40,4 @@ export const Messages = {
failToDownloadFromUrl: () => getLocalizedString("depChecker.failToDownloadFromUrl"),

linuxDepsNotFound: () => getLocalizedString("depChecker.linuxDepsNotFound"),

// linuxDepsNotFoundHelpLinkMessage: () => getLocalizedString(
// "depChecker.linuxDepsNotFoundHelpLinkMessage"
// ),
};
4 changes: 2 additions & 2 deletions packages/fx-core/src/component/deps-checker/depsChecker.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import { DepsCheckerError } from "./depsError";
import { UserError } from "@microsoft/teamsfx-api";

export interface DepsChecker {
getInstallationInfo(installOptions?: InstallOptions): Promise<DependencyStatus>;
Expand All @@ -20,7 +20,7 @@ export type DependencyStatus = {
binFolders?: string[];
};
telemetryProperties?: { [key: string]: string };
error?: DepsCheckerError;
error?: UserError;
};

export interface DepsInfo {
Expand Down
53 changes: 0 additions & 53 deletions packages/fx-core/src/component/deps-checker/depsError.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/fx-core/src/component/deps-checker/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

export * from "./checkerFactory";
export * from "./depsChecker";
export * from "./depsError";
export * from "./depsLogger";
export * from "./depsManager";
export * from "./depsTelemetry";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import * as os from "os";
import { ConfigFolderName, UserError } from "@microsoft/teamsfx-api";
import * as child_process from "child_process";
import * as fs from "fs-extra";
import * as os from "os";
import * as path from "path";
import * as child_process from "child_process";
import * as util from "util";
import { ConfigFolderName } from "@microsoft/teamsfx-api";
import { performance } from "perf_hooks";
import { dotnetFailToInstallHelpLink, dotnetExplanationHelpLink } from "../constant/helpLink";
import { DepsCheckerError, LinuxNotSupportedError } from "../depsError";
import { runWithProgressIndicator } from "../util/progressIndicator";
import { cpUtils } from "../util/cpUtils";
import { isLinux, isWindows, isArm64, isMacOS } from "../util/system";
import * as util from "util";
import { getLocalizedString } from "../../../common/localizeUtils";
import { DepsCheckerError, InstallSoftwareError } from "../../../error";
import { getResourceFolder } from "../../../folder";
import { dotnetExplanationHelpLink, dotnetFailToInstallHelpLink } from "../constant/helpLink";
import { Messages } from "../constant/message";
import { DepsCheckerEvent, TelemtryMessages } from "../constant/telemetry";
import { DependencyStatus, DepsChecker, DepsType } from "../depsChecker";
import { DepsLogger } from "../depsLogger";
import { DepsTelemetry } from "../depsTelemetry";
import { DepsChecker, DependencyStatus, DepsType } from "../depsChecker";
import { Messages } from "../constant/message";
import { getResourceFolder } from "../../../folder";
import { getLocalizedString } from "../../../common/localizeUtils";
import { cpUtils } from "../util/cpUtils";
import { runWithProgressIndicator } from "../util/progressIndicator";
import { isArm64, isLinux, isMacOS, isWindows } from "../util/system";

const execFile = util.promisify(child_process.execFile);

Expand Down Expand Up @@ -48,10 +48,7 @@ export class DotnetChecker implements DepsChecker {
this._telemetry = telemetry;
}

public async getDepsInfo(
isInstalled: boolean,
error?: DepsCheckerError
): Promise<DependencyStatus> {
public async getDepsInfo(isInstalled: boolean, error?: UserError): Promise<DependencyStatus> {
const execPath = await this.getDotnetExecPathFromConfig();
return {
name: DotnetCoreSDKName,
Expand Down Expand Up @@ -108,7 +105,7 @@ export class DotnetChecker implements DepsChecker {
} catch (error) {
this._logger.printDetailLog();
this._logger.error(`${error.message as string}, error = '${error.toString() as string}'`);
if (error instanceof DepsCheckerError) {
if (error instanceof UserError) {
return await this.getDepsInfo(false, error);
}
return await this.getDepsInfo(
Expand All @@ -122,10 +119,7 @@ export class DotnetChecker implements DepsChecker {

public async install(): Promise<void> {
if (isLinux()) {
throw new LinuxNotSupportedError(
Messages.linuxDepsNotFound().split("@SupportedPackages").join(installedNameWithVersion),
dotnetExplanationHelpLink
);
throw new InstallSoftwareError(installedNameWithVersion, dotnetExplanationHelpLink);
}

this._logger.debug(`[start] cleanup bin/dotnet and config`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@
/**
* @author Xiaofu Huang <[email protected]>
*/
import { ConfigFolderName, err, ok, Result, UserError } from "@microsoft/teamsfx-api";
import * as fs from "fs-extra";
import * as os from "os";
import * as path from "path";
import semver from "semver";
import * as uuid from "uuid";
import { ConfigFolderName, err, ok, Result } from "@microsoft/teamsfx-api";
import { getLocalizedString } from "../../../common/localizeUtils";
import { v3DefaultHelpLink, v3NodeNotFoundHelpLink } from "../constant/helpLink";
import { DepsCheckerError, InstallSoftwareError, NodejsNotFoundError } from "../../../error";
import { v3DefaultHelpLink } from "../constant/helpLink";
import { Messages } from "../constant/message";
import { TelemetryProperties } from "../constant/telemetry";
import { DependencyStatus, DepsChecker, DepsType, FuncInstallOptions } from "../depsChecker";
import { DepsCheckerError, LinuxNotSupportedError, NodeNotFoundError } from "../depsError";
import { DepsLogger } from "../depsLogger";
import { DepsTelemetry } from "../depsTelemetry";
import { cpUtils } from "../util/cpUtils";
import { createSymlink, rename, unlinkSymlink } from "../util/fileHelper";
import { isLinux, isWindows } from "../util/system";
import { NodeChecker } from "./nodeChecker";
import { TelemetryProperties } from "../constant/telemetry";

type FuncVersion = {
majorVersion: number;
Expand Down Expand Up @@ -51,7 +51,7 @@ export class FuncToolChecker implements DepsChecker {
public async getDepsInfo(
funcVersion: FuncVersion | undefined,
binFolder: string | undefined,
error?: DepsCheckerError
error?: UserError
): Promise<DependencyStatus> {
return Promise.resolve({
name: funcToolName,
Expand Down Expand Up @@ -97,7 +97,7 @@ export class FuncToolChecker implements DepsChecker {

return installationInfo;
} catch (error) {
if (error instanceof DepsCheckerError) {
if (error instanceof UserError) {
return await this.getDepsInfo(undefined, undefined, error);
}
return await this.getDepsInfo(
Expand Down Expand Up @@ -156,7 +156,7 @@ export class FuncToolChecker implements DepsChecker {
private async getNodeVersion(): Promise<string> {
const nodeVersion = (await NodeChecker.getInstalledNodeVersion())?.majorVersion;
if (!nodeVersion) {
throw new NodeNotFoundError(Messages.NodeNotFound(), v3NodeNotFoundHelpLink);
throw new NodejsNotFoundError();
}
return nodeVersion;
}
Expand Down Expand Up @@ -296,10 +296,7 @@ export class FuncToolChecker implements DepsChecker {
symlinkDir: string | undefined
): Promise<DependencyStatus> {
if (isLinux()) {
throw new LinuxNotSupportedError(
Messages.linuxDepsNotFound().split("@SupportedPackages").join(funcToolName),
v3DefaultHelpLink
);
throw new InstallSoftwareError(funcToolName, v3DefaultHelpLink);
}
if (!(await this.hasNPM())) {
throw new DepsCheckerError(Messages.needInstallNpm(), v3DefaultHelpLink);
Expand Down
Loading

0 comments on commit 66e1a3b

Please sign in to comment.