diff --git a/packages/lib/models/Setting.test.ts b/packages/lib/models/Setting.test.ts index a282e1936e..80d3e17ab4 100644 --- a/packages/lib/models/Setting.test.ts +++ b/packages/lib/models/Setting.test.ts @@ -330,6 +330,25 @@ describe('models/Setting', function() { }); }); + it('should not erase settings of parent profile', async () => { + // When a sub-profile settings are saved, we should ensure that the + // local settings of the root profiles are not lost. + // https://github.com/laurent22/joplin/issues/6459 + + await Setting.reset(); + + Setting.setValue('sync.target', 9); // Local setting (Root profile) + await Setting.saveAll(); + + await switchToSubProfileSettings(); + + Setting.setValue('sync.target', 2); // Local setting (Sub-profile) + await Setting.saveAll(); + + const globalSettings = JSON.parse(await readFile(`${Setting.value('rootProfileDir')}/settings-1.json`, 'utf8')); + expect(globalSettings['sync.target']).toBe(9); + }); + it('all global settings should be saved to file', async () => { for (const [k, v] of Object.entries(Setting.metadata())) { if (v.isGlobal && v.storage !== SettingStorage.File) throw new Error(`Setting "${k}" is global but storage is not "file"`); diff --git a/packages/lib/models/Setting.ts b/packages/lib/models/Setting.ts index 012e4fa691..b5e320986e 100644 --- a/packages/lib/models/Setting.ts +++ b/packages/lib/models/Setting.ts @@ -305,7 +305,7 @@ class Setting extends BaseModel { return BaseModel.TYPE_SETTING; } - static async reset() { + public static async reset() { if (this.saveTimeoutId_) shim.clearTimeout(this.saveTimeoutId_); if (this.changeEventTimeoutId_) shim.clearTimeout(this.changeEventTimeoutId_); @@ -316,6 +316,7 @@ class Setting extends BaseModel { this.cache_ = []; this.customMetadata_ = {}; this.fileHandler_ = null; + this.rootFileHandler_ = null; } public static get settingFilePath(): string { @@ -2156,7 +2157,17 @@ class Setting extends BaseModel { if (this.canUseFileStorage()) { if (this.value('isSubProfile')) { const { globalSettings, localSettings } = splitGlobalAndLocalSettings(valuesForFile); - await this.rootFileHandler.save(globalSettings); + const currentGlobalSettings = await this.rootFileHandler.load(); + + // When saving to the root setting file, we preserve the + // existing settings, which are specific to the root profile, + // and add the global settings. + + await this.rootFileHandler.save({ + ...currentGlobalSettings, + ...globalSettings, + }); + await this.fileHandler.save(localSettings); } else { await this.fileHandler.save(valuesForFile); diff --git a/packages/lib/testing/test-utils.ts b/packages/lib/testing/test-utils.ts index 78dce7e0a1..2a23aa04ba 100644 --- a/packages/lib/testing/test-utils.ts +++ b/packages/lib/testing/test-utils.ts @@ -255,6 +255,10 @@ async function afterAllCleanUp() { } } +const settingFilename = (id: number): string => { + return `settings-${id}.json`; +}; + async function switchClient(id: number, options: any = null) { options = Object.assign({}, { keychainEnabled: false }, options); @@ -271,18 +275,19 @@ async function switchClient(id: number, options: any = null) { BaseItem.revisionService_ = revisionServices_[id]; await Setting.reset(); - Setting.settingFilename = `settings-${id}.json`; + Setting.settingFilename = settingFilename(id); Setting.setConstant('profileDir', rootProfileDir); Setting.setConstant('rootProfileDir', rootProfileDir); Setting.setConstant('resourceDirName', resourceDirName(id)); Setting.setConstant('resourceDir', resourceDir(id)); Setting.setConstant('pluginDir', pluginDir(id)); + Setting.setConstant('isSubProfile', false); await loadKeychainServiceAndSettings(options.keychainEnabled ? KeychainServiceDriver : KeychainServiceDriverDummy); Setting.setValue('sync.target', syncTargetId()); - Setting.setValue('sync.wipeOutFailSafe', false); // To keep things simple, always disable fail-safe unless explicitely set in the test itself + Setting.setValue('sync.wipeOutFailSafe', false); // To keep things simple, always disable fail-safe unless explicitly set in the test itself // More generally, this function should clear all data, and so that should // include settings.json @@ -336,6 +341,7 @@ async function setupDatabase(id: number = null, options: any = null) { Setting.setConstant('profileDir', rootProfileDir); Setting.setConstant('rootProfileDir', rootProfileDir); + Setting.setConstant('isSubProfile', false); if (databases_[id]) { BaseModel.setDb(databases_[id]);