History

fix(web): Remove saved environments atomically (#2917)

Mike Olson committed 4 months ago

Commit 1fcc57a1 parent e95b57d2 files · +52 −0Browse files

Unified · Split

2 files
  1. apps/desktop/src/settings/DesktopSavedEnvironments.test.ts +20 −0
  2. apps/desktop/src/settings/DesktopSavedEnvironments.ts +32 −0
apps/desktop/src/settings/DesktopSavedEnvironments.test.ts+20−0
@@ -272,6 +272,26 @@ describe("DesktopSavedEnvironments", () => { · 271 unmodified lines
272272 ),
273273 );
274274
275+ it.effect("removes saved environment metadata and its embedded secret atomically", () =>
276+ withSavedEnvironments(
277+ Effect.gen(function* () {
278+ const savedEnvironments = yield* DesktopSavedEnvironments.DesktopSavedEnvironments;
279+ yield* savedEnvironments.setRegistry([savedRegistryRecord]);
280+ yield* savedEnvironments.setSecret({
281+ environmentId: savedRegistryRecord.environmentId,
282+ secret: "bearer-token",
283+ });
284+
285+ yield* savedEnvironments.removeEnvironment(savedRegistryRecord.environmentId);
286+
287+ assert.deepEqual(yield* savedEnvironments.getRegistry, []);
288+ assert.isTrue(
289+ Option.isNone(yield* savedEnvironments.getSecret(savedRegistryRecord.environmentId)),
290+ );
291+ }),
292+ ),
293+ );
294+
275295 it.effect("treats empty saved environment documents as empty", () =>
276296 withSavedEnvironments(
277297 Effect.gen(function* () {
apps/desktop/src/settings/DesktopSavedEnvironments.ts+32−0
@@ -121,6 +121,9 @@ export interface DesktopSavedEnvironmentsShape { · 120 unmodified lines
121121 readonly setRegistry: (
122122 records: readonly PersistedSavedEnvironmentRecord[],
123123 ) => Effect.Effect<void, DesktopSavedEnvironmentsWriteError>;
124+ readonly removeEnvironment: (
125+ environmentId: string,
126+ ) => Effect.Effect<void, DesktopSavedEnvironmentsWriteError>;
124127 readonly getSecret: (
125128 environmentId: string,
126129 ) => Effect.Effect<Option.Option<string>, DesktopSavedEnvironmentsGetSecretError>;
@@ -293,6 +296,23 @@ export const layer = Layer.effect( · 166 unmodified lines
293296 ).pipe(Effect.mapError((cause) => new DesktopSavedEnvironmentsWriteError({ cause })));
294297 yield* writeDocument(preserveExistingSecrets(currentDocument, records));
295298 }),
299+ removeEnvironment: Effect.fn("desktop.savedEnvironments.removeEnvironment")(
300+ function* (environmentId) {
301+ yield* Effect.annotateCurrentSpan({ environmentId });
302+ const document = yield* readRegistryDocument(
303+ fileSystem,
304+ environment.savedEnvironmentRegistryPath,
305+ ).pipe(Effect.mapError((cause) => new DesktopSavedEnvironmentsWriteError({ cause })));
306+ if (!document.records.some((record) => record.environmentId === environmentId)) {
307+ return;
308+ }
309+
310+ yield* writeDocument({
311+ version: document.version,
312+ records: document.records.filter((record) => record.environmentId !== environmentId),
313+ });
314+ },
315+ ),
296316 getSecret: Effect.fn("desktop.savedEnvironments.getSecret")(function* (environmentId) {
297317 yield* Effect.annotateCurrentSpan({ environmentId });
298318 const document = yield* readRegistryDocument(
@@ -385,6 +405,18 @@ export const layerTest = (input?: { · 86 unmodified lines
385405 return DesktopSavedEnvironments.of({
386406 getRegistry: Ref.get(recordsRef),
387407 setRegistry: (records) => Ref.set(recordsRef, records),
408+ removeEnvironment: (environmentId) =>
409+ Ref.update(recordsRef, (records) =>
410+ records.filter((record) => record.environmentId !== environmentId),
411+ ).pipe(
412+ Effect.andThen(
413+ Ref.update(secretsRef, (secrets) => {
414+ const nextSecrets = new Map(secrets);
415+ nextSecrets.delete(environmentId);
416+ return nextSecrets;
417+ }),
418+ ),
419+ ),
388420 getSecret: (environmentId) =>
389421 Ref.get(secretsRef).pipe(
390422 Effect.map((secrets) => Option.fromNullishOr(secrets.get(environmentId))),