diff --git a/services/web/app/src/Features/Project/UserSettingsHelper.mjs b/services/web/app/src/Features/Project/UserSettingsHelper.mjs index bb6ba50c4c..93d751e0db 100644 --- a/services/web/app/src/Features/Project/UserSettingsHelper.mjs +++ b/services/web/app/src/Features/Project/UserSettingsHelper.mjs @@ -13,6 +13,26 @@ function getOverallTheme(user) { return 'system' } +/** + * Build the settings for a reference provider (zotero, mendeley, papers). + * + * The group entries are rebuilt explicitly so that the `_id` stored on older + * documents doesn't leak to the frontend, which posts these settings back to us + * unchanged. + * + * @param {object | undefined} settings the provider settings from `user.ace` + */ +function buildRefProviderSettings(settings) { + if (settings == null) { + return settings + } + return { + enabled: settings.enabled, + disablePersonalLibrary: settings.disablePersonalLibrary, + groups: (settings.groups ?? []).map(group => ({ id: group.id })), + } +} + async function buildUserSettings(_req, _res, user) { return { mode: user.ace.mode, @@ -35,9 +55,9 @@ async function buildUserSettings(_req, _res, user) { referencesSearchMode: user.ace.referencesSearchMode, darkModePdf: user.ace.darkModePdf ?? false, floatingMenu: user.ace.floatingMenu ?? true, - zotero: user.ace.zotero, - mendeley: user.ace.mendeley, - papers: user.ace.papers, + zotero: buildRefProviderSettings(user.ace.zotero), + mendeley: buildRefProviderSettings(user.ace.mendeley), + papers: buildRefProviderSettings(user.ace.papers), } } diff --git a/services/web/app/src/Features/User/UserController.mjs b/services/web/app/src/Features/User/UserController.mjs index 19e1dcd960..9b3628cec7 100644 --- a/services/web/app/src/Features/User/UserController.mjs +++ b/services/web/app/src/Features/User/UserController.mjs @@ -364,7 +364,7 @@ async function unsubscribe(req, res, next) { const refProviderSettingsSchema = z .strictObject({ enabled: z.boolean().optional(), - groups: z.array(z.strictObject({ id: z.string() })).optional(), + groups: z.array(z.object({ id: z.string() })).optional(), disablePersonalLibrary: z.boolean().optional(), }) .optional() diff --git a/services/web/app/src/models/User.mjs b/services/web/app/src/models/User.mjs index 2555e748c8..026ebcf7d9 100644 --- a/services/web/app/src/models/User.mjs +++ b/services/web/app/src/models/User.mjs @@ -12,6 +12,7 @@ const refProviderSettingsSchema = { groups: { type: [ { + _id: false, id: { type: String }, }, ], diff --git a/services/web/test/unit/src/Project/UserSettingsHelper.test.mjs b/services/web/test/unit/src/Project/UserSettingsHelper.test.mjs index 35b71537dc..56e70c74c7 100644 --- a/services/web/test/unit/src/Project/UserSettingsHelper.test.mjs +++ b/services/web/test/unit/src/Project/UserSettingsHelper.test.mjs @@ -56,4 +56,43 @@ describe('UserSettingsHelper', function () { expect(settings.overallTheme).toBe('') }) }) + + describe('reference provider settings', function () { + it('should drop the _id from group entries', async function (ctx) { + const user = { + ace: { + zotero: { + enabled: true, + disablePersonalLibrary: false, + groups: [{ _id: 'abc123', id: '123' }], + }, + }, + signUpDate: new Date('2022-01-01'), + } + + const settings = await ctx.UserSettingsHelper.buildUserSettings( + ctx.req, + ctx.res, + user + ) + + expect(settings.zotero).toEqual({ + enabled: true, + disablePersonalLibrary: false, + groups: [{ id: '123' }], + }) + }) + + it('should leave unset providers undefined', async function (ctx) { + const user = { ace: {}, signUpDate: new Date('2022-01-01') } + + const settings = await ctx.UserSettingsHelper.buildUserSettings( + ctx.req, + ctx.res, + user + ) + + expect(settings.zotero).toBeUndefined() + }) + }) }) diff --git a/services/web/test/unit/src/User/UserController.test.mjs b/services/web/test/unit/src/User/UserController.test.mjs index db6417be21..b6c9516beb 100644 --- a/services/web/test/unit/src/User/UserController.test.mjs +++ b/services/web/test/unit/src/User/UserController.test.mjs @@ -625,6 +625,21 @@ describe('UserController', function () { }) }) + it('should drop the _id from zotero groups', function (ctx) { + return new Promise(resolve => { + ctx.req.body = { + zotero: { + groups: [{ _id: 'abc123', id: '123' }], + }, + } + ctx.res.sendStatus = code => { + ctx.user.ace.zotero.groups.should.deep.equal([{ id: '123' }]) + resolve() + } + ctx.UserController.updateUserSettings(ctx.req, ctx.res) + }) + }) + it('should set zotero settings with partial update', function (ctx) { return new Promise(resolve => { ctx.user.ace.zotero = {