diff --git a/packages/vscode-extension/.eslintrc.js b/packages/vscode-extension/.eslintrc.js index 0be43f7f..7e2488c9 100644 --- a/packages/vscode-extension/.eslintrc.js +++ b/packages/vscode-extension/.eslintrc.js @@ -1,3 +1,5 @@ +const rules = require("@typescript-eslint/eslint-plugin").rules + module.exports = { "env": { "es6": true, @@ -15,6 +17,7 @@ module.exports = { "plugins": [ "@typescript-eslint" ], + "ignorePatterns": ['.eslintrc.js'], "rules": { "@typescript-eslint/member-delimiter-style": [ "warn", @@ -29,7 +32,23 @@ module.exports = { } } ], - "@typescript-eslint/naming-convention": "warn", + "@typescript-eslint/naming-convention": [ + "warn", + ...rules["naming-convention"].defaultOptions, + { + "selector": ["enumMember"], + "format": ["UPPER_CASE"] + }, + { + "selector": ["property"], + "format": ["camelCase", "UPPER_CASE"] + }, + { + "selector": ["variable"], + "modifiers": ["const"], + "format": ["camelCase", "UPPER_CASE", "PascalCase"] + } + ], "@typescript-eslint/no-unused-expressions": "warn", "@typescript-eslint/semi": [ "warn", diff --git a/packages/vscode-extension/messages.json b/packages/vscode-extension/messages.json index 1bc1e876..f408d1e9 100644 --- a/packages/vscode-extension/messages.json +++ b/packages/vscode-extension/messages.json @@ -99,6 +99,9 @@ "IMPORT_OBJECTS_QUESTIONS_SELECT_OBJECTS": "Select the object you want to import.", "IMPORT_OBJECTS_QUESTIONS_SELECT_TYPES": "Select the object types you want to import, or make no selection to list all object types.", + "LIST_AUTH_LOADING": "Loading the configured authentication IDs in this machine...", + "LIST_AUTH_SELECT": "Select the authentication ID to use", + "LIST_FILES_ERROR_NO_FILES_FOUND": "No files found.", "LIST_FILES_ERROR_NO_FOLDERS_FOUND": "No folders available.", "LIST_FILES_LISTING": "Listing files...", @@ -123,7 +126,7 @@ "MANAGE_AUTH_REMOVE_CANCEL": "Cancel", "MANAGE_AUTH_REMOVE_CONFIRMATION_MESSAGE": "This auth ID will be removed locally but still be valid in your account: {0}.", "MANAGE_AUTH_REMOVE_CONTINUE": "Continue", - "MANAGE_AUTH_REMOVE_REMOVING_AUTH_ID": "Removing auhtID...", + "MANAGE_AUTH_REMOVE_REMOVING_AUTH_ID": "Removing auth ID...", "MANAGE_AUTH_RENAME_ENTER_NEW_AUTH_ID": "Enter the new auth ID.", "MANAGE_AUTH_RENAME_RENAMING_AUTH_ID": "Renaming auth ID...", diff --git a/packages/vscode-extension/package-lock.json b/packages/vscode-extension/package-lock.json index a47fd2be..cfa83499 100644 --- a/packages/vscode-extension/package-lock.json +++ b/packages/vscode-extension/package-lock.json @@ -9,13 +9,13 @@ "version": "1.6.0", "license": "UPL-1.0", "dependencies": { - "@oracle/suitecloud-cli": "^1.8.0" + "@oracle/suitecloud-cli": "^1.9.0" }, "devDependencies": { "@types/glob": "8.1.0", "@types/mocha": "10.0.6", "@types/node": "20.10.7", - "@types/vscode": "1.85.0", + "@types/vscode": "1.80.0", "@typescript-eslint/eslint-plugin": "6.18.0", "@typescript-eslint/parser": "6.18.0", "@vscode/test-electron": "2.3.8", @@ -163,16 +163,16 @@ } }, "node_modules/@oracle/suitecloud-cli": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@oracle/suitecloud-cli/-/suitecloud-cli-1.8.0.tgz", - "integrity": "sha512-jCWPKjqZfNcgOs7SGccy84AcYGbPfIxojZeEWhCFTRRwHWbK+QdBS1verIR5hyAGIJvrzv6WLcDqpXOKcWVqig==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@oracle/suitecloud-cli/-/suitecloud-cli-1.9.0.tgz", + "integrity": "sha512-WSi7D4ODuvBTCS9lVnVF3RH0TSp1qSLNqNEPYDnlF93vbFqen2kJsyjFvBrJ0XLKcImtg2LimRWo58OToSi1gQ==", "hasInstallScript": true, "dependencies": { "chalk": "4.1.2", "cli-spinner": "0.2.10", - "commander": "11.0.0", - "inquirer": "8.2.5", - "xml2js": "0.6.0" + "commander": "11.1.0", + "inquirer": "8.2.6", + "xml2js": "0.6.2" }, "bin": { "suitecloud": "src/suitecloud.js" @@ -231,9 +231,9 @@ "dev": true }, "node_modules/@types/vscode": { - "version": "1.85.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.85.0.tgz", - "integrity": "sha512-CF/RBon/GXwdfmnjZj0WTUMZN5H6YITOfBCP4iEZlOtVQXuzw6t7Le7+cR+7JzdMrnlm7Mfp49Oj2TuSXIWo3g==", + "version": "1.80.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.80.0.tgz", + "integrity": "sha512-qK/CmOdS2o7ry3k6YqU4zD3R2AYlJfbwBoSbKpBoP+GpXNE+0NEgJOli4n0bm0diK5kfBnchgCEj4igQz/44Hg==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { @@ -866,9 +866,9 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/commander": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz", - "integrity": "sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", "engines": { "node": ">=16" } @@ -1587,9 +1587,9 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/inquirer": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.5.tgz", - "integrity": "sha512-QAgPDQMEgrDssk1XiwwHoOGYF9BAbUcc1+j+FhEvaOt8/cKRqyLn0U5qA6F74fGhTMGxf92pOvPBeh29jQJDTQ==", + "version": "8.2.6", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz", + "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==", "dependencies": { "ansi-escapes": "^4.2.1", "chalk": "^4.1.1", @@ -1605,12 +1605,25 @@ "string-width": "^4.1.0", "strip-ansi": "^6.0.0", "through": "^2.3.6", - "wrap-ansi": "^7.0.0" + "wrap-ansi": "^6.0.1" }, "engines": { "node": ">=12.0.0" } }, + "node_modules/inquirer/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -2666,6 +2679,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -2685,9 +2699,9 @@ "dev": true }, "node_modules/xml2js": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.0.tgz", - "integrity": "sha512-eLTh0kA8uHceqesPqSE+VvO1CDDJWMwlQfB6LuN6T8w6MaDJ8Txm8P7s5cHD0miF0V+GGTZrDQfxPZQVsur33w==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" diff --git a/packages/vscode-extension/src/commands/CompareFile.ts b/packages/vscode-extension/src/commands/CompareFile.ts index 569e1da2..c74111fe 100644 --- a/packages/vscode-extension/src/commands/CompareFile.ts +++ b/packages/vscode-extension/src/commands/CompareFile.ts @@ -10,8 +10,9 @@ import * as vscode from 'vscode'; import { Uri } from 'vscode'; import { ACP_UNRESTRICTED_FOLDERS } from '../ApplicationConstants'; import { COMPARE_FILE } from '../service/TranslationKeys'; -import { actionResultStatus, ApplicationConstants, ProjectInfoService } from '../util/ExtensionUtil'; +import { actionResultStatus, ApplicationConstants, ProjectInfoService, AuthenticationUtils } from '../util/ExtensionUtil'; import FileImportCommon from './FileImportCommon'; +import ListAuthService from '../service/ListAuthService'; export default class CompareFile extends FileImportCommon { private static readonly ACCOUNT_COPY_BASE_NAME = 'accountCopy'; @@ -46,11 +47,23 @@ export default class CompareFile extends FileImportCommon { } protected async execute() { + const listAuthService = new ListAuthService(this.messageService, this.translationService, this.rootWorkspaceFolder!); + const activeFilePath = this.activeFile!; const tempFolderPath = fs.mkdtempSync(path.join(os.tmpdir(), CompareFile.TEMP_FOLDER_PREFIX)); + const authIds = await listAuthService.getAuthIds(); + if(!authIds) { + return; + } + + const selectedAuthId = await listAuthService.selectAuthId(authIds); + if(!selectedAuthId) { + return; + } + this.copyManifestFileToTempFolder(tempFolderPath); - this.copyProjectJsonToTempFolder(tempFolderPath); + this.createProjectJsonInTempFolder(tempFolderPath, selectedAuthId.authId); const activeFileRelativePath = activeFilePath.split(this.getFileCabinetFolderPath())[1]?.replace(/\\/g, '/'); const importFilePath = this.getImportFilePath(tempFolderPath, activeFilePath, activeFileRelativePath); @@ -125,9 +138,8 @@ export default class CompareFile extends FileImportCommon { return path.join(importFileParentFolderPath, path.basename(activeFilePath)); } - private copyProjectJsonToTempFolder(tempFolderPath: string) { - const projectJsonPath = path.join(this.rootWorkspaceFolder!, ApplicationConstants.FILES.PROJECT_JSON); - fs.copyFileSync(projectJsonPath, path.join(tempFolderPath, ApplicationConstants.FILES.PROJECT_JSON)); + private createProjectJsonInTempFolder(tempFolderPath: string, authId: string) { + AuthenticationUtils.setDefaultAuthentication(tempFolderPath, authId); } private copyManifestFileToTempFolder(tempFolderPath: string) { diff --git a/packages/vscode-extension/src/commands/ManageAuth.ts b/packages/vscode-extension/src/commands/ManageAuth.ts index beb92e5e..c9193a88 100644 --- a/packages/vscode-extension/src/commands/ManageAuth.ts +++ b/packages/vscode-extension/src/commands/ManageAuth.ts @@ -1,16 +1,11 @@ -import { commands, QuickPickItem, window } from 'vscode'; -import { commandsInfoMap } from '../commandsMap'; -import { getSdkPath } from '../core/sdksetup/SdkProperties'; -import { COMMAND, EXTENSION_INSTALLATION, MANAGE_ACCOUNTS, MANAGE_AUTH } from '../service/TranslationKeys'; -import { ActionResult, AuthListData, ValidationResult } from '../types/ActionResult'; -import { AccountCredentialsFormatter, actionResultStatus, AuthenticationUtils, InteractiveAnswersValidator } from '../util/ExtensionUtil'; +import { QuickPickItem, window } from 'vscode'; +import { COMMAND, MANAGE_AUTH } from '../service/TranslationKeys'; +import { AuthListData, ValidationResult } from '../types/ActionResult'; +import { AccountCredentialsFormatter, actionResultStatus, InteractiveAnswersValidator } from '../util/ExtensionUtil'; import BaseAction from './BaseAction'; +import ListAuthService, { AuthIdItem } from '../service/ListAuthService'; -interface AuthIdItem extends QuickPickItem { - authId: string; -} - -interface ManageAuhtOptionItem extends QuickPickItem { +interface ManageAuthOptionItem extends QuickPickItem { manageOption: ManageAuthOption; } enum ManageAuthOption { @@ -39,88 +34,50 @@ export default class ManageAuth extends BaseAction { // AuthIDs management is independent from it this.vsConsoleLogger.hiddeInitialProjectFolerNameDetails(); - const authIDsMapPromise = AuthenticationUtils.getAuthIds(getSdkPath()); - this.messageService.showStatusBarMessage(this.translationService.getMessage(MANAGE_ACCOUNTS.LOADING), true, authIDsMapPromise); - const auhtIDsMapResult: ActionResult = await authIDsMapPromise; - - if (auhtIDsMapResult.isSuccess()) { - const auhtIDsMap = auhtIDsMapResult.data; - if (Object.keys(auhtIDsMap).length === 0) { - this.showNoAccountsWarningMessage(); - return; - } - const selectedAuhtID = await this.showAuthList(auhtIDsMap); - if (!selectedAuhtID) { - return; - } - - const selectedManageAuhtOption = await this.showManageAuthOptions(selectedAuhtID); - if (!selectedManageAuhtOption) { - return; - } + const listAuthService = new ListAuthService(this.messageService, this.translationService, this.rootWorkspaceFolder); - if (selectedManageAuhtOption.manageOption === ManageAuthOption.INFO) { - this.showInfo(selectedAuhtID, auhtIDsMap); - } else if (selectedManageAuhtOption.manageOption === ManageAuthOption.RENAME) { - this.renameAuthId(selectedAuhtID, auhtIDsMap); - } else if (selectedManageAuhtOption.manageOption === ManageAuthOption.REMOVE) { - this.removeAuhtId(selectedAuhtID); - } - } else { - this.vsConsoleLogger.error(auhtIDsMapResult.errorMessages[0]); - this.messageService.showCommandError(undefined, false); + const authIds = await listAuthService.getAuthIds(); + if(!authIds) { + return; } - return; - } - private showNoAccountsWarningMessage() { - const runSetupAccountButtonMessage = this.translationService.getMessage( - EXTENSION_INSTALLATION.PROJECT_STARTUP.BUTTONS.RUN_SUITECLOUD_SETUP_ACCOUNT, - commandsInfoMap.setupaccount.vscodeCommandName - ); - const noAccountWarningMessage = this.translationService.getMessage( - MANAGE_AUTH.GENERAL.NO_ACCOUNTS_TO_MANAGE, - commandsInfoMap.setupaccount.vscodeCommandName - ); - window.showWarningMessage(noAccountWarningMessage, runSetupAccountButtonMessage).then((result) => { - if (result === runSetupAccountButtonMessage) { - commands.executeCommand(commandsInfoMap.setupaccount.vscodeCommandId); - } - }); - } + const selectedAuthID = await listAuthService.selectAuthId(authIds); + if (!selectedAuthID) { + return; + } - private async showAuthList(authIDsMap: AuthListData) { - return window.showQuickPick(this.getAuthIDItems(authIDsMap), { - placeHolder: this.translationService.getMessage(MANAGE_AUTH.GENERAL.SELECT_AUTH_ID_TO_MANAGE), - ignoreFocusOut: true, - canPickMany: false, - }); - } + const selectedManageAuthOption = await this.showManageAuthOptions(selectedAuthID); + if (!selectedManageAuthOption) { + return; + } - private getAuthIDItems(authIDsMap: AuthListData): AuthIdItem[] { - return Object.keys(authIDsMap).map((authId) => ({ - label: `${authId} | ${authIDsMap[authId].accountInfo.roleName} @ ${authIDsMap[authId].accountInfo.companyName}`, - authId: authId, - })); + if (selectedManageAuthOption.manageOption === ManageAuthOption.INFO) { + this.showInfo(selectedAuthID, authIds); + } else if (selectedManageAuthOption.manageOption === ManageAuthOption.RENAME) { + this.renameAuthId(selectedAuthID, authIds); + } else if (selectedManageAuthOption.manageOption === ManageAuthOption.REMOVE) { + this.removeAuthId(selectedAuthID); + } + return; } - private async showManageAuthOptions(selectedAuhtID: AuthIdItem) { - const options: ManageAuhtOptionItem[] = [ + private async showManageAuthOptions(selectedAuthID: AuthIdItem) { + const options: ManageAuthOptionItem[] = [ { label: this.translationService.getMessage(MANAGE_AUTH.GENERAL.INFO_OPTION), manageOption: ManageAuthOption.INFO }, { label: this.translationService.getMessage(MANAGE_AUTH.GENERAL.RENAME_OPTION), manageOption: ManageAuthOption.RENAME }, { label: this.translationService.getMessage(MANAGE_AUTH.GENERAL.REMOVE_OPTION), manageOption: ManageAuthOption.REMOVE }, ]; return window.showQuickPick(options, { - placeHolder: this.translationService.getMessage(MANAGE_AUTH.GENERAL.SELECT_OPTION_FOR_AUTH_ID, selectedAuhtID.authId), + placeHolder: this.translationService.getMessage(MANAGE_AUTH.GENERAL.SELECT_OPTION_FOR_AUTH_ID, selectedAuthID.authId), ignoreFocusOut: true, canPickMany: false, }); } - private showInfo(selectedAuhtID: AuthIdItem, authList: AuthListData) { - const authIDInfo = authList[selectedAuhtID.authId]; + private showInfo(selectedAuthID: AuthIdItem, authList: AuthListData) { + const authIDInfo = authList[selectedAuthID.authId]; const accountCredentials = authIDInfo as any; - accountCredentials.authId = selectedAuhtID.authId; + accountCredentials.authId = selectedAuthID.authId; accountCredentials.domain = authIDInfo.urls.app; const authIDInfoMessage = AccountCredentialsFormatter.getInfoString(authIDInfo); @@ -129,29 +86,29 @@ export default class ManageAuth extends BaseAction { this.vsConsoleLogger.info(''); } - private async renameAuthId(selectedAuhtID: AuthIdItem, auhtIDsMap: AuthListData) { - const newAuhtID = await window.showInputBox({ + private async renameAuthId(selectedAuthID: AuthIdItem, authIds: AuthListData) { + const newAuthID = await window.showInputBox({ placeHolder: this.translationService.getMessage(MANAGE_AUTH.RENAME.ENTER_NEW_AUTH_ID), validateInput: (fieldValue: string) => { const validationResult = InteractiveAnswersValidator.showValidationResults( fieldValue, InteractiveAnswersValidator.validateFieldIsNotEmpty, InteractiveAnswersValidator.validateFieldHasNoSpaces, - (fieldValue: string) => InteractiveAnswersValidator.validateSameAuthID(fieldValue, selectedAuhtID.authId), - (fieldValue: string) => InteractiveAnswersValidator.validateAuthIDNotInList(fieldValue, Object.keys(auhtIDsMap)), + (fieldValue: string) => InteractiveAnswersValidator.validateSameAuthID(fieldValue, selectedAuthID.authId), + (fieldValue: string) => InteractiveAnswersValidator.validateAuthIDNotInList(fieldValue, Object.keys(authIds)), InteractiveAnswersValidator.validateAlphanumericHyphenUnderscore, InteractiveAnswersValidator.validateMaximumLength ); return typeof validationResult === 'string' ? validationResult : ''; }, }); - if (!newAuhtID) { + if (!newAuthID) { return; } const commandOptions: { [option: string]: string } = {}; - commandOptions.rename = selectedAuhtID.authId; - commandOptions.renameto = newAuhtID; + commandOptions.rename = selectedAuthID.authId; + commandOptions.renameto = newAuthID; const renameActionPromise = this.runSuiteCloudCommand(commandOptions, NON_EXISTING_PATH); const commandMessage = this.translationService.getMessage(COMMAND.TRIGGERED, this.vscodeCommandName); const statusBarMessage = this.translationService.getMessage(MANAGE_AUTH.RENAME.RENAMING_AUTH_ID); @@ -165,13 +122,13 @@ export default class ManageAuth extends BaseAction { } } - private async removeAuhtId(selectedAuhtID: AuthIdItem) { + private async removeAuthId(selectedAuthID: AuthIdItem) { const REMOVE_ANSWER = { CONTINUE: this.translationService.getMessage(MANAGE_AUTH.REMOVE.CONTINUE), CANCEL: this.translationService.getMessage(MANAGE_AUTH.REMOVE.CANCEL), }; const removeAnswer = await window.showQuickPick([REMOVE_ANSWER.CONTINUE, REMOVE_ANSWER.CANCEL], { - placeHolder: this.translationService.getMessage(MANAGE_AUTH.REMOVE.CONFIRMATION_MESSAGE, selectedAuhtID.authId), + placeHolder: this.translationService.getMessage(MANAGE_AUTH.REMOVE.CONFIRMATION_MESSAGE, selectedAuthID.authId), ignoreFocusOut: true, canPickMany: false, }); @@ -182,15 +139,15 @@ export default class ManageAuth extends BaseAction { if (removeAnswer === REMOVE_ANSWER.CONTINUE) { const commandOptions: { [option: string]: string } = {}; - commandOptions.remove = selectedAuhtID.authId; + commandOptions.remove = selectedAuthID.authId; const renameAuthIDActionPromise = this.runSuiteCloudCommand(commandOptions, NON_EXISTING_PATH); const commandMessage = this.translationService.getMessage(COMMAND.TRIGGERED, this.vscodeCommandName); const statusBarMessage = this.translationService.getMessage(MANAGE_AUTH.REMOVE.REMOVING_AUTH_ID); this.messageService.showInformationMessage(commandMessage, statusBarMessage, renameAuthIDActionPromise, true, false); - const renameAuhtIDActionResult = await renameAuthIDActionPromise; - if (renameAuhtIDActionResult.status === actionResultStatus.SUCCESS) { + const renameAuthIDActionResult = await renameAuthIDActionPromise; + if (renameAuthIDActionResult.status === actionResultStatus.SUCCESS) { this.messageService.showCommandInfo(undefined, false); } else { this.messageService.showCommandError(undefined, false); diff --git a/packages/vscode-extension/src/commands/SetupAccount.ts b/packages/vscode-extension/src/commands/SetupAccount.ts index 654d1974..9fd23821 100644 --- a/packages/vscode-extension/src/commands/SetupAccount.ts +++ b/packages/vscode-extension/src/commands/SetupAccount.ts @@ -5,42 +5,39 @@ import { actionResultStatus, AuthenticationUtils, InteractiveAnswersValidator } from '../util/ExtensionUtil'; import BaseAction from './BaseAction'; import { window, QuickPickItem, MessageItem } from 'vscode'; -import { AuthListData, ActionResult, AuthenticateActionResult } from '../types/ActionResult'; +import { AuthListData, AuthenticateActionResult, CancellationToken } from '../types/ActionResult'; import { getSdkPath } from '../core/sdksetup/SdkProperties'; import { MANAGE_ACCOUNTS, DISMISS } from '../service/TranslationKeys'; +import ListAuthService from '../service/ListAuthService'; const COMMAND_NAME = 'setupaccount'; -enum UiOption { - new_authid, - select_authid, - new_authid_browser, - new_authid_save_token, - cancel_process, - dismiss, +enum UIOptions { + NEW_AUTHID, + SELECT_AUTHID, + NEW_AUTHID_BROWSER, + NEW_AUTHID_TOKEN, + CANCEL, + DISMISS, } interface NewAuthIdItem extends QuickPickItem { - option: UiOption.new_authid; + option: UIOptions.NEW_AUTHID; } interface SelectAuthIdItem extends QuickPickItem { - option: UiOption.select_authid; + option: UIOptions.SELECT_AUTHID; authId: string; } interface MessageItemWithCode extends MessageItem { - code: UiOption.cancel_process | UiOption.dismiss; + code: UIOptions.CANCEL | UIOptions.DISMISS; } type AuthIdItem = NewAuthIdItem | SelectAuthIdItem; interface NewAuthID extends QuickPickItem { - option: UiOption.new_authid_browser | UiOption.new_authid_save_token; -} - -interface CancellationToken { - cancel?: (x: string) => void; + option: UIOptions.NEW_AUTHID_BROWSER | UIOptions.NEW_AUTHID_TOKEN; } export default class SetupAccount extends BaseAction { @@ -49,58 +46,54 @@ export default class SetupAccount extends BaseAction { } protected async execute(): Promise { - const accountsPromise = AuthenticationUtils.getAuthIds(getSdkPath()); - this.messageService.showStatusBarMessage(this.translationService.getMessage(MANAGE_ACCOUNTS.LOADING), true, accountsPromise); - const actionResult: ActionResult = await accountsPromise; - if (actionResult.isSuccess()) { - const selected = await this.getAuthListOption(actionResult.data); - if (!selected) { - return; - } else if (selected.option === UiOption.new_authid) { - await this.handleNewAuth(actionResult.data); - } else if (selected.option === UiOption.select_authid) { - this.handleSelectedAuth(selected.authId); - } - } else { - this.vsConsoleLogger.error(actionResult.errorMessages[0]); - this.messageService.showCommandError(); + const listAuthService = new ListAuthService(this.messageService, this.translationService, this.rootWorkspaceFolder!); + + const authIds = await listAuthService.getAuthIds(false); + if(!authIds) { + return; } - return; - } - private async getAuthListOption(data: AuthListData) { - return window.showQuickPick(this.getAuthOptions(data), { - placeHolder: this.translationService.getMessage(MANAGE_ACCOUNTS.SELECT_CREATE), - ignoreFocusOut: true, - canPickMany: false, - }); + const selected = await this.selectAuthId(authIds); + if (!selected) { + return; + } else if (selected.option === UIOptions.NEW_AUTHID) { + await this.handleNewAuth(authIds); + } else if (selected.option === UIOptions.SELECT_AUTHID) { + this.handleSelectedAuth(selected.authId); + } } - private getAuthOptions(authData: AuthListData): AuthIdItem[] { + private async selectAuthId(authData: AuthListData) { let options: AuthIdItem[] = [ { - option: UiOption.new_authid, + option: UIOptions.NEW_AUTHID, + description: "(default)", label: this.translationService.getMessage(MANAGE_ACCOUNTS.CREATE_NEW), }, ]; - Object.keys(authData).forEach((authId) => { + Object.entries(authData).forEach(([authId, info]) => { options.push({ - option: UiOption.select_authid, - label: `${authId} | ${authData[authId].accountInfo.roleName} @ ${authData[authId].accountInfo.companyName}`, + option: UIOptions.SELECT_AUTHID, + label: `${authId} | ${info.accountInfo.roleName} @ ${info.accountInfo.companyName}`, authId: authId, }); }); - return options; + + return window.showQuickPick(options, { + placeHolder: this.translationService.getMessage(MANAGE_ACCOUNTS.SELECT_CREATE), + ignoreFocusOut: true, + canPickMany: false, + }); } - private async handleNewAuth(accountCredentialsList: AuthListData) { + private async handleNewAuth(authIds: AuthListData) { const selected = await this.getNewAuthIdOption(); if (!selected) { return; - } else if (selected.option === UiOption.new_authid_browser) { - await this.handleBrowserAuth(accountCredentialsList); - } else if (selected.option === UiOption.new_authid_save_token) { - await this.handleSaveToken(accountCredentialsList); + } else if (selected.option === UIOptions.NEW_AUTHID_BROWSER) { + await this.handleBrowserAuth(authIds); + } else if (selected.option === UIOptions.NEW_AUTHID_TOKEN) { + await this.handleSaveToken(authIds); } } @@ -112,11 +105,11 @@ export default class SetupAccount extends BaseAction { try { let options: NewAuthID[] = [ { - option: UiOption.new_authid_browser, + option: UIOptions.NEW_AUTHID_BROWSER, label: this.translationService.getMessage(MANAGE_ACCOUNTS.CREATE.BROWSER), }, { - option: UiOption.new_authid_save_token, + option: UIOptions.NEW_AUTHID_TOKEN, label: this.translationService.getMessage(MANAGE_ACCOUNTS.CREATE.SAVE_TOKEN.OPTION), }, ]; @@ -130,29 +123,27 @@ export default class SetupAccount extends BaseAction { } } - private async handleBrowserAuth(accountCredentialsList: AuthListData) { - const authId = await this.getNewAuthId(accountCredentialsList); + private async handleBrowserAuth(authIds: AuthListData) { + const authId = await this.getNewAuthId(authIds); if (!authId) { return; } const url = await this.getUrl(); - const commandParams: { authid: string; url?: string } = { - authid: authId, - }; if (url === undefined) { return; } - if (url) { - commandParams.url = url; - } + const commandParams = { + authid: authId, + url + }; let cancellationToken: CancellationToken = {}; const dismissButton: MessageItemWithCode = { - code: UiOption.dismiss, + code: UIOptions.DISMISS, title: this.translationService.getMessage(DISMISS), }; const cancelButton: MessageItemWithCode = { - code: UiOption.cancel_process, + code: UIOptions.CANCEL, title: this.translationService.getMessage(MANAGE_ACCOUNTS.CREATE.BROWSER_CANCEL), }; @@ -160,13 +151,13 @@ export default class SetupAccount extends BaseAction { const authenticatePromise: Promise = AuthenticationUtils.authenticateWithOauth( commandParams, getSdkPath(), - this.rootWorkspaceFolder, + this.rootWorkspaceFolder!, cancellationToken ); window .showInformationMessage(this.translationService.getMessage(MANAGE_ACCOUNTS.CREATE.CONTINUE_IN_BROWSER), dismissButton, cancelButton) .then((x) => { - if (x?.code === UiOption.cancel_process) { + if (x?.code === UIOptions.CANCEL) { if (cancellationToken.cancel) { cancellationToken.cancel(this.translationService.getMessage(MANAGE_ACCOUNTS.CANCELED)); } @@ -183,7 +174,7 @@ export default class SetupAccount extends BaseAction { this.handleAuthenticateActionResult(actionResult); } - private async getNewAuthId(accountCredentialsList: AuthListData) { + private async getNewAuthId(authIds: AuthListData) { return window.showInputBox({ placeHolder: this.translationService.getMessage(MANAGE_ACCOUNTS.CREATE.ENTER_AUTH_ID), ignoreFocusOut: true, @@ -192,7 +183,7 @@ export default class SetupAccount extends BaseAction { fieldValue, InteractiveAnswersValidator.validateFieldIsNotEmpty, InteractiveAnswersValidator.validateFieldHasNoSpaces, - (fieldValue: string) => InteractiveAnswersValidator.validateAuthIDNotInList(fieldValue, Object.keys(accountCredentialsList)), + (fieldValue: string) => InteractiveAnswersValidator.validateAuthIDNotInList(fieldValue, Object.keys(authIds)), InteractiveAnswersValidator.validateAlphanumericHyphenUnderscore, InteractiveAnswersValidator.validateMaximumLength ); @@ -263,8 +254,8 @@ export default class SetupAccount extends BaseAction { }); } - private async handleSaveToken(accountCredentialsList: AuthListData) { - const authId = await this.getNewAuthId(accountCredentialsList); + private async handleSaveToken(authIds: AuthListData) { + const authId = await this.getNewAuthId(authIds); if (!authId) { return; } @@ -299,7 +290,7 @@ export default class SetupAccount extends BaseAction { commandParams.url = url; } - const saveTokenPromise = AuthenticationUtils.saveToken(commandParams, getSdkPath(), this.rootWorkspaceFolder); + const saveTokenPromise = AuthenticationUtils.saveToken(commandParams, getSdkPath(), this.rootWorkspaceFolder!); this.messageService.showStatusBarMessage( this.translationService.getMessage(MANAGE_ACCOUNTS.CREATE.SAVE_TOKEN.SAVING_TBA), true, diff --git a/packages/vscode-extension/src/service/ListAuthService.ts b/packages/vscode-extension/src/service/ListAuthService.ts new file mode 100644 index 00000000..5b865241 --- /dev/null +++ b/packages/vscode-extension/src/service/ListAuthService.ts @@ -0,0 +1,69 @@ +import * as vscode from 'vscode'; +import { AuthenticationUtils } from '../util/ExtensionUtil'; +import MessageService from './MessageService'; +import { LIST_AUTH } from './TranslationKeys'; +import { VSTranslationService } from './VSTranslationService'; +import { getSdkPath } from '../core/sdksetup/SdkProperties'; +import { AuthListData } from '../types/ActionResult'; +import { showSetupAccountWarningMessage } from '../startup/ShowSetupAccountWarning'; + +export interface AuthIdItem extends vscode.QuickPickItem { + authId: string; +} + +export default class ListAuthService { + private readonly messageService: MessageService; + private readonly translationService: VSTranslationService; + private rootProjectFolder?: string; + + constructor(messageService: MessageService, translationService: VSTranslationService, rootProjectFolder?: string) { + this.messageService = messageService; + this.translationService = translationService; + this.rootProjectFolder = rootProjectFolder; + } + + public async getAuthIds(showWarning: boolean = true): Promise { + const authIdsPromise = AuthenticationUtils.getAuthIds(getSdkPath()); + const statusBarMessage = this.translationService.getMessage(LIST_AUTH.LOADING); + this.messageService.showStatusBarMessage(statusBarMessage, true, authIdsPromise); + const authIdsResult = await authIdsPromise; + + if (authIdsResult.isSuccess()) { + const authIds: AuthListData = authIdsResult.data; + if (Object.keys(authIds).length === 0 && showWarning) { + showSetupAccountWarningMessage(); + return; + } + return authIds; + } + else { + throw authIdsResult.errorMessages; + } + } + + public async selectAuthId(authIds: AuthListData) { + const defaultAuthId = this.getDefaultAuthId(); + + const options = Object.entries(authIds).map(([authId, info]) => ({ + label: `${authId} | ${info.accountInfo.roleName} @ ${info.accountInfo.companyName}`, + description: defaultAuthId && defaultAuthId === authId ? "(default)" : "", + authId, + })); + + options.sort((a, b) => (a.description || "") > (b.description || "") ? -1 : 1); + + return vscode.window.showQuickPick(options, { + placeHolder: this.translationService.getMessage(LIST_AUTH.SELECT), + ignoreFocusOut: true, + canPickMany: false, + }); + } + + private getDefaultAuthId() { + try { + return AuthenticationUtils.getProjectDefaultAuthId(this.rootProjectFolder); + } catch(e) { + return undefined; + } + } +} \ No newline at end of file diff --git a/packages/vscode-extension/src/service/TranslationKeys.ts b/packages/vscode-extension/src/service/TranslationKeys.ts index 03bbe18b..7349b123 100644 --- a/packages/vscode-extension/src/service/TranslationKeys.ts +++ b/packages/vscode-extension/src/service/TranslationKeys.ts @@ -170,6 +170,11 @@ export const IMPORT_OBJECTS = { }, }; +export const LIST_AUTH = { + LOADING: 'LIST_AUTH_LOADING', + SELECT: 'LIST_AUTH_SELECT' +}; + export const LIST_FILES = { ERROR: { NO_FILES_FOUND: 'LIST_FILES_ERROR_NO_FILES_FOUND', @@ -252,7 +257,7 @@ export const STATUS_BARS = { SUITECLOUD_PROJECT: { TOOLTIP: 'STATUS_BARS_SUITECLOUD_PROJECT_TOOLTIP' } -} +}; export const UPLOAD_FILE = { ERROR: { diff --git a/packages/vscode-extension/src/types/ActionResult.d.ts b/packages/vscode-extension/src/types/ActionResult.d.ts index 948c8b55..a51f6519 100644 --- a/packages/vscode-extension/src/types/ActionResult.d.ts +++ b/packages/vscode-extension/src/types/ActionResult.d.ts @@ -21,6 +21,11 @@ export interface AuthenticateActionResult extends ActionResult { }; } +export type AuthenticateParams = { + authid: string; + url?: string; +}; + export type AuthListData = { [authID: string]: { accountInfo: { @@ -40,4 +45,8 @@ export type AuthListData = { }; }; -export type ValidationResult = { valid: false; message: string } | { valid: true }; +export interface CancellationToken { + cancel?: (x: string) => void; +} + +export type ValidationResult = { valid: false; message: string } | { valid: true }; \ No newline at end of file diff --git a/packages/vscode-extension/src/util/ExtensionUtil.ts b/packages/vscode-extension/src/util/ExtensionUtil.ts index 16bcb831..d1bd6adf 100644 --- a/packages/vscode-extension/src/util/ExtensionUtil.ts +++ b/packages/vscode-extension/src/util/ExtensionUtil.ts @@ -2,7 +2,7 @@ ** Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. ** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. */ -import { ActionResult, AuthListData } from '../types/ActionResult'; +import { ActionResult, AuthListData, AuthenticateActionResult, AuthenticateParams, CancellationToken } from '../types/ActionResult'; export const ApplicationConstants = require('@oracle/suitecloud-cli/src/ApplicationConstants'); export const ExecutionEnvironmentContext = require('@oracle/suitecloud-cli/src/ExecutionEnvironmentContext'); @@ -33,8 +33,11 @@ export const ProjectInfoService = require('@oracle/suitecloud-cli/src/services/P export const TranslationService = require('@oracle/suitecloud-cli/src/services/TranslationService'); export const AuthenticationUtils: { [key: string]: any; - getProjectDefaultAuthId(projectFolder?: string): string; + authenticateWithOauth(params: AuthenticateParams, sdkPath: string, projectFolder: string, cancelToken?: CancellationToken, executionEnvironmentContext?: typeof ExecutionEnvironmentContext): Promise; getAuthIds(sdkPath: string): Promise>; + getProjectDefaultAuthId(projectFolder?: string): string; + saveToken(params: AuthenticateParams, sdkPath: string, projectFolder: string, executionEnvironmentContext?: typeof ExecutionEnvironmentContext): Promise; + setDefaultAuthentication(projectFolder: string, authId: string): void; } = require('@oracle/suitecloud-cli/src/utils/AuthenticationUtils'); export const AccountCredentialsFormatter: { getInfoString(accountCredentials: any): string;