Skip to content

Commit 76f72d8

Browse files
authored
fix: atomic writes to preferences COMPASS-7270 COMPASS-7269 (#4920)
1 parent c2cc547 commit 76f72d8

File tree

6 files changed

+99
-6
lines changed

6 files changed

+99
-6
lines changed

package-lock.json

Lines changed: 63 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/compass-preferences-model/src/storage.spec.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,23 @@ describe('storage', function () {
136136
).to.deep.equal(getDefaultPreferences());
137137
});
138138

139+
it('when invalid json is stored, it sets the defaults', async function () {
140+
const storage = new StoragePreferences(tmpDir);
141+
142+
const preferencesFile = getPreferencesFile(tmpDir);
143+
await fs.mkdir(getPreferencesFolder(tmpDir));
144+
await fs.writeFile(preferencesFile, '{}}', 'utf-8');
145+
146+
// Ensure it exists
147+
expect(async () => await fs.access(preferencesFile)).to.not.throw;
148+
149+
await storage.setup();
150+
151+
expect(
152+
JSON.parse((await fs.readFile(preferencesFile)).toString())
153+
).to.deep.equal(getDefaultPreferences());
154+
});
155+
139156
it('updates preferences', async function () {
140157
const storage = new StoragePreferences(tmpDir);
141158
await storage.setup();

packages/compass-preferences-model/src/storage.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,15 @@ export class StoragePreferences extends BasePreferencesStorage {
7474
try {
7575
this.preferences = await this.readPreferences();
7676
} catch (e) {
77-
if ((e as any).code !== 'ENOENT') {
78-
throw e;
77+
if (
78+
(e as any).code === 'ENOENT' || // First time user
79+
e instanceof SyntaxError // Invalid json
80+
) {
81+
// Create the file for the first time
82+
await this.userData.write(this.file, this.defaultPreferences);
83+
return;
7984
}
80-
// Create the file for the first time
81-
await this.userData.write(this.file, this.defaultPreferences);
85+
throw e;
8286
}
8387
}
8488

packages/compass-user-data/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
"@types/chai": "^4.2.21",
5959
"@types/mocha": "^9.0.0",
6060
"@types/sinon-chai": "^3.2.5",
61+
"@types/write-file-atomic": "^4.0.1",
6162
"chai": "^4.3.6",
6263
"depcheck": "^1.4.1",
6364
"eslint": "^7.25.0",
@@ -67,6 +68,7 @@
6768
"prettier": "^2.7.1",
6869
"sinon": "^9.2.3",
6970
"typescript": "^5.0.4",
71+
"write-file-atomic": "^5.0.1",
7072
"zod": "^3.22.2"
7173
}
7274
}

packages/compass-user-data/src/user-data.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import path from 'path';
44
import { createLoggerAndTelemetry } from '@mongodb-js/compass-logging';
55
import { getStoragePath } from '@mongodb-js/compass-utils';
66
import type { z } from 'zod';
7+
import writeFile from 'write-file-atomic';
78

89
const { log, mongoLogId } = createLoggerAndTelemetry('COMPASS-USER-STORAGE');
910

@@ -172,7 +173,7 @@ export class UserData<T extends z.Schema> {
172173
const filepath = this.getFileName(id);
173174
const absolutePath = await this.getFileAbsolutePath(filepath);
174175
try {
175-
await fs.writeFile(absolutePath, this.serialize(content), {
176+
await writeFile(absolutePath, this.serialize(content), {
176177
encoding: 'utf-8',
177178
});
178179
return true;

packages/compass/src/main/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,13 @@ async function main(): Promise<void> {
108108
});
109109
}
110110

111-
await CompassApplication.init(mode, globalPreferences);
111+
try {
112+
await CompassApplication.init(mode, globalPreferences);
113+
} catch (e) {
114+
await handleUncaughtException(e as Error);
115+
await CompassApplication.runExitHandlers().finally(() => app.exit(1));
116+
return;
117+
}
112118

113119
if (mode === 'CLI') {
114120
let exitCode = 0;

0 commit comments

Comments
 (0)