From e82840895a6cb380f11c55c4625866cc1d2fb3cd Mon Sep 17 00:00:00 2001 From: Jon Gellin Date: Wed, 18 Sep 2024 12:00:55 -0400 Subject: [PATCH 1/4] feat: command log --- .../src/commands/index.ts | 107 +++++-------- .../src/commands/util/commandLog.ts | 64 ++++++++ .../src/commands/util/commandLogEntry.ts | 12 ++ .../src/commands/util/commandWrapper.ts | 21 +++ .../src/commands/util/index.ts | 8 +- .../src/context/workspaceContext.ts | 7 + .../salesforcedx-vscode-core/src/index.ts | 141 +++++++++--------- 7 files changed, 221 insertions(+), 139 deletions(-) create mode 100644 packages/salesforcedx-vscode-core/src/commands/util/commandLog.ts create mode 100644 packages/salesforcedx-vscode-core/src/commands/util/commandLogEntry.ts create mode 100644 packages/salesforcedx-vscode-core/src/commands/util/commandWrapper.ts diff --git a/packages/salesforcedx-vscode-core/src/commands/index.ts b/packages/salesforcedx-vscode-core/src/commands/index.ts index ef0fe77c70..37d2997693 100644 --- a/packages/salesforcedx-vscode-core/src/commands/index.ts +++ b/packages/salesforcedx-vscode-core/src/commands/index.ts @@ -17,117 +17,89 @@ export { } from './auth/authParamsGatherer'; export { orgLoginAccessToken } from './auth/orgLoginAccessToken'; export { - DeviceCodeResponse, - OrgLoginWebContainerExecutor, + createOrgLoginWebExecutor, DeviceCodeResponse, orgLoginWeb, OrgLoginWebContainerExecutor, OrgLoginWebDemoModeExecutor, - OrgLoginWebExecutor, - createOrgLoginWebExecutor, - orgLoginWeb + OrgLoginWebExecutor } from './auth/orgLoginWeb'; export { AuthDevHubParams, - AuthDevHubParamsGatherer, - OrgLoginWebDevHubContainerExecutor, + AuthDevHubParamsGatherer, createAuthDevHubExecutor, + orgLoginWebDevHub, OrgLoginWebDevHubContainerExecutor, OrgLoginWebDevHubDemoModeExecutor, - OrgLoginWebDevHubExecutor, - createAuthDevHubExecutor, - orgLoginWebDevHub + OrgLoginWebDevHubExecutor } from './auth/orgLoginWebDevHub'; export { OrgLogoutAll, orgLogoutAll, orgLogoutDefault } from './auth/orgLogout'; export { ConfigList, configList } from './configList'; -export { ConfigSetExecutor, configSet } from './configSet'; +export { configSet, ConfigSetExecutor } from './configSet'; export { dataQuery } from './dataQuery'; export { - DebuggerSessionDetachExecutor, - IdGatherer, + DebuggerSessionDetachExecutor, debuggerStop, IdGatherer, IdSelection, - StopActiveDebuggerSessionExecutor, - debuggerStop + StopActiveDebuggerSessionExecutor } from './debuggerStop'; export { - ConfirmationAndSourcePathGatherer, - DeleteSourceExecutor, - ManifestChecker, - deleteSource + ConfirmationAndSourcePathGatherer, deleteSource, DeleteSourceExecutor, + ManifestChecker } from './deleteSource'; -export { projectGenerateManifest } from './projectGenerateManifest'; -export { DescribeMetadataExecutor, describeMetadata } from './describeMetadata'; -export { ListMetadataExecutor, listMetadata } from './listMetadata'; -export { - PackageInstallExecutor, - SelectInstallationKey, - SelectPackageID, - packageInstall -} from './packageInstall'; -export { - RefreshSObjectsExecutor, - checkSObjectsAndRefresh, - refreshSObjects, - initSObjectDefinitions -} from './refreshSObjects'; -export { renameLightningComponent } from './renameLightningComponent'; export { deployManifest } from './deployManifest'; export { - LibraryDeploySourcePathExecutor, - deploySourcePaths + deploySourcePaths, LibraryDeploySourcePathExecutor } from './deploySourcePath'; -export { sourceDiff, sourceFolderDiff, handleCacheResults } from './sourceDiff'; -export { retrieveManifest } from './retrieveManifest'; -export { retrieveComponent } from './retrieveMetadata'; -export { - LibraryRetrieveSourcePathExecutor, - SourcePathChecker, - retrieveSourcePaths -} from './retrieveSourcePath'; +export { describeMetadata, DescribeMetadataExecutor } from './describeMetadata'; +export { listMetadata, ListMetadataExecutor } from './listMetadata'; export { openDocumentation } from './openDocumentation'; -export { AliasGatherer, OrgCreateExecutor, orgCreate } from './orgCreate'; +export { AliasGatherer, orgCreate, OrgCreateExecutor } from './orgCreate'; export { orgDelete } from './orgDelete'; export { OrgDisplay, orgDisplay } from './orgDisplay'; export { orgList } from './orgList'; export { - OrgOpenContainerExecutor, - OrgOpenExecutor, getExecutor, - orgOpen + orgOpen, OrgOpenContainerExecutor, + OrgOpenExecutor } from './orgOpen'; export { - ProjectDeployStartExecutor, - projectDeployStart + packageInstall, PackageInstallExecutor, + SelectInstallationKey, + SelectPackageID +} from './packageInstall'; +export { + projectDeployStart, ProjectDeployStartExecutor } from './projectDeployStart'; export { - PathExistsChecker, - ProjectNameAndPathAndTemplate, - ProjectTemplateItem, + PathExistsChecker, projectGenerateWithManifest, ProjectNameAndPathAndTemplate, projectTemplateEnum, ProjectTemplateItem, SelectProjectFolder, SelectProjectName, - SelectProjectTemplate, - projectGenerateWithManifest, - projectTemplateEnum, - sfProjectGenerate + SelectProjectTemplate, sfProjectGenerate } from './projectGenerate'; +export { projectGenerateManifest } from './projectGenerateManifest'; export { - ProjectRetrieveStartExecutor, - projectRetrieveStart + projectRetrieveStart, ProjectRetrieveStartExecutor } from './projectRetrieveStart'; +export { + checkSObjectsAndRefresh, initSObjectDefinitions, refreshSObjects, RefreshSObjectsExecutor +} from './refreshSObjects'; +export { renameLightningComponent } from './renameLightningComponent'; +export { retrieveManifest } from './retrieveManifest'; +export { retrieveComponent } from './retrieveMetadata'; +export { + LibraryRetrieveSourcePathExecutor, retrieveSourcePaths, SourcePathChecker +} from './retrieveSourcePath'; export { viewAllChanges, viewLocalChanges, viewRemoteChanges } from './source/viewChanges'; +export { handleCacheResults, sourceDiff, sourceFolderDiff } from './sourceDiff'; export { CreateDebugLevel, CreateTraceFlag, QueryTraceFlag, - QueryUser, - StartApexDebugLoggingExecutor, + QueryUser, startApexDebugLogging, StartApexDebugLoggingExecutor, UpdateDebugLevelsExecutor, - UpdateTraceFlagsExecutor, - startApexDebugLogging + UpdateTraceFlagsExecutor } from './startApexDebugLogging'; export { - StopApexDebugLoggingExecutor, - stopApexDebugLogging, - turnOffLogging + stopApexDebugLogging, StopApexDebugLoggingExecutor, turnOffLogging } from './stopApexDebugLogging'; export { taskStop } from './taskStop'; export { @@ -149,5 +121,6 @@ export { visualforceGenerateComponent, visualforceGeneratePage } from './templates'; +export { CommandLogEntry, getCommandLog, getLastCommandLogEntry, logCommand, registerCommand } from './util'; import { DeveloperLogTraceFlag } from '../traceflag/developerLogTraceFlag'; export const developerLogTraceFlag = DeveloperLogTraceFlag.getInstance(); diff --git a/packages/salesforcedx-vscode-core/src/commands/util/commandLog.ts b/packages/salesforcedx-vscode-core/src/commands/util/commandLog.ts new file mode 100644 index 0000000000..31d75f6418 --- /dev/null +++ b/packages/salesforcedx-vscode-core/src/commands/util/commandLog.ts @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +import { WorkspaceContext } from '../../context'; +import { CommandLogEntry } from './commandLogEntry'; + +export const logCommand = async (commandId: string, duration: number): Promise => { + await CommandLog.getInstance().logCommand(commandId, duration); +}; + +export const getCommandLog = (commandIdFilter?: string): CommandLogEntry[] => { + return CommandLog.getInstance().getCommandLog(commandIdFilter); +}; + +export const getLastCommandLogEntry = (commandIdFilter?: string): CommandLogEntry | undefined => { + const commandLog = CommandLog.getInstance().getCommandLog(commandIdFilter); + return commandLog.length > 0 ? commandLog[commandLog.length - 1] : undefined; +}; + +class CommandLog { + private static instance: CommandLog; + private static STORAGE_KEY = 'COMMAND_LOG'; + private static MAX_LOG_ENTRIES = 1000; + + private commandLogEntries: CommandLogEntry[] = []; + + private constructor() { + this.loadCommandLog(); + } + + public static getInstance(): CommandLog { + if (!CommandLog.instance) { + CommandLog.instance = new CommandLog(); + } + return CommandLog.instance; + } + + public async logCommand(commandId: string, duration: number) { + const timestamp = Date.now(); + this.commandLogEntries.push({ commandId, timestamp, duration }); + if (this.commandLogEntries.length > CommandLog.MAX_LOG_ENTRIES) { + this.commandLogEntries.shift(); + } + await this.updateCommandLog(); + } + + public getCommandLog(commandIdFilter?: string): CommandLogEntry[] { + return this.commandLogEntries.filter(entry => entry.commandId === commandIdFilter); + } + + private loadCommandLog() { + // load command log from storage + this.commandLogEntries = WorkspaceContext.getInstance().workspaceState.get(CommandLog.STORAGE_KEY) || []; + } + + private async updateCommandLog() { + // save command log to storage + await WorkspaceContext.getInstance().workspaceState.update(CommandLog.STORAGE_KEY, this.commandLogEntries); + } +} diff --git a/packages/salesforcedx-vscode-core/src/commands/util/commandLogEntry.ts b/packages/salesforcedx-vscode-core/src/commands/util/commandLogEntry.ts new file mode 100644 index 0000000000..e65e28372b --- /dev/null +++ b/packages/salesforcedx-vscode-core/src/commands/util/commandLogEntry.ts @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +export type CommandLogEntry = { + commandId: string; + timestamp: number; + duration?: number; +}; diff --git a/packages/salesforcedx-vscode-core/src/commands/util/commandWrapper.ts b/packages/salesforcedx-vscode-core/src/commands/util/commandWrapper.ts new file mode 100644 index 0000000000..b1311166a6 --- /dev/null +++ b/packages/salesforcedx-vscode-core/src/commands/util/commandWrapper.ts @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +import * as vscode from 'vscode'; +import { logCommand } from './commandLog'; + +export const registerCommand = (commandId: string, callback: (...args: any[]) => any, thisArg?: any): vscode.Disposable => { + return vscode.commands.registerCommand(commandId, wrapCommandCallback(commandId, callback), thisArg); +}; + +const wrapCommandCallback = (commandId: string, callback: (...args: any[]) => any): (...args: any[]) => any => { + return async (...args: any[]) => { + const startTime = Date.now(); + await callback(...args); + await logCommand(commandId, Date.now() - startTime); + }; +}; diff --git a/packages/salesforcedx-vscode-core/src/commands/util/index.ts b/packages/salesforcedx-vscode-core/src/commands/util/index.ts index e7a86fbe0c..cebb8fdb21 100644 --- a/packages/salesforcedx-vscode-core/src/commands/util/index.ts +++ b/packages/salesforcedx-vscode-core/src/commands/util/index.ts @@ -5,8 +5,11 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ -export { CommandParams } from './commandParams'; export { CommandletExecutor } from './commandletExecutor'; +export * from './commandLog'; +export * from './commandLogEntry'; +export { CommandParams } from './commandParams'; +export * from './commandWrapper'; export { CompositePostconditionChecker } from './compositePostconditionChecker'; export { CompositePreconditionChecker } from './compositePreconditionChecker'; export { ConflictDetectionMessages } from './conflictDetectionMessages'; @@ -17,6 +20,7 @@ export { EmptyPreChecker } from './emptyPreChecker'; export { FlagParameter } from './flagParameter'; export { formatException } from './formatException'; export { LibraryPathsGatherer } from './libraryPathsGatherer'; +export * from './lwcAuraDuplicateComponentCheckers'; export { OverwriteComponentPrompt } from './overwriteComponentPrompt'; export { CompositeParametersGatherer, @@ -39,4 +43,4 @@ export { PathStrategyFactory, SourcePathStrategy } from './sourcePathStrategies'; -export * from './lwcAuraDuplicateComponentCheckers'; + diff --git a/packages/salesforcedx-vscode-core/src/context/workspaceContext.ts b/packages/salesforcedx-vscode-core/src/context/workspaceContext.ts index d349479db7..f973e10299 100644 --- a/packages/salesforcedx-vscode-core/src/context/workspaceContext.ts +++ b/packages/salesforcedx-vscode-core/src/context/workspaceContext.ts @@ -20,16 +20,19 @@ import { workspaceContextUtils } from '.'; export class WorkspaceContext { protected static instance?: WorkspaceContext; + protected workspaceStore: vscode.Memento; public readonly onOrgChange: vscode.Event; protected constructor() { const workspaceContextUtil = WorkspaceContextUtil.getInstance(); this.onOrgChange = workspaceContextUtil.onOrgChange; this.onOrgChange(this.handleCliConfigChange); + this.workspaceStore = {} as vscode.Memento; } public async initialize(extensionContext: vscode.ExtensionContext) { await WorkspaceContextUtil.getInstance().initialize(extensionContext); + this.workspaceStore = extensionContext.workspaceState; } public static getInstance(forceNew = false): WorkspaceContext { @@ -54,6 +57,10 @@ export class WorkspaceContext { await decorators.showOrg(); } + get workspaceState(): vscode.Memento { + return this.workspaceStore; + } + get username(): string | undefined { return WorkspaceContextUtil.getInstance().username; } diff --git a/packages/salesforcedx-vscode-core/src/index.ts b/packages/salesforcedx-vscode-core/src/index.ts index 52d830a9c1..db664ebed0 100644 --- a/packages/salesforcedx-vscode-core/src/index.ts +++ b/packages/salesforcedx-vscode-core/src/index.ts @@ -63,6 +63,7 @@ import { projectGenerateWithManifest, projectRetrieveStart, refreshSObjects, + registerCommand, renameLightningComponent, retrieveComponent, retrieveManifest, @@ -140,257 +141,257 @@ const registerCommands = ( extensionContext: vscode.ExtensionContext ): vscode.Disposable => { // Customer-facing commands - const orgLoginAccessTokenCmd = vscode.commands.registerCommand( + const orgLoginAccessTokenCmd = registerCommand( 'sf.org.login.access.token', orgLoginAccessToken ); - const orgLoginWebCmd = vscode.commands.registerCommand( + const orgLoginWebCmd = registerCommand( 'sf.org.login.web', orgLoginWeb ); - const orgLoginWebDevHubCmd = vscode.commands.registerCommand( + const orgLoginWebDevHubCmd = registerCommand( 'sf.org.login.web.dev.hub', orgLoginWebDevHub ); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const orgLogoutAllCmd = vscode.commands.registerCommand( + const orgLogoutAllCmd = registerCommand( 'sf.org.logout.all', orgLogoutAll ); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const orgLogoutDefaultCmd = vscode.commands.registerCommand( + const orgLogoutDefaultCmd = registerCommand( 'sf.org.logout.default', orgLogoutDefault ); - const openDocumentationCmd = vscode.commands.registerCommand( + const openDocumentationCmd = registerCommand( 'sf.open.documentation', openDocumentation ); - const orgCreateCmd = vscode.commands.registerCommand( + const orgCreateCmd = registerCommand( 'sf.org.create', orgCreate ); - const orgOpenCmd = vscode.commands.registerCommand(ORG_OPEN_COMMAND, orgOpen); - const deleteSourceCmd = vscode.commands.registerCommand( + const orgOpenCmd = registerCommand(ORG_OPEN_COMMAND, orgOpen); + const deleteSourceCmd = registerCommand( 'sf.delete.source', deleteSource ); - const deleteSourceCurrentFileCmd = vscode.commands.registerCommand( + const deleteSourceCurrentFileCmd = registerCommand( 'sf.delete.source.current.file', deleteSource ); - const deployCurrentSourceFileCmd = vscode.commands.registerCommand( + const deployCurrentSourceFileCmd = registerCommand( 'sf.deploy.current.source.file', deploySourcePaths ); - const deployInManifestCmd = vscode.commands.registerCommand( + const deployInManifestCmd = registerCommand( 'sf.deploy.in.manifest', deployManifest ); - const deployMultipleSourcePathsCmd = vscode.commands.registerCommand( + const deployMultipleSourcePathsCmd = registerCommand( 'sf.deploy.multiple.source.paths', deploySourcePaths ); - const deploySourcePathCmd = vscode.commands.registerCommand( + const deploySourcePathCmd = registerCommand( 'sf.deploy.source.path', deploySourcePaths ); - const projectRetrieveStartCmd = vscode.commands.registerCommand( + const projectRetrieveStartCmd = registerCommand( 'sf.project.retrieve.start', projectRetrieveStart ); - const projectDeployStartCmd = vscode.commands.registerCommand( + const projectDeployStartCmd = registerCommand( 'sf.project.deploy.start', projectDeployStart ); const projectRetrieveStartIgnoreConflictsCmd = - vscode.commands.registerCommand( + registerCommand( 'sf.project.retrieve.start.ignore.conflicts', projectRetrieveStart, flagIgnoreConflicts ); - const projectDeployStartIgnoreConflictsCmd = vscode.commands.registerCommand( + const projectDeployStartIgnoreConflictsCmd = registerCommand( 'sf.project.deploy.start.ignore.conflicts', projectDeployStart, flagIgnoreConflicts ); - const retrieveCmd = vscode.commands.registerCommand( + const retrieveCmd = registerCommand( 'sf.retrieve.source.path', retrieveSourcePaths ); - const retrieveCurrentFileCmd = vscode.commands.registerCommand( + const retrieveCurrentFileCmd = registerCommand( 'sf.retrieve.current.source.file', retrieveSourcePaths ); - const retrieveInManifestCmd = vscode.commands.registerCommand( + const retrieveInManifestCmd = registerCommand( 'sf.retrieve.in.manifest', retrieveManifest ); - const forceSourceStatusCmd = vscode.commands.registerCommand( + const forceSourceStatusCmd = registerCommand( 'sf.view.all.changes', viewAllChanges ); - const forceSourceStatusLocalCmd = vscode.commands.registerCommand( + const forceSourceStatusLocalCmd = registerCommand( 'sf.view.local.changes', viewLocalChanges ); - const forceSourceStatusRemoteCmd = vscode.commands.registerCommand( + const forceSourceStatusRemoteCmd = registerCommand( 'sf.view.remote.changes', viewRemoteChanges ); - const taskStopCmd = vscode.commands.registerCommand('sf.task.stop', taskStop); - const apexGenerateClassCmd = vscode.commands.registerCommand( + const taskStopCmd = registerCommand('sf.task.stop', taskStop); + const apexGenerateClassCmd = registerCommand( 'sf.apex.generate.class', apexGenerateClass ); - const apexGenerateUnitTestClassCmd = vscode.commands.registerCommand( + const apexGenerateUnitTestClassCmd = registerCommand( 'sf.apex.generate.unit.test.class', apexGenerateUnitTestClass ); - const analyticsGenerateTemplateCmd = vscode.commands.registerCommand( + const analyticsGenerateTemplateCmd = registerCommand( 'sf.analytics.generate.template', analyticsGenerateTemplate ); - const visualforceGenerateComponentCmd = vscode.commands.registerCommand( + const visualforceGenerateComponentCmd = registerCommand( 'sf.visualforce.generate.component', visualforceGenerateComponent ); - const visualforceGeneratePageCmd = vscode.commands.registerCommand( + const visualforceGeneratePageCmd = registerCommand( 'sf.visualforce.generate.page', visualforceGeneratePage ); - const lightningGenerateAppCmd = vscode.commands.registerCommand( + const lightningGenerateAppCmd = registerCommand( 'sf.lightning.generate.app', lightningGenerateApp ); - const lightningGenerateAuraComponentCmd = vscode.commands.registerCommand( + const lightningGenerateAuraComponentCmd = registerCommand( 'sf.lightning.generate.aura.component', lightningGenerateAuraComponent ); - const lightningGenerateEventCmd = vscode.commands.registerCommand( + const lightningGenerateEventCmd = registerCommand( 'sf.lightning.generate.event', lightningGenerateEvent ); - const lightningGenerateInterfaceCmd = vscode.commands.registerCommand( + const lightningGenerateInterfaceCmd = registerCommand( 'sf.lightning.generate.interface', lightningGenerateInterface ); - const lightningGenerateLwcCmd = vscode.commands.registerCommand( + const lightningGenerateLwcCmd = registerCommand( 'sf.lightning.generate.lwc', lightningGenerateLwc ); - const forceLightningLwcTestCreateCmd = vscode.commands.registerCommand( + const forceLightningLwcTestCreateCmd = registerCommand( 'sf.force.lightning.lwc.test.create', forceLightningLwcTestCreate ); - const debuggerStopCmd = vscode.commands.registerCommand( + const debuggerStopCmd = registerCommand( 'sf.debugger.stop', debuggerStop ); - const configListCmd = vscode.commands.registerCommand( + const configListCmd = registerCommand( 'sf.config.list', configList ); - const forceAliasListCmd = vscode.commands.registerCommand( + const forceAliasListCmd = registerCommand( 'sf.alias.list', aliasList ); - const orgDeleteDefaultCmd = vscode.commands.registerCommand( + const orgDeleteDefaultCmd = registerCommand( 'sf.org.delete.default', orgDelete ); - const orgDeleteUsernameCmd = vscode.commands.registerCommand( + const orgDeleteUsernameCmd = registerCommand( 'sf.org.delete.username', orgDelete, { flag: '--target-org' } ); - const orgDisplayDefaultCmd = vscode.commands.registerCommand( + const orgDisplayDefaultCmd = registerCommand( 'sf.org.display.default', orgDisplay ); - const orgDisplayUsernameCmd = vscode.commands.registerCommand( + const orgDisplayUsernameCmd = registerCommand( 'sf.org.display.username', orgDisplay, { flag: '--target-org' } ); - const orgListCleanCmd = vscode.commands.registerCommand( + const orgListCleanCmd = registerCommand( 'sf.org.list.clean', orgList ); - const dataQueryInputCmd = vscode.commands.registerCommand( + const dataQueryInputCmd = registerCommand( 'sf.data.query.input', dataQuery ); - const dataQuerySelectionCmd = vscode.commands.registerCommand( + const dataQuerySelectionCmd = registerCommand( 'sf.data.query.selection', dataQuery ); - const projectGenerateCmd = vscode.commands.registerCommand( + const projectGenerateCmd = registerCommand( 'sf.project.generate', sfProjectGenerate ); - const packageInstallCmd = vscode.commands.registerCommand( + const packageInstallCmd = registerCommand( 'sf.package.install', packageInstall ); - const projectGenerateWithManifestCmd = vscode.commands.registerCommand( + const projectGenerateWithManifestCmd = registerCommand( 'sf.project.generate.with.manifest', projectGenerateWithManifest ); - const apexGenerateTriggerCmd = vscode.commands.registerCommand( + const apexGenerateTriggerCmd = registerCommand( 'sf.apex.generate.trigger', apexGenerateTrigger ); - const startApexDebugLoggingCmd = vscode.commands.registerCommand( + const startApexDebugLoggingCmd = registerCommand( 'sf.start.apex.debug.logging', startApexDebugLogging ); - const stopApexDebugLoggingCmd = vscode.commands.registerCommand( + const stopApexDebugLoggingCmd = registerCommand( 'sf.stop.apex.debug.logging', stopApexDebugLogging ); - const isvDebugBootstrapCmd = vscode.commands.registerCommand( + const isvDebugBootstrapCmd = registerCommand( 'sf.debug.isv.bootstrap', isvDebugBootstrap ); - const configSetCmd = vscode.commands.registerCommand( + const configSetCmd = registerCommand( 'sf.config.set', configSet ); - const diffFile = vscode.commands.registerCommand('sf.diff', sourceDiff); + const diffFile = registerCommand('sf.diff', sourceDiff); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const diffFolder = vscode.commands.registerCommand( + const diffFolder = registerCommand( 'sf.folder.diff', sourceFolderDiff ); - const forceRefreshSObjectsCmd = vscode.commands.registerCommand( + const forceRefreshSObjectsCmd = registerCommand( 'sf.internal.refreshsobjects', refreshSObjects ); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const renameLightningComponentCmd = vscode.commands.registerCommand( + const renameLightningComponentCmd = registerCommand( 'sf.rename.lightning.component', renameLightningComponent ); - const getCoreLoggerServiceCmd = vscode.commands.registerCommand( + const getCoreLoggerServiceCmd = registerCommand( 'sf.vscode.core.logger.get.instance', getCoreLoggerService ); @@ -465,28 +466,28 @@ const registerInternalDevCommands = ( // eslint-disable-next-line @typescript-eslint/no-unused-vars extensionContext: vscode.ExtensionContext ): vscode.Disposable => { - const internalLightningGenerateAppCmd = vscode.commands.registerCommand( + const internalLightningGenerateAppCmd = registerCommand( 'sf.internal.lightning.generate.app', internalLightningGenerateApp ); const internalLightningGenerateAuraComponentCmd = - vscode.commands.registerCommand( + registerCommand( 'sf.internal.lightning.generate.aura.component', internalLightningGenerateAuraComponent ); - const internalLightningGenerateEventCmd = vscode.commands.registerCommand( + const internalLightningGenerateEventCmd = registerCommand( 'sf.internal.lightning.generate.event', internalLightningGenerateEvent ); - const internalLightningGenerateInterfaceCmd = vscode.commands.registerCommand( + const internalLightningGenerateInterfaceCmd = registerCommand( 'sf.internal.lightning.generate.interface', internalLightningGenerateInterface ); - const internalLightningGenerateLwcCmd = vscode.commands.registerCommand( + const internalLightningGenerateLwcCmd = registerCommand( 'sf.internal.lightning.generate.lwc', internalLightningGenerateLwc ); @@ -503,7 +504,7 @@ const registerInternalDevCommands = ( const registerOrgPickerCommands = ( orgListParam: OrgList ): vscode.Disposable => { - const setDefaultOrgCmd = vscode.commands.registerCommand( + const setDefaultOrgCmd = registerCommand( 'sf.set.default.org', () => orgListParam.setDefaultOrg() ); @@ -515,35 +516,35 @@ const setupOrgBrowser = async ( ): Promise => { await orgBrowser.init(extensionContext); - vscode.commands.registerCommand( + registerCommand( 'sf.metadata.view.type.refresh', async node => { await orgBrowser.refreshAndExpand(node); } ); - vscode.commands.registerCommand( + registerCommand( 'sf.metadata.view.component.refresh', async node => { await orgBrowser.refreshAndExpand(node); } ); - vscode.commands.registerCommand( + registerCommand( 'sf.retrieve.component', async (trigger: RetrieveMetadataTrigger) => { await retrieveComponent(trigger); } ); - vscode.commands.registerCommand( + registerCommand( 'sf.retrieve.open.component', async (trigger: RetrieveMetadataTrigger) => { await retrieveComponent(trigger, true); } ); - vscode.commands.registerCommand( + registerCommand( 'sf.project.generate.manifest', projectGenerateManifest ); From e7a6202991e398f0ba07ecc95243037db65d86b3 Mon Sep 17 00:00:00 2001 From: Jon Gellin Date: Wed, 18 Sep 2024 16:52:57 -0400 Subject: [PATCH 2/4] chore: command event stream --- .../src/cli/commandExecutor.ts | 20 ++++-- .../src/cli/localCommandExecutor.ts | 10 +++ .../src/commands/commandEventStream.ts | 67 +++++++++++++++++++ .../src/commands/index.ts | 1 + .../src/commands/index.ts | 2 +- .../src/commands/util/commandLog.ts | 67 ++++++++++++++++--- .../src/commands/util/commandLogEntry.ts | 5 +- .../src/commands/util/commandWrapper.ts | 6 +- .../src/context/workspaceContext.ts | 6 +- .../salesforcedx-vscode-core/src/index.ts | 8 ++- 10 files changed, 169 insertions(+), 23 deletions(-) create mode 100644 packages/salesforcedx-utils-vscode/src/commands/commandEventStream.ts diff --git a/packages/salesforcedx-utils-vscode/src/cli/commandExecutor.ts b/packages/salesforcedx-utils-vscode/src/cli/commandExecutor.ts index 2adb16ae9d..471de74c27 100644 --- a/packages/salesforcedx-utils-vscode/src/cli/commandExecutor.ts +++ b/packages/salesforcedx-utils-vscode/src/cli/commandExecutor.ts @@ -16,6 +16,7 @@ import { Subscription } from 'rxjs/Subscription'; // Below two dependancies are not structured correcly for import unless require is used. /* eslint-disable @typescript-eslint/no-var-requires */ import { Command } from './'; +import { CommandEventStream, CommandEventType } from '../commands'; const cross_spawn = require('cross-spawn'); const kill = require('tree-kill'); /* eslint-enable @typescript-eslint/no-var-requires */ @@ -155,28 +156,35 @@ export class CompositeCliCommandExecution implements CommandExecution { } }); } - this.processErrorSubject.subscribe(() => { + this.processErrorSubject.subscribe(e => { if (timerSubscriber) { timerSubscriber.unsubscribe(); } + CommandEventStream.getInstance().post({ type: CommandEventType.ERROR, error: `${e}` }); }); - this.processExitSubject.subscribe(() => { + this.processExitSubject.subscribe(exitCode => { if (timerSubscriber) { timerSubscriber.unsubscribe(); } + if (exitCode !== undefined) { + CommandEventStream.getInstance().post({ type: CommandEventType.EXIT_CODE, exitCode }); + } }); } public successfulExit() { + CommandEventStream.getInstance().post({ type: CommandEventType.EXIT_CODE, exitCode: 0 }); this.exitSubject.next(0); } public failureExit(e?: {}) { if (e) { + CommandEventStream.getInstance().post({ type: CommandEventType.ERROR, error: `${e}` }); // eslint-disable-next-line @typescript-eslint/no-base-to-string, @typescript-eslint/restrict-template-expressions this.stderr.next(`${e}${os.EOL}`); } + CommandEventStream.getInstance().post({ type: CommandEventType.EXIT_CODE, exitCode: 1 }); this.exitSubject.next(1); } } @@ -209,16 +217,20 @@ export class CliCommandExecution implements CommandExecution { // Process this.processExitSubject = Observable.fromEvent(childProcess, 'exit'); - this.processExitSubject.subscribe(() => { + this.processExitSubject.subscribe(exitCode => { if (timerSubscriber) { timerSubscriber.unsubscribe(); } + if (exitCode !== undefined) { + CommandEventStream.getInstance().post({ type: CommandEventType.EXIT_CODE, exitCode }); + } }); this.processErrorSubject = Observable.fromEvent(childProcess, 'error'); - this.processErrorSubject.subscribe(() => { + this.processErrorSubject.subscribe(e => { if (timerSubscriber) { timerSubscriber.unsubscribe(); } + CommandEventStream.getInstance().post({ type: CommandEventType.ERROR, error: `${e}` }); }); // Output diff --git a/packages/salesforcedx-utils-vscode/src/cli/localCommandExecutor.ts b/packages/salesforcedx-utils-vscode/src/cli/localCommandExecutor.ts index b64d57c1be..3d4f4e02df 100644 --- a/packages/salesforcedx-utils-vscode/src/cli/localCommandExecutor.ts +++ b/packages/salesforcedx-utils-vscode/src/cli/localCommandExecutor.ts @@ -9,6 +9,7 @@ import 'rxjs/add/observable/fromEvent'; import { Observable } from 'rxjs/Observable'; import { Command } from '.'; import { CancellationToken, CommandExecution } from './commandExecutor'; +import { CommandEventStream, CommandEventType } from '../commands'; export class LocalCommandExecution implements CommandExecution { public static readonly EXIT_EVENT = 'exitEvent'; @@ -47,5 +48,14 @@ export class LocalCommandExecution implements CommandExecution { this.cmdEmitter, LocalCommandExecution.STDERR_EVENT ) ; + + this.processExitSubject.subscribe(exitCode => { + if (exitCode !== undefined) { + CommandEventStream.getInstance().post({ type: CommandEventType.EXIT_CODE, exitCode }); + } + }); + this.processErrorSubject.subscribe(error => { + CommandEventStream.getInstance().post({ type: CommandEventType.ERROR, error: `${error}` }); + }); } } diff --git a/packages/salesforcedx-utils-vscode/src/commands/commandEventStream.ts b/packages/salesforcedx-utils-vscode/src/commands/commandEventStream.ts new file mode 100644 index 0000000000..96a9b05ce8 --- /dev/null +++ b/packages/salesforcedx-utils-vscode/src/commands/commandEventStream.ts @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +import * as vscode from 'vscode'; + +export enum CommandEventType { + START = 'start', + END = 'end', + EXIT_CODE = 'exitCode', + DATA = 'data', + ERROR = 'error' +} + +export type CommandEventStart = { + type: CommandEventType.START; + commandId: string; +}; + +export type CommandEventEnd = { + type: CommandEventType.END; + commandId: string; +}; + +export type CommandEventExitCode = { + type: CommandEventType.EXIT_CODE; + exitCode: number; +}; + +export type CommandEventData = { + type: CommandEventType.DATA; + data: any; +}; + +export type CommandEventError = { + type: CommandEventType.ERROR; + error: string; +}; + +export type CommandEvent = CommandEventStart | CommandEventEnd | CommandEventExitCode | CommandEventData | CommandEventError; + +export class CommandEventStream { + private static instance: CommandEventStream; + private readonly eventEmitter = new vscode.EventEmitter(); + + private constructor() {} + + public static getInstance(): CommandEventStream { + if (!CommandEventStream.instance) { + CommandEventStream.instance = new CommandEventStream(); + } + return CommandEventStream.instance; + } + + public initialize(extensionContext: vscode.ExtensionContext) { + extensionContext.subscriptions.push(this.eventEmitter); + } + + public readonly onCommandEvent: vscode.Event = this.eventEmitter.event; + + public post(event: CommandEvent) { + this.eventEmitter.fire(event); + } +} diff --git a/packages/salesforcedx-utils-vscode/src/commands/index.ts b/packages/salesforcedx-utils-vscode/src/commands/index.ts index 5464d20421..9ef34b6f12 100644 --- a/packages/salesforcedx-utils-vscode/src/commands/index.ts +++ b/packages/salesforcedx-utils-vscode/src/commands/index.ts @@ -6,6 +6,7 @@ */ export { ChannelService } from './channelService'; +export * from './commandEventStream'; import { NotificationService } from './notificationService'; export const notificationService = NotificationService.getInstance(); diff --git a/packages/salesforcedx-vscode-core/src/commands/index.ts b/packages/salesforcedx-vscode-core/src/commands/index.ts index 37d2997693..7b964213bc 100644 --- a/packages/salesforcedx-vscode-core/src/commands/index.ts +++ b/packages/salesforcedx-vscode-core/src/commands/index.ts @@ -121,6 +121,6 @@ export { visualforceGenerateComponent, visualforceGeneratePage } from './templates'; -export { CommandLogEntry, getCommandLog, getLastCommandLogEntry, logCommand, registerCommand } from './util'; +export { CommandLogEntry, getCommandLog, getLastCommandLogEntry, registerCommand } from './util'; import { DeveloperLogTraceFlag } from '../traceflag/developerLogTraceFlag'; export const developerLogTraceFlag = DeveloperLogTraceFlag.getInstance(); diff --git a/packages/salesforcedx-vscode-core/src/commands/util/commandLog.ts b/packages/salesforcedx-vscode-core/src/commands/util/commandLog.ts index 31d75f6418..90c694df92 100644 --- a/packages/salesforcedx-vscode-core/src/commands/util/commandLog.ts +++ b/packages/salesforcedx-vscode-core/src/commands/util/commandLog.ts @@ -5,28 +5,27 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ +import { CommandEvent, CommandEventStream, CommandEventType } from '@salesforce/salesforcedx-utils-vscode'; import { WorkspaceContext } from '../../context'; import { CommandLogEntry } from './commandLogEntry'; -export const logCommand = async (commandId: string, duration: number): Promise => { - await CommandLog.getInstance().logCommand(commandId, duration); -}; - -export const getCommandLog = (commandIdFilter?: string): CommandLogEntry[] => { +export const getCommandLog = (commandIdFilter?: string, exitCodeFilter?: number): CommandLogEntry[] => { return CommandLog.getInstance().getCommandLog(commandIdFilter); }; -export const getLastCommandLogEntry = (commandIdFilter?: string): CommandLogEntry | undefined => { +export const getLastCommandLogEntry = (commandIdFilter?: string, exitCodeFilter?: number): CommandLogEntry | undefined => { const commandLog = CommandLog.getInstance().getCommandLog(commandIdFilter); return commandLog.length > 0 ? commandLog[commandLog.length - 1] : undefined; }; -class CommandLog { +export class CommandLog { private static instance: CommandLog; private static STORAGE_KEY = 'COMMAND_LOG'; private static MAX_LOG_ENTRIES = 1000; private commandLogEntries: CommandLogEntry[] = []; + private inProgressCommands: Map = new Map(); + private inProgressData: any = {}; private constructor() { this.loadCommandLog(); @@ -39,17 +38,35 @@ class CommandLog { return CommandLog.instance; } + public initialize() { + CommandEventStream.getInstance().onCommandEvent(this.processCommandEvent.bind(this)); + } + public async logCommand(commandId: string, duration: number) { - const timestamp = Date.now(); - this.commandLogEntries.push({ commandId, timestamp, duration }); + const entry = { + commandId, + timestamp: Date.now(), + duration, + exitCode: this.inProgressData.exitCode, + error: this.inProgressData.error, + data: this.inProgressData.data + } + this.commandLogEntries.push(entry); if (this.commandLogEntries.length > CommandLog.MAX_LOG_ENTRIES) { this.commandLogEntries.shift(); } await this.updateCommandLog(); } - public getCommandLog(commandIdFilter?: string): CommandLogEntry[] { - return this.commandLogEntries.filter(entry => entry.commandId === commandIdFilter); + public getCommandLog(commandIdFilter?: string, exitCodeFilter?: number): CommandLogEntry[] { + let results = Array.from(this.commandLogEntries); + if (commandIdFilter) { + results = results.filter(entry => entry.commandId === commandIdFilter); + } + if (exitCodeFilter !== undefined) { + results = results.filter(entry => entry.exitCode === exitCodeFilter); + } + return results; } private loadCommandLog() { @@ -61,4 +78,32 @@ class CommandLog { // save command log to storage await WorkspaceContext.getInstance().workspaceState.update(CommandLog.STORAGE_KEY, this.commandLogEntries); } + + private async processCommandEvent(event: CommandEvent): Promise { + switch (event.type) { + case CommandEventType.START: + this.inProgressCommands.set(event.commandId, Date.now()); + break; + case CommandEventType.END: + const start = this.inProgressCommands.get(event.commandId); + if (start) { + const duration = Date.now() - start; + await this.logCommand(event.commandId, duration); + this.inProgressCommands.delete(event.commandId); + if (this.inProgressCommands.size === 0) { + this.inProgressData = {}; + } + } + break; + case CommandEventType.EXIT_CODE: + this.inProgressData.exitCode = event.exitCode; + break; + case CommandEventType.DATA: + this.inProgressData.data = { ...this.inProgressData.data, ...event.data }; + break; + case CommandEventType.ERROR: + this.inProgressData.error = event.error; + break; + } + } } diff --git a/packages/salesforcedx-vscode-core/src/commands/util/commandLogEntry.ts b/packages/salesforcedx-vscode-core/src/commands/util/commandLogEntry.ts index e65e28372b..6bc07d326a 100644 --- a/packages/salesforcedx-vscode-core/src/commands/util/commandLogEntry.ts +++ b/packages/salesforcedx-vscode-core/src/commands/util/commandLogEntry.ts @@ -8,5 +8,8 @@ export type CommandLogEntry = { commandId: string; timestamp: number; - duration?: number; + duration: number; + exitCode?: number; + error?: string; + data?: any; }; diff --git a/packages/salesforcedx-vscode-core/src/commands/util/commandWrapper.ts b/packages/salesforcedx-vscode-core/src/commands/util/commandWrapper.ts index b1311166a6..49526a0999 100644 --- a/packages/salesforcedx-vscode-core/src/commands/util/commandWrapper.ts +++ b/packages/salesforcedx-vscode-core/src/commands/util/commandWrapper.ts @@ -5,8 +5,8 @@ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ +import { CommandEventStream, CommandEventType } from '@salesforce/salesforcedx-utils-vscode'; import * as vscode from 'vscode'; -import { logCommand } from './commandLog'; export const registerCommand = (commandId: string, callback: (...args: any[]) => any, thisArg?: any): vscode.Disposable => { return vscode.commands.registerCommand(commandId, wrapCommandCallback(commandId, callback), thisArg); @@ -14,8 +14,8 @@ export const registerCommand = (commandId: string, callback: (...args: any[]) => const wrapCommandCallback = (commandId: string, callback: (...args: any[]) => any): (...args: any[]) => any => { return async (...args: any[]) => { - const startTime = Date.now(); + CommandEventStream.getInstance().post({ type: CommandEventType.START, commandId }); await callback(...args); - await logCommand(commandId, Date.now() - startTime); + CommandEventStream.getInstance().post({ type: CommandEventType.END, commandId }); }; }; diff --git a/packages/salesforcedx-vscode-core/src/context/workspaceContext.ts b/packages/salesforcedx-vscode-core/src/context/workspaceContext.ts index f973e10299..779cefe807 100644 --- a/packages/salesforcedx-vscode-core/src/context/workspaceContext.ts +++ b/packages/salesforcedx-vscode-core/src/context/workspaceContext.ts @@ -7,12 +7,14 @@ import { Connection } from '@salesforce/core-bundle'; import { + CommandEventStream, OrgUserInfo, WorkspaceContextUtil } from '@salesforce/salesforcedx-utils-vscode'; import * as vscode from 'vscode'; -import { decorators } from '../decorators'; import { workspaceContextUtils } from '.'; +import { CommandLog } from '../commands/util'; +import { decorators } from '../decorators'; /** * Manages the context of a workspace during a session with an open SFDX Project. @@ -33,6 +35,8 @@ export class WorkspaceContext { public async initialize(extensionContext: vscode.ExtensionContext) { await WorkspaceContextUtil.getInstance().initialize(extensionContext); this.workspaceStore = extensionContext.workspaceState; + CommandEventStream.getInstance().initialize(extensionContext); + CommandLog.getInstance().initialize(); } public static getInstance(forceNew = false): WorkspaceContext { diff --git a/packages/salesforcedx-vscode-core/src/index.ts b/packages/salesforcedx-vscode-core/src/index.ts index db664ebed0..abeea9c015 100644 --- a/packages/salesforcedx-vscode-core/src/index.ts +++ b/packages/salesforcedx-vscode-core/src/index.ts @@ -92,7 +92,9 @@ import { SelectOutputDir, SfCommandlet, SfCommandletExecutor, - SfWorkspaceChecker + SfWorkspaceChecker, + getCommandLog, + getLastCommandLogEntry } from './commands/util'; import { CommandEventDispatcher } from './commands/util/commandEventDispatcher'; @@ -674,7 +676,9 @@ export const activate = async (extensionContext: vscode.ExtensionContext) => { TelemetryService, WorkspaceContext, CommandEventDispatcher - } + }, + getCommandLog, + getLastCommandLogEntry }; if ( From a6f8463be6cf351a0fca826d80e8a3a72b65a97f Mon Sep 17 00:00:00 2001 From: Jon Gellin Date: Wed, 18 Sep 2024 17:03:10 -0400 Subject: [PATCH 3/4] chore: lint --- .../src/cli/commandExecutor.ts | 4 +++- .../src/cli/localCommandExecutor.ts | 2 +- .../src/commands/util/commandLog.ts | 24 ++++++++++--------- .../src/context/workspaceContext.ts | 2 +- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/packages/salesforcedx-utils-vscode/src/cli/commandExecutor.ts b/packages/salesforcedx-utils-vscode/src/cli/commandExecutor.ts index 471de74c27..1b252fefec 100644 --- a/packages/salesforcedx-utils-vscode/src/cli/commandExecutor.ts +++ b/packages/salesforcedx-utils-vscode/src/cli/commandExecutor.ts @@ -15,8 +15,8 @@ import { Subscription } from 'rxjs/Subscription'; // Below two dependancies are not structured correcly for import unless require is used. /* eslint-disable @typescript-eslint/no-var-requires */ -import { Command } from './'; import { CommandEventStream, CommandEventType } from '../commands'; +import { Command } from './'; const cross_spawn = require('cross-spawn'); const kill = require('tree-kill'); /* eslint-enable @typescript-eslint/no-var-requires */ @@ -180,6 +180,7 @@ export class CompositeCliCommandExecution implements CommandExecution { public failureExit(e?: {}) { if (e) { + // eslint-disable-next-line @typescript-eslint/no-base-to-string, @typescript-eslint/restrict-template-expressions CommandEventStream.getInstance().post({ type: CommandEventType.ERROR, error: `${e}` }); // eslint-disable-next-line @typescript-eslint/no-base-to-string, @typescript-eslint/restrict-template-expressions this.stderr.next(`${e}${os.EOL}`); @@ -230,6 +231,7 @@ export class CliCommandExecution implements CommandExecution { if (timerSubscriber) { timerSubscriber.unsubscribe(); } + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions CommandEventStream.getInstance().post({ type: CommandEventType.ERROR, error: `${e}` }); }); diff --git a/packages/salesforcedx-utils-vscode/src/cli/localCommandExecutor.ts b/packages/salesforcedx-utils-vscode/src/cli/localCommandExecutor.ts index 3d4f4e02df..fe54c8f82e 100644 --- a/packages/salesforcedx-utils-vscode/src/cli/localCommandExecutor.ts +++ b/packages/salesforcedx-utils-vscode/src/cli/localCommandExecutor.ts @@ -7,9 +7,9 @@ import { EventEmitter } from 'events'; import 'rxjs/add/observable/fromEvent'; import { Observable } from 'rxjs/Observable'; +import { CommandEventStream, CommandEventType } from '../commands'; import { Command } from '.'; import { CancellationToken, CommandExecution } from './commandExecutor'; -import { CommandEventStream, CommandEventType } from '../commands'; export class LocalCommandExecution implements CommandExecution { public static readonly EXIT_EVENT = 'exitEvent'; diff --git a/packages/salesforcedx-vscode-core/src/commands/util/commandLog.ts b/packages/salesforcedx-vscode-core/src/commands/util/commandLog.ts index 90c694df92..c98b7f7c1d 100644 --- a/packages/salesforcedx-vscode-core/src/commands/util/commandLog.ts +++ b/packages/salesforcedx-vscode-core/src/commands/util/commandLog.ts @@ -10,11 +10,11 @@ import { WorkspaceContext } from '../../context'; import { CommandLogEntry } from './commandLogEntry'; export const getCommandLog = (commandIdFilter?: string, exitCodeFilter?: number): CommandLogEntry[] => { - return CommandLog.getInstance().getCommandLog(commandIdFilter); + return CommandLog.getInstance().getCommandLog(commandIdFilter, exitCodeFilter); }; export const getLastCommandLogEntry = (commandIdFilter?: string, exitCodeFilter?: number): CommandLogEntry | undefined => { - const commandLog = CommandLog.getInstance().getCommandLog(commandIdFilter); + const commandLog = CommandLog.getInstance().getCommandLog(commandIdFilter, exitCodeFilter); return commandLog.length > 0 ? commandLog[commandLog.length - 1] : undefined; }; @@ -50,7 +50,7 @@ export class CommandLog { exitCode: this.inProgressData.exitCode, error: this.inProgressData.error, data: this.inProgressData.data - } + }; this.commandLogEntries.push(entry); if (this.commandLogEntries.length > CommandLog.MAX_LOG_ENTRIES) { this.commandLogEntries.shift(); @@ -84,14 +84,16 @@ export class CommandLog { case CommandEventType.START: this.inProgressCommands.set(event.commandId, Date.now()); break; - case CommandEventType.END: - const start = this.inProgressCommands.get(event.commandId); - if (start) { - const duration = Date.now() - start; - await this.logCommand(event.commandId, duration); - this.inProgressCommands.delete(event.commandId); - if (this.inProgressCommands.size === 0) { - this.inProgressData = {}; + case CommandEventType.END: + { + const start = this.inProgressCommands.get(event.commandId); + if (start) { + const duration = Date.now() - start; + await this.logCommand(event.commandId, duration); + this.inProgressCommands.delete(event.commandId); + if (this.inProgressCommands.size === 0) { + this.inProgressData = {}; + } } } break; diff --git a/packages/salesforcedx-vscode-core/src/context/workspaceContext.ts b/packages/salesforcedx-vscode-core/src/context/workspaceContext.ts index 779cefe807..69d5c68d6d 100644 --- a/packages/salesforcedx-vscode-core/src/context/workspaceContext.ts +++ b/packages/salesforcedx-vscode-core/src/context/workspaceContext.ts @@ -12,9 +12,9 @@ import { WorkspaceContextUtil } from '@salesforce/salesforcedx-utils-vscode'; import * as vscode from 'vscode'; -import { workspaceContextUtils } from '.'; import { CommandLog } from '../commands/util'; import { decorators } from '../decorators'; +import { workspaceContextUtils } from '.'; /** * Manages the context of a workspace during a session with an open SFDX Project. From 43ec57515fab996cf1dab0f695489aae99b7f587 Mon Sep 17 00:00:00 2001 From: Jon Gellin Date: Fri, 25 Oct 2024 12:11:26 -0400 Subject: [PATCH 4/4] chore: command executor posting to command stream --- .../src/cli/commandExecutor.ts | 4 --- .../test/jest/cli/commandExecutor.test.ts | 27 +++++++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 packages/salesforcedx-utils-vscode/test/jest/cli/commandExecutor.test.ts diff --git a/packages/salesforcedx-utils-vscode/src/cli/commandExecutor.ts b/packages/salesforcedx-utils-vscode/src/cli/commandExecutor.ts index 1b252fefec..c2680fc721 100644 --- a/packages/salesforcedx-utils-vscode/src/cli/commandExecutor.ts +++ b/packages/salesforcedx-utils-vscode/src/cli/commandExecutor.ts @@ -174,18 +174,14 @@ export class CompositeCliCommandExecution implements CommandExecution { } public successfulExit() { - CommandEventStream.getInstance().post({ type: CommandEventType.EXIT_CODE, exitCode: 0 }); this.exitSubject.next(0); } public failureExit(e?: {}) { if (e) { - // eslint-disable-next-line @typescript-eslint/no-base-to-string, @typescript-eslint/restrict-template-expressions - CommandEventStream.getInstance().post({ type: CommandEventType.ERROR, error: `${e}` }); // eslint-disable-next-line @typescript-eslint/no-base-to-string, @typescript-eslint/restrict-template-expressions this.stderr.next(`${e}${os.EOL}`); } - CommandEventStream.getInstance().post({ type: CommandEventType.EXIT_CODE, exitCode: 1 }); this.exitSubject.next(1); } } diff --git a/packages/salesforcedx-utils-vscode/test/jest/cli/commandExecutor.test.ts b/packages/salesforcedx-utils-vscode/test/jest/cli/commandExecutor.test.ts new file mode 100644 index 0000000000..f5b22add93 --- /dev/null +++ b/packages/salesforcedx-utils-vscode/test/jest/cli/commandExecutor.test.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +import { CliCommandExecutor } from "../../../src/cli/commandExecutor"; +import { CommandEventStream } from "../../../src/commands/commandEventStream"; + + +describe('CompositeCliCommandExecution', () => { + const commandEventStreamPostSpy = jest.spyOn(CommandEventStream.getInstance(), 'post'); + +}); + +describe('CliCommandExecutor', () => { + describe('patchEnv', () => { + it('should patch the environment with the base environment', () => { + const baseEnvironment = new Map(); + baseEnvironment.set('SFDX_ENV', 'test'); + const options = { env: { TEST: 'test' } }; + const patchedOptions = CliCommandExecutor.patchEnv(options, baseEnvironment); + expect(patchedOptions.env).toEqual({ SFDX_ENV: 'test', TEST: 'test', SFDX_TOOL: 'salesforce-vscode-extensions' }); + }); + }); +});