Skip to content

Commit f9d5ef6

Browse files
authored
Merge aws#5348 use globalState abstraction
2 parents 1c778ac + f23ba38 commit f9d5ef6

26 files changed

+242
-122
lines changed

packages/amazonq/test/unit/codewhisperer/util/authUtil.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
createTestAuth,
2222
} from 'aws-core-vscode/test'
2323
import { Auth, Connection, isAnySsoConnection, isBuilderIdConnection } from 'aws-core-vscode/auth'
24-
import { vscodeComponent } from 'aws-core-vscode/shared'
24+
import { globals, vscodeComponent } from 'aws-core-vscode/shared'
2525

2626
const enterpriseSsoStartUrl = 'https://enterprise.awsapps.com/start'
2727

@@ -30,7 +30,7 @@ describe('AuthUtil', async function () {
3030
let authUtil: AuthUtil
3131

3232
beforeEach(async function () {
33-
auth = createTestAuth()
33+
auth = createTestAuth(globals.globalState)
3434
authUtil = new AuthUtil(auth)
3535
})
3636

@@ -286,7 +286,7 @@ describe('getChatAuthState()', function () {
286286
let laterDate: Date
287287

288288
beforeEach(async function () {
289-
auth = createTestAuth()
289+
auth = createTestAuth(globals.globalState)
290290
authUtil = new AuthUtil(auth)
291291

292292
laterDate = new Date(Date.now() + 10_000_000)

packages/core/src/auth/secondaryAuth.ts

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,7 @@ export class SecondaryAuth<T extends Connection = Connection> {
9898
public readonly toolId: ToolId,
9999
public readonly toolLabel: string,
100100
public readonly isUsable: (conn: Connection) => conn is T,
101-
private readonly auth: Auth,
102-
private readonly memento = globals.context.globalState
101+
private readonly auth: Auth
103102
) {
104103
const handleConnectionChanged = async (newActiveConn?: Connection) => {
105104
if (newActiveConn === undefined && this.#activeConnection?.id) {
@@ -176,7 +175,9 @@ export class SecondaryAuth<T extends Connection = Connection> {
176175
}
177176

178177
public async saveConnection(conn: T) {
179-
await this.memento.update(this.key, conn.id)
178+
// TODO: fix this
179+
// eslint-disable-next-line aws-toolkits/no-banned-usages
180+
await globals.context.globalState.update(this.key, conn.id)
180181
this.#savedConnection = conn
181182
this.#onDidChangeActiveConnection.fire(this.activeConnection)
182183
}
@@ -207,7 +208,9 @@ export class SecondaryAuth<T extends Connection = Connection> {
207208

208209
/** Stop using the saved connection and fallback to using the active connection, if it is usable. */
209210
public async clearSavedConnection() {
210-
await this.memento.update(this.key, undefined)
211+
// TODO: fix this
212+
// eslint-disable-next-line aws-toolkits/no-banned-usages
213+
await globals.context.globalState.update(this.key, undefined)
211214
this.#savedConnection = undefined
212215
this.#onDidChangeActiveConnection.fire(this.activeConnection)
213216
}
@@ -252,18 +255,21 @@ export class SecondaryAuth<T extends Connection = Connection> {
252255
})
253256

254257
private async loadSavedConnection() {
255-
const id = cast(this.memento.get(this.key), Optional(String))
258+
// TODO: fix this
259+
// eslint-disable-next-line aws-toolkits/no-banned-usages
260+
const globalState = globals.context.globalState
261+
const id = cast(globalState.get(this.key), Optional(String))
256262
if (id === undefined) {
257263
return
258264
}
259265

260266
const conn = await this.auth.getConnection({ id })
261267
if (conn === undefined) {
262268
getLogger().warn(`auth (${this.toolId}): removing saved connection "${this.key}" as it no longer exists`)
263-
await this.memento.update(this.key, undefined)
269+
await globalState.update(this.key, undefined)
264270
} else if (!this.isUsable(conn)) {
265271
getLogger().warn(`auth (${this.toolId}): saved connection "${this.key}" is not valid`)
266-
await this.memento.update(this.key, undefined)
272+
await globalState.update(this.key, undefined)
267273
} else {
268274
await this.auth.refreshConnectionState(conn)
269275
return conn

packages/core/src/dev/activation.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export type DevOptions = {
4848
}
4949

5050
let targetContext: vscode.ExtensionContext
51+
let globalState: vscode.Memento
5152
let targetAuth: Auth
5253

5354
/**
@@ -127,7 +128,7 @@ export class DevDocumentProvider implements vscode.TextDocumentContentProvider {
127128
} else if (uri.path.startsWith('/globalstate')) {
128129
// lol hax
129130
// as of November 2023, all of a memento's properties are stored as property `f` when minified
130-
return JSON.stringify((targetContext.globalState as any).f, undefined, 4)
131+
return JSON.stringify((globalState as any).f, undefined, 4)
131132
} else {
132133
return `unknown URI path: ${uri}`
133134
}
@@ -154,6 +155,8 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
154155
// Internal command to open dev menu for a specific context and options
155156
vscode.commands.registerCommand('_aws.dev.invokeMenu', (opts: DevOptions) => {
156157
targetContext = opts.context
158+
// eslint-disable-next-line aws-toolkits/no-banned-usages
159+
globalState = targetContext.globalState
157160
targetAuth = opts.auth
158161
void openMenu(
159162
entries(menuOptions)
@@ -298,7 +301,7 @@ class ObjectEditor {
298301
case 'globalsView':
299302
return showState('globalstate')
300303
case 'globals':
301-
return this.openState(targetContext.globalState, key)
304+
return this.openState(globalState, key)
302305
case 'secrets':
303306
return this.openState(targetContext.secrets, key)
304307
case 'auth':
@@ -381,7 +384,7 @@ async function openStorageFromInput() {
381384
return new SkipPrompter('')
382385
} else if (target === 'globals') {
383386
// List all globalState keys in the quickpick menu.
384-
const items = targetContext.globalState
387+
const items = globalState
385388
.keys()
386389
.map((key) => {
387390
return {

packages/core/src/shared/credentials/credentialsProfileMru.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,21 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
import * as vscode from 'vscode'
6+
import globals from '../extensionGlobals'
77

88
/**
99
* Tracks the credentials selected by the user, ordered by most recent.
1010
*/
1111
export class CredentialsProfileMru {
1212
public static readonly maxCredentialMruSize = 5
1313

14-
private static readonly configurationStateName: string = 'recentCredentials'
15-
16-
public constructor(private readonly _context: vscode.ExtensionContext) {}
14+
public constructor() {}
1715

1816
/**
1917
* @description Returns the most recently used credentials names
2018
*/
2119
public getMruList(): string[] {
22-
return this._context.globalState.get<string[]>(CredentialsProfileMru.configurationStateName, [])
20+
return globals.globalState.tryGet<string[]>('recentCredentials', Object, [])
2321
}
2422

2523
/**
@@ -38,6 +36,6 @@ export class CredentialsProfileMru {
3836

3937
mru.splice(CredentialsProfileMru.maxCredentialMruSize)
4038

41-
await this._context.globalState.update(CredentialsProfileMru.configurationStateName, mru)
39+
await globals.globalState.update('recentCredentials', mru)
4240
}
4341
}

packages/core/src/shared/credentials/defaultCredentialSelectionDataProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export class DefaultCredentialSelectionDataProvider implements CredentialSelecti
4646
public readonly existingProfileNames: string[],
4747
protected context: vscode.ExtensionContext
4848
) {
49-
this._credentialsMru = new CredentialsProfileMru(context)
49+
this._credentialsMru = new CredentialsProfileMru()
5050
}
5151

5252
public async pickCredentialProfile(

packages/core/src/shared/extensionGlobals.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ function proxyGlobals(globals_: ToolkitGlobals): ToolkitGlobals {
134134
let globals = proxyGlobals(resolveGlobalsObject())
135135

136136
export function checkDidReload(context: ExtensionContext): boolean {
137+
// TODO: fix this
138+
// eslint-disable-next-line aws-toolkits/no-banned-usages
137139
return !!context.globalState.get<string>('ACTIVATION_LAUNCH_PATH_KEY')
138140
}
139141

@@ -147,6 +149,7 @@ export function initialize(context: ExtensionContext, isWeb: boolean = false): T
147149
context,
148150
clock: copyClock(),
149151
didReload: checkDidReload(context),
152+
// eslint-disable-next-line aws-toolkits/no-banned-usages
150153
globalState: new GlobalState(context.globalState),
151154
manifestPaths: {} as ToolkitGlobals['manifestPaths'],
152155
visualizationResourcePaths: {} as ToolkitGlobals['visualizationResourcePaths'],

packages/core/src/shared/globalState.ts

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type globalKey =
2727
| stepFunctionsKey
2828
| ToolIdStateKey
2929
| JsonSchemasKey
30+
| 'amazonq.telemetry.migrated'
3031
| 'aws.amazonq.codewhisperer.newCustomizations'
3132
| 'aws.amazonq.hasShownWalkthrough'
3233
| 'aws.amazonq.showTryChatCodeLens'
@@ -61,6 +62,8 @@ type globalKey =
6162
| 'region'
6263
// TODO: implement this via `PromptSettings` instead of globalState.
6364
| 'sam.sync.updateMessage'
65+
| 'telemetryClientId'
66+
| 'telemetryId'
6467

6568
/**
6669
* Extension-local (not visible to other vscode extensions) shared state which persists after IDE
@@ -75,7 +78,7 @@ type globalKey =
7578
* - garbage collection
7679
*/
7780
export class GlobalState implements vscode.Memento {
78-
constructor(private readonly memento: vscode.Memento) {}
81+
constructor(protected readonly memento: vscode.Memento) {}
7982

8083
keys(): readonly string[] {
8184
return this.memento.keys()
@@ -93,9 +96,9 @@ export class GlobalState implements vscode.Memento {
9396
* {@link String}, {@link Boolean}, etc.
9497
* @param defaultVal Value returned if `key` has no value.
9598
*/
96-
getStrict<T>(key: globalKey, type: TypeConstructor<T>, defaulVal?: T) {
99+
getStrict<T>(key: globalKey, type: TypeConstructor<T>, defaultVal?: T) {
97100
try {
98-
const val = this.memento.get<T>(key) ?? defaulVal
101+
const val = this.memento.get<T>(key) ?? defaultVal
99102
return !type || val === undefined ? val : cast(val, type)
100103
} catch (e) {
101104
const msg = `GlobalState: invalid state (or read failed) for key: "${key}"`
@@ -117,27 +120,27 @@ export class GlobalState implements vscode.Memento {
117120
* @param key Key name
118121
* @param defaultVal Value returned if `key` has no value.
119122
*/
120-
get<T>(key: globalKey, defaulVal?: T): T | undefined {
123+
get<T>(key: globalKey, defaultVal?: T): T | undefined {
121124
const skip = (o: any) => o as T // Don't type check.
122-
return this.getStrict(key, skip, defaulVal)
125+
return this.getStrict(key, skip, defaultVal)
123126
}
124127

125128
/**
126-
* Gets the value for `key` if it satisfies the `type` specification, else logs an error and returns `defaulVal`.
129+
* Gets the value for `key` if it satisfies the `type` specification, else logs an error and returns `defaultVal`.
127130
*
128131
* @param key Key name
129132
* @param type Type validator function, or primitive type constructor such as {@link Object},
130133
* {@link String}, {@link Boolean}, etc.
131134
* @param defaultVal Value returned if `key` has no value.
132135
*/
133136
tryGet<T>(key: globalKey, type: TypeConstructor<T>): T | undefined
134-
tryGet<T>(key: globalKey, type: TypeConstructor<T>, defaulVal: T): T
135-
tryGet<T>(key: globalKey, type: TypeConstructor<T>, defaulVal?: T): T | undefined {
137+
tryGet<T>(key: globalKey, type: TypeConstructor<T>, defaultVal: T): T
138+
tryGet<T>(key: globalKey, type: TypeConstructor<T>, defaultVal?: T): T | undefined {
136139
try {
137-
return this.getStrict(key, type, defaulVal)
140+
return this.getStrict(key, type, defaultVal)
138141
} catch (e) {
139142
getLogger().error('%s', (e as Error).message)
140-
return defaulVal
143+
return defaultVal
141144
}
142145
}
143146

packages/core/src/shared/telemetry/activation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export async function activate(
4444
await config.initAmazonQSetting() // TODO: Remove after a few releases.
4545

4646
DefaultTelemetryClient.productName = productName
47-
globals.telemetry = await DefaultTelemetryService.create(extensionContext, awsContext, getComputeRegion())
47+
globals.telemetry = await DefaultTelemetryService.create(awsContext, getComputeRegion())
4848

4949
const isAmazonQExt = isAmazonQ()
5050
try {

packages/core/src/shared/telemetry/telemetryService.ts

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,11 @@ export class DefaultTelemetryService {
4949
* Use {@link create}() to create an instance of this class.
5050
*/
5151
protected constructor(
52-
private readonly context: ExtensionContext,
5352
private readonly awsContext: AwsContext,
5453
private readonly computeRegion?: string,
5554
publisher?: TelemetryPublisher
5655
) {
57-
this.persistFilePath = path.join(context.globalStorageUri.fsPath, 'telemetryCache')
56+
this.persistFilePath = path.join(globals.context.globalStorageUri.fsPath, 'telemetryCache')
5857

5958
this.startTime = new globals.clock.Date()
6059

@@ -70,14 +69,9 @@ export class DefaultTelemetryService {
7069
* This exists since we need to first ensure the global storage
7170
* path exists before creating the instance.
7271
*/
73-
static async create(
74-
context: ExtensionContext,
75-
awsContext: AwsContext,
76-
computeRegion?: string,
77-
publisher?: TelemetryPublisher
78-
) {
79-
await DefaultTelemetryService.ensureGlobalStorageExists(context)
80-
return new DefaultTelemetryService(context, awsContext, computeRegion, publisher)
72+
static async create(awsContext: AwsContext, computeRegion?: string, publisher?: TelemetryPublisher) {
73+
await DefaultTelemetryService.ensureGlobalStorageExists(globals.context)
74+
return new DefaultTelemetryService(awsContext, computeRegion, publisher)
8175
}
8276

8377
public get logger(): TelemetryLogger {
@@ -219,8 +213,9 @@ export class DefaultTelemetryService {
219213
const clientId = getClientId(globals.globalState)
220214
// grab our Cognito identityId
221215
const poolId = DefaultTelemetryClient.config.identityPool
222-
const identityMapJson = this.context.globalState.get<string>(
216+
const identityMapJson = globals.globalState.tryGet(
223217
DefaultTelemetryService.telemetryCognitoIdKey,
218+
String,
224219
'[]'
225220
)
226221
// Maps don't cleanly de/serialize with JSON.parse/stringify so we need to do it ourselves
@@ -234,7 +229,7 @@ export class DefaultTelemetryService {
234229

235230
// save it
236231
identityMap.set(poolId, identityPublisherTuple.cognitoIdentityId)
237-
await this.context.globalState.update(
232+
await globals.globalState.update(
238233
DefaultTelemetryService.telemetryCognitoIdKey,
239234
JSON.stringify(Array.from(identityMap.entries()))
240235
)

0 commit comments

Comments
 (0)