Skip to content

Commit

Permalink
refactor: globalState abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
justinmk3 committed Jul 10, 2024
1 parent 21d5fbf commit 80e715b
Show file tree
Hide file tree
Showing 17 changed files with 50 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import * as vscode from 'vscode'
import * as sinon from 'sinon'
import {
onAcceptance,
userGroupKey,
UserGroup,
AcceptedSuggestionEntry,
session,
Expand Down Expand Up @@ -80,7 +79,7 @@ describe('onAcceptance', function () {
})

it('Should report telemetry that records this user decision event', async function () {
await globals.context.globalState.update(userGroupKey, {
await globals.context.globalState.update('CODEWHISPERER_USER_GROUP', {
group: UserGroup.Control,
version: extensionVersion,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import {
AuthUtil,
session,
CodeWhispererUserGroupSettings,
userGroupKey,
UserGroup,
} from 'aws-core-vscode/codewhisperer'
import { globals } from 'aws-core-vscode/shared'
Expand Down Expand Up @@ -58,7 +57,7 @@ describe('onInlineAcceptance', function () {
})

it('Should report telemetry that records this user decision event', async function () {
await globals.context.globalState.update(userGroupKey, {
await globals.context.globalState.update('CODEWHISPERER_USER_GROUP', {
group: UserGroup.Classifier,
version: extensionVersion,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import {
ConfigurationEntry,
RecommendationHandler,
CodeWhispererCodeCoverageTracker,
userGroupKey,
UserGroup,
supplementalContextUtil,
} from 'aws-core-vscode/codewhisperer'
Expand Down Expand Up @@ -115,7 +114,7 @@ describe('recommendationHandler', function () {
})

it('should call telemetry function that records a CodeWhisperer service invocation', async function () {
await globals.context.globalState.update(userGroupKey, {
await globals.context.globalState.update('CODEWHISPERER_USER_GROUP', {
group: UserGroup.CrossFile,
version: extensionVersion,
})
Expand Down Expand Up @@ -167,7 +166,7 @@ describe('recommendationHandler', function () {
})

it('should call telemetry function that records a Empty userDecision event', async function () {
await globals.context.globalState.update(userGroupKey, {
await globals.context.globalState.update('CODEWHISPERER_USER_GROUP', {
group: UserGroup.CrossFile,
version: extensionVersion,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
vsCodeState,
TelemetryHelper,
AuthUtil,
userGroupKey,
UserGroup,
CodeWhispererUserGroupSettings,
} from 'aws-core-vscode/codewhisperer'
Expand Down Expand Up @@ -524,7 +523,7 @@ describe('codewhispererCodecoverageTracker', function () {
})

it('should emit correct code coverage telemetry in python file', async function () {
await globals.context.globalState.update(userGroupKey, {
await globals.globalState.update('CODEWHISPERER_USER_GROUP', {
group: UserGroup.Control,
version: extensionVersion,
})
Expand All @@ -547,7 +546,7 @@ describe('codewhispererCodecoverageTracker', function () {
})

it('should emit correct code coverage telemetry when success count = 0', async function () {
await globals.context.globalState.update(userGroupKey, {
await globals.globalState.update('CODEWHISPERER_USER_GROUP', {
group: UserGroup.Control,
version: extensionVersion,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import { assertTelemetryCurried } from 'aws-core-vscode/test'
import {
AuthUtil,
CodeWhispererTracker,
userGroupKey,
UserGroup,
CodeWhispererUserGroupSettings,
} from 'aws-core-vscode/codewhisperer'
Expand Down Expand Up @@ -96,7 +95,7 @@ describe('codewhispererTracker', function () {
})

it('Should call recordCodewhispererUserModification with suggestion event', async function () {
await globals.context.globalState.update(userGroupKey, {
await globals.context.globalState.update('CODEWHISPERER_USER_GROUP', {
group: UserGroup.CrossFile,
version: extensionVersion,
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ describe('getCodeWhispererUserGroup', function () {
})

it('getUserGroup should set the group and version if there is none', async function () {
await globals.context.globalState.update(CodeWhispererConstants.userGroupKey, undefined)
await globals.globalState.update('CODEWHISPERER_USER_GROUP', undefined)

assert.ok(!globals.context.globalState.get(CodeWhispererConstants.userGroupKey))
assert.ok(!globals.globalState.get('CODEWHISPERER_USER_GROUP'))

assert.ok(CodeWhispererUserGroupSettings.getUserGroup())
assert.ok(CodeWhispererUserGroupSettings.instance.version)
Expand All @@ -30,7 +30,7 @@ describe('getCodeWhispererUserGroup', function () {
})

it('should return the same result', async function () {
await globals.context.globalState.update(CodeWhispererConstants.userGroupKey, undefined)
await globals.globalState.update('CODEWHISPERER_USER_GROUP', undefined)

const group0 = CodeWhispererUserGroupSettings.getUserGroup()
const group1 = CodeWhispererUserGroupSettings.getUserGroup()
Expand All @@ -44,7 +44,7 @@ describe('getCodeWhispererUserGroup', function () {
})

it('should return result stored in the extension context if the plugin version remains the same', async function () {
await globals.context.globalState.update(CodeWhispererConstants.userGroupKey, {
await globals.globalState.update('CODEWHISPERER_USER_GROUP', {
group: CodeWhispererConstants.UserGroup.Control,
version: extensionVersion,
})
Expand All @@ -56,7 +56,7 @@ describe('getCodeWhispererUserGroup', function () {
})

it('should return different result if the plugin version is not the same', async function () {
await globals.context.globalState.update(CodeWhispererConstants.userGroupKey, {
await globals.globalState.update('CODEWHISPERER_USER_GROUP', {
group: CodeWhispererConstants.UserGroup.Control,
version: 'fake-extension-version',
})
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/amazonq/util/viewBadgeHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { window, TreeItem, TreeView, ViewBadge } from 'vscode'
import { getLogger } from '../../shared/logger'
import globals from '../../shared/extensionGlobals'
import { AuthUtil } from '../../codewhisperer/util/authUtil'
import { GlobalState } from '../../shared/globalState'

let badgeHelperView: TreeView<void> | undefined

Expand Down Expand Up @@ -43,7 +42,7 @@ export function changeViewBadge(badge?: ViewBadge) {
* Removes the view badge from the badge helper view and prevents it from showing up ever again
*/
export function deactivateInitialViewBadge() {
GlobalState.instance.tryUpdate('hasAlreadyOpenedAmazonQ', true)
globals.globalState.tryUpdate('hasAlreadyOpenedAmazonQ', true)
changeViewBadge()
}

Expand Down
5 changes: 2 additions & 3 deletions packages/core/src/awsService/s3/commands/uploadFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import { CancellationError } from '../../../shared/utilities/timeoutUtils'
import { progressReporter } from '../progressReporter'
import globals from '../../../shared/extensionGlobals'
import { telemetry } from '../../../shared/telemetry/telemetry'
import { GlobalState } from '../../../shared/globalState'

export interface FileSizeBytes {
/**
Expand Down Expand Up @@ -102,7 +101,7 @@ export async function uploadFileCommand(
})
)
if (node instanceof S3FolderNode) {
GlobalState.instance.tryUpdate('aws.lastUploadedToS3Folder', {
globals.globalState.tryUpdate('aws.lastUploadedToS3Folder', {
bucket: node.bucket,
folder: node.folder,
})
Expand Down Expand Up @@ -166,7 +165,7 @@ export async function uploadFileCommand(
)

if (bucketResponse.folder) {
GlobalState.instance.tryUpdate('aws.lastUploadedToS3Folder', {
globals.globalState.tryUpdate('aws.lastUploadedToS3Folder', {
bucket: bucketResponse.bucket,
folder: bucketResponse.folder,
})
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/awsexplorer/activation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ import { CodeCatalystRootNode } from '../codecatalyst/explorer'
import { CodeCatalystAuthenticationProvider } from '../codecatalyst/auth'
import { S3FolderNode } from '../awsService/s3/explorer/s3FolderNode'
import { AmazonQNode, refreshAmazonQ, refreshAmazonQRootNode } from '../amazonq/explorer/amazonQTreeNode'
import { GlobalState } from '../shared/globalState'
import { activateViewsShared, registerToolView } from './activationShared'
import { isExtensionInstalled } from '../shared/utilities'
import { CommonAuthViewProvider } from '../login/webview'
Expand All @@ -53,7 +52,7 @@ export async function activate(args: {
})
view.onDidExpandElement(element => {
if (element.element instanceof S3FolderNode) {
GlobalState.instance.tryUpdate('aws.lastTouchedS3Folder', {
globals.globalState.tryUpdate('aws.lastTouchedS3Folder', {
bucket: element.element.bucket,
folder: element.element.folder,
})
Expand Down
6 changes: 2 additions & 4 deletions packages/core/src/codecatalyst/reconnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import globals from '../shared/extensionGlobals'
import { isDevenvVscode } from './utils'
import { telemetry } from '../shared/telemetry/telemetry'
import { SsoConnection } from '../auth/connection'
import { GlobalState } from '../shared/globalState'

const localize = nls.loadMessageBundle()

Expand All @@ -41,8 +40,7 @@ export function watchRestartingDevEnvs(ctx: ExtContext, authProvider: CodeCataly

function handleRestart(conn: SsoConnection, ctx: ExtContext, envId: string | undefined) {
if (envId !== undefined) {
const pendingReconnects =
GlobalState.instance.get<Record<string, DevEnvMemento>>('CODECATALYST_RECONNECT') ?? {}
const pendingReconnects = globals.globalState.get<Record<string, DevEnvMemento>>('CODECATALYST_RECONNECT') ?? {}
if (envId in pendingReconnects) {
const devenv = pendingReconnects[envId]
const devenvName = getDevEnvName(devenv.alias, envId)
Expand All @@ -51,7 +49,7 @@ function handleRestart(conn: SsoConnection, ctx: ExtContext, envId: string | und
localize('AWS.codecatalyst.reconnect.success', 'Reconnected to Dev Environment: {0}', devenvName)
)
delete pendingReconnects[envId]
GlobalState.instance.tryUpdate('CODECATALYST_RECONNECT', pendingReconnects)
globals.globalState.tryUpdate('CODECATALYST_RECONNECT', pendingReconnects)
}
} else {
getLogger().info('codecatalyst: attempting to poll dev environments')
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/codewhisperer/util/authUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import { onceChanged, once } from '../../shared/utilities/functionUtils'
import { indent } from '../../shared/utilities/textUtilities'
import { showReauthenticateMessage } from '../../shared/utilities/messages'
import { showAmazonQWalkthroughOnce } from '../../amazonq/onboardingPage/walkthrough'
import { setContext } from '../../shared'
import { setContext } from '../../shared/vscode/setContext'

/** Backwards compatibility for connections w pre-chat scopes */
export const codeWhispererCoreScopes = [...scopesCodeWhispererCore]
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/codewhisperer/util/userGroupUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import { UserGroup } from '../models/constants'
import globals from '../../shared/extensionGlobals'
import { extensionVersion } from '../../shared/vscode/env'
import { GlobalState } from '../../shared/globalState'

export class CodeWhispererUserGroupSettings {
private _userGroup: UserGroup | undefined
Expand Down Expand Up @@ -50,7 +49,7 @@ export class CodeWhispererUserGroupSettings {
this._version = extensionVersion
this._userGroup = this.guessUserGroup()

GlobalState.instance.tryUpdate('CODEWHISPERER_USER_GROUP', {
globals.globalState.tryUpdate('CODEWHISPERER_USER_GROUP', {
group: this._userGroup,
version: this._version,
})
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/shared/extensionGlobals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ export function initialize(context: ExtensionContext, isWeb: boolean = false): T
context,
clock: copyClock(),
didReload: checkDidReload(context),
globalState: GlobalState.instance,
globalState: new GlobalState(context),
manifestPaths: {} as ToolkitGlobals['manifestPaths'],
visualizationResourcePaths: {} as ToolkitGlobals['visualizationResourcePaths'],
isWeb,
Expand All @@ -170,6 +170,7 @@ export { globals as default }
*/
interface ToolkitGlobals {
readonly context: ExtensionContext
/** Global, shared, mutable, persisted state (survives IDE restart), namespaced to the extension (i.e. not shared with other vscode extensions). */
readonly globalState: GlobalState
/** Decides the prefix for package.json extension parameters, e.g. commands, 'setContext' values, etc. */
contextPrefix: string
Expand Down
42 changes: 23 additions & 19 deletions packages/core/src/shared/globalState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,44 @@
*/

import * as vscode from 'vscode'
import globals from './extensionGlobals'
import { getLogger } from './logger/logger'

type globalKey =
| 'gumby.wasQCodeTransformationUsed'
| 'aws.toolkit.amazonq.dismissed'
| 'aws.toolkit.amazonqInstall.dismissed'
| 'hasAlreadyOpenedAmazonQ'
| 'aws.downloadPath'
| 'aws.lastTouchedS3Folder'
| 'aws.lastUploadedToS3Folder'
| 'aws.downloadPath'
| 'aws.toolkit.amazonq.dismissed'
| 'aws.toolkit.amazonqInstall.dismissed'
// Deprecated/legacy names. New keys should start with "aws.".
| 'CODECATALYST_RECONNECT'
| 'CODEWHISPERER_USER_GROUP'
| 'gumby.wasQCodeTransformationUsed'
| 'hasAlreadyOpenedAmazonQ'

/**
* Extension-local, shared state which persists after IDE restart. Shared with all instances (or
* tabs, in a web browser) of this extension for a given user, but not visible to other vscode
* extensions. Global state should be avoided, except when absolutely necessary.
*
* This wrapper adds structure and visibility to the vscode `globalState` interface. It also opens
* the door for:
* - validation
* - garbage collection
*/
export class GlobalState implements vscode.Memento {
static #instance: GlobalState
static get instance(): GlobalState {
return (this.#instance ??= new GlobalState())
}
public constructor(private readonly extContext: vscode.ExtensionContext) {}

public keys(): readonly string[] {
return globals.context.globalState.keys()
return this.extContext.globalState.keys()
}

public get<T>(key: globalKey, defaultValue?: T): T | undefined {
return globals.context.globalState.get(key) ?? defaultValue
return this.extContext.globalState.get(key) ?? defaultValue
}

/** Asynchronously updates globalState, or logs an error on failure. */
public tryUpdate(key: globalKey, value: any): void {
globals.context.globalState.update(key, value).then(
this.extContext.globalState.update(key, value).then(
undefined, // TODO: log.debug() ?
e => {
getLogger().error('GlobalState: failed to set "%s": %s', key, (e as Error).message)
Expand All @@ -43,13 +50,10 @@ export class GlobalState implements vscode.Memento {
}

public update(key: globalKey, value: any): Thenable<void> {
return globals.context.globalState.update(key, value)
return this.extContext.globalState.update(key, value)
}

public static samAndCfnSchemaDestinationUri() {
return vscode.Uri.joinPath(globals.context.globalStorageUri, 'sam.schema.json')
public samAndCfnSchemaDestinationUri() {
return vscode.Uri.joinPath(this.extContext.globalStorageUri, 'sam.schema.json')
}
}

export const globalState = GlobalState.instance
export default globalState
5 changes: 2 additions & 3 deletions packages/core/src/shared/logger/logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
*/

import * as vscode from 'vscode'
import globals from '../extensionGlobals'

const toolkitLoggers: {
main: Logger | undefined
Expand Down Expand Up @@ -202,11 +201,11 @@ export class PerfLog {
public constructor(public readonly topic: string) {
const log = getLogger()
this.log = log
this.start = globals.clock.Date.now()
this.start = performance.now()
}

public elapsed(): number {
return globals.clock.Date.now() - this.start
return performance.now() - this.start
}

public done(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('downloadFileAsCommand', function () {
dialog.accept()
})
const outputChannel = new MockOutputChannel()
await globals.context.globalState.update('aws.downloadPath', temp)
await globals.globalState.update('aws.downloadPath', temp)

s3.downloadFileStream = sinon.stub().resolves(bufferToStream(Buffer.alloc(16)))

Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/test/globalSetup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { getTestWindow, resetTestWindow } from './shared/vscode/window'
import { mapTestErrors, normalizeError, setRunnableTimeout } from './setupUtil'
import { TelemetryDebounceInfo } from '../shared/vscode/commands2'
import { disableAwsSdkWarning } from '../shared/awsClientBuilder'
import { GlobalState } from '../shared/globalState'

disableAwsSdkWarning()

Expand Down Expand Up @@ -84,6 +85,7 @@ export const mochaHooks = {
globals.telemetry.logger.clear()
TelemetryDebounceInfo.instance.clear()
;(globals.context as FakeExtensionContext).globalState = new FakeMemento()
;(globals as any).globalState = new GlobalState(globals.context)

await testUtil.closeAllEditors()
},
Expand Down

0 comments on commit 80e715b

Please sign in to comment.