-
Notifications
You must be signed in to change notification settings - Fork 14
cleanup: remove StorageManager
entirely
#1858
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
… Memento/SecretStorage interfaces
…s to make test stubbing easier
…e/.workspaceState/.secretStorage directly
…Manager; use MigrationStorageType enums
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
1 similar comment
This comment has been minimized.
This comment has been minimized.
/** Clears all keys from the `workspaceState` of the {@link ExtensionContext}. */ | ||
export async function clearWorkspaceState(): Promise<void> { | ||
const workspaceState: WorkspaceState = getWorkspaceState(); | ||
const keys: readonly string[] = workspaceState.keys(); | ||
const deletePromises: Thenable<void>[] = keys.map((key) => workspaceState.update(key, undefined)); | ||
await Promise.all(deletePromises); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the only method from StorageManager worth saving
const setSecretStub = sandbox.stub(getStorageManager(), "setSecret").resolves(); | ||
const setCCloudAuthStatusStub = sandbox | ||
.stub(ResourceManager.getInstance(), "setCCloudAuthStatus") | ||
.resolves(); | ||
getCCloudConnectionStub.resolves(TEST_AUTHENTICATED_CCLOUD_CONNECTION); | ||
// authentication completes successfully | ||
browserAuthFlowStub.resolves({ success: true, resetPassword: false }); | ||
|
||
await authProvider.createSession(); | ||
|
||
sinon.assert.calledWith( | ||
setSecretStub, | ||
SecretStorageKeys.CCLOUD_AUTH_STATUS, | ||
setCCloudAuthStatusStub, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to stub the call made more closely to this portion of createSession()
:
vscode/src/authn/ccloudProvider.ts
Lines 188 to 192 in 602c956
// update the auth status in the secret store so other workspaces can be notified of the change | |
// and the middleware doesn't get an outdated status before the poller can update it | |
await getResourceManager().setCCloudAuthStatus( | |
authenticatedConnection.status.authentication.status, | |
); |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
await getStorageManager().deleteWorkspaceState(WorkspaceStorageKeys.DIFF_BASE_URI); | ||
await workspaceState.update(WorkspaceStorageKeys.DIFF_BASE_URI, undefined); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Memento doesn't have a dedicated delete
method: https://github.com/DefinitelyTyped/DefinitelyTyped/blob/227fe5bd71ef4cc4fdaea251ccbc8d9056c3c24e/types/vscode/index.d.ts#L8559-L8568
@@ -261,7 +260,7 @@ async function _activateExtension( | |||
// set up the local Docker event listener singleton and start watching for system events | |||
EventListener.getInstance().start(); | |||
// reset the Docker credentials secret so `src/docker/configs.ts` can pull it fresh | |||
getStorageManager().deleteSecret(SecretStorageKeys.DOCKER_CREDS_SECRET_KEY); | |||
void context.secrets.delete(SecretStorageKeys.DOCKER_CREDS_SECRET_KEY); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Starting to explicitly add void
where we don't want to await/handle the promise here (as part of #1498)
export type MigrationStorageType = "global" | "workspace" | "secret"; | ||
export enum MigrationStorageType { | ||
GLOBAL = "global", | ||
WORKSPACE = "workspace", | ||
SECRET = "secret", | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unrelated to this PR, but made it easier to update test assertions
async clearGlobalState(): Promise<void> { | ||
const keys = await this.getGlobalStateKeys(); | ||
await Promise.all(keys.map((key) => this.deleteGlobalState(key))); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This hasn't been used in months
type TestMigrationSetup = [ | ||
keyof StorageManager, | ||
(manager: StorageManager) => Promise<void>, | ||
MigrationStorageType, | ||
keyof StorageManager, | ||
]; | ||
const testSetup: TestMigrationSetup[] = [ | ||
["getGlobalState", migrateGlobalState, "global", "setGlobalState"], | ||
["getWorkspaceState", migrateWorkspaceState, "workspace", "setWorkspaceState"], | ||
["getSecret", migrateSecretStorage, "secret", "setSecret"], | ||
]; | ||
|
||
for (const [ | ||
managerGetMethodName, | ||
migrationFunc, | ||
storageType, | ||
managerSetMethodName, | ||
] of testSetup) { | ||
it(`migrate*() for "${storageType}" state/storage should call executeMigrations() when storage version is incorrect`, async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This setup was getting a little too complicated; I figured it was best to split up into explicit tests for easier maintenance
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR removes the StorageManager
abstraction and shifts all storage interactions to lightweight helpers (getWorkspaceState
, getGlobalState
, getSecretStorage
, etc.), simplifying and streamlining how state and secrets are accessed and tested.
- Replaced all
StorageManager
calls with direct state/secret helpers in code and tests - Updated migrations and
migrationManager
to use typed enums and storage utils - Refactored tests to stub the new helpers and removed singleton-based stubbing boilerplate
Reviewed Changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated no comments.
Show a summary per file
File | Description |
---|---|
src/storage/resourceManager.test.ts | Switched from StorageManager to getWorkspaceState /clearWorkspaceState |
src/storage/migrations/v2.ts | Replaced ExtensionContext.secrets with getSecretStorage() |
src/storage/migrations/base.ts | Switched store-type strings to MigrationStorageType enum values |
src/storage/migrationManager.ts | Removed StorageManager param; uses helpers for all storage ops |
src/storage/migrationManager.test.ts | Refactored tests to stub new helpers; removed singleton manager stubs |
src/storage/index.ts | Deleted StorageManager class entirely |
src/storage/constants.ts | Introduced MigrationStorageType enum; cleaned up comments |
src/sidecar/sidecarManager.ts | Replaced secret storage calls with getSecretStorage() |
src/sidecar/middlewares.ts | Swapped getExtensionContext().secrets for getSecretStorage() |
src/loaders/resourceLoader.test.ts | Replaced test activation stubs to use getTestExtensionContext() |
src/flinkSql/languageClient.ts | Swapped getStorageManager().getSecret for getSecretStorage().get |
src/extension.ts | Updated extension activation to use setupStorage() helpers |
src/extension.test.ts | Removed StorageManager from activation tests |
src/docker/configs.ts | Replaced StorageManager in Docker creds logic with getSecretStorage |
src/directConnectManager.ts | Swapped secret-change listener to getSecretStorage().onDidChange |
src/commands/diffs.ts | Replaced workspace-state calls with getWorkspaceState() |
src/commands/diffs.test.ts | Updated tests to read/write from context.workspaceState |
src/authn/ccloudProvider.ts | Replaced all secret calls with getSecretStorage() |
src/authn/ccloudProvider.test.ts | Refactored tests to stub getSecretStorage() and clear state via helper |
eslint.config.mjs | Commented out require-await rule |
Comments suppressed due to low confidence (4)
src/storage/migrationManager.test.ts:11
- The tests reference
CODEBASE_STORAGE_VERSION
but it’s not imported, which causes runtime ReferenceErrors. Please add an import forCODEBASE_STORAGE_VERSION
(e.g.import { CODEBASE_STORAGE_VERSION } from './migrationManager';
).
import { DURABLE_STORAGE_VERSION_KEY, MigrationStorageType } from "./constants";
src/sidecar/sidecarManager.ts:548
- [nitpick] The variable
existing_secret
uses snake_case and should be renamed toexistingSecret
(ortoken
,cachedToken
) to follow the project's camelCase naming conventions.
const existing_secret: string | undefined = await getSecretStorage().get(
src/storage/utils.ts:1
- The new
clearWorkspaceState
helper isn't covered by unit tests. Adding dedicated tests (e.g. setting and then clearing keys) will ensure it behaves as expected.
export function clearWorkspaceState() {
eslint.config.mjs:36
- [nitpick] This commented-out ESLint rule adds noise. Consider either removing it or re-enabling
require-await
by addressing the flagged functions incrementally to enforce consistency.
// "require-await": "warn",
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
Summary of Changes
ℹ️ Click for background info
Back in the early days of this extension's development, I was annoyed that most extension examples showed the
ExtensionContext
being passed around to various functions in order to call things likeglobalState
/workspaceState
/secrets
, so I put together theStorageManager
singleton class as an easy way of working with extension state.Fast forward a bit and we added some basic get/set logic for the
ExtensionContext
, which was then used in various places when we had to access the package.json info, static root, etc.Eventually, we started noticing that
StorageManager
was missing some methods and event listeners, and it was lacking compared to interacting with the Memento (globalState
/workspaceState
) and SecretStorage (secrets
) interfaces directly, so some parts of the codebase just started usinggetExtensionContext().___
.On top of that, we started adding the whole
ResourceLoader
layer to incorporate some caching. At this point we ended up with the following stack for managing storage for our primary resources:StorageManager was arguably the weakest layer, and only added to maintenance headaches and complicating the mental model.
This PR gets rid of the entire
StorageManager
layer we had between the extension and the VS Code Memento/SecretStorage calls.If I had it the way I originally intended with this branch, it would've meant callers would use
getExtensionContext()
and work with the ExtensionContext'sglobalState
,workspaceState
, andsecrets
directly, but unfortunately they don't play nicely with sinon stubbing and throwTypeError: Descriptor for property [globalState|workspaceState|secrets] is non-configurable and non-writable
errors.So for a happy middle-ground, we have a few little convenience functions that lend themselves nicely to testing while also pointing directly at Memento/SecretStorage, with an arguably smaller mental model required for use:
vscode/src/storage/utils.ts
Lines 6 to 31 in d7ed387
However, there were a lot of places dealing with the StorageManager, so many of the other files edited here are updating imports and caller syntax.
Closes #1669.
Any additional details or context that should be provided?
stubs.ts
with helper functions to attachsinon
stubs/stubbedInstances/spies/etc to sandboxes #508). As a result,tests/stubs/
was created withextensionStorage.ts
to include a few helper functions to get stubbed "instances" of Memento/SecretStorage:getStubbedGlobalState
,getStubbedWorkspaceState
, andgetStubbedSecretStorage
. Each of these return fully-stubbed objects that can easily be adjusted per-test/-suite and should help reduce boilerplate. Example of working with it in a test (fromstorage/migrationManager.test.ts
) where we previously had to set up two stubs while only altering the return value of one:globalState.get
/workspaceState.get
calls, but the ResourceManager methods using them are left asasync
because changing them would've meant blowing this branch up even more.require-await
eslint rule, but then had to comment it out due to it flagging 150+ warnings and failing in CI:Pull request checklist
Please check if your PR fulfills the following (if applicable):
Tests
Other
.vsix
file?