diff --git a/src/auth/auth.service.ts b/src/auth/auth.service.ts index fa201b7b5..4d77c2461 100644 --- a/src/auth/auth.service.ts +++ b/src/auth/auth.service.ts @@ -145,7 +145,7 @@ export class AuthService { ); const createUserSettingsDto: CreateUserSettingsDto = { userId, - frontendSettings: {}, + externalSettings: {}, }; await this.usersService.createUserSettings(userId, createUserSettingsDto); } diff --git a/src/users/dto/update-user-settings.dto.ts b/src/users/dto/update-user-settings.dto.ts index a9cca316e..63794c6ea 100644 --- a/src/users/dto/update-user-settings.dto.ts +++ b/src/users/dto/update-user-settings.dto.ts @@ -11,13 +11,13 @@ export class UpdateUserSettingsDto { readonly jobCount?: number; @ApiProperty({ - type: "object", - additionalProperties: { type: "array", items: {} }, + type: Object, required: false, + default: {}, }) @IsOptional() @IsObject() - readonly frontendSettings?: Record; + readonly externalSettings?: Record; } export class PartialUpdateUserSettingsDto extends PartialType( diff --git a/src/users/schemas/user-settings.schema.ts b/src/users/schemas/user-settings.schema.ts index 9a629abe7..9aa9a9f08 100644 --- a/src/users/schemas/user-settings.schema.ts +++ b/src/users/schemas/user-settings.schema.ts @@ -45,12 +45,12 @@ export class UserSettings { @ApiProperty({ type: "object", - additionalProperties: { type: "array", items: {} }, default: {}, - description: "users preferred ui settings in dataset table", + description: + "A customizable object for storing the user's external settings, which can contain various nested properties and configurations.", }) @Prop({ type: Object, default: {}, required: false }) - frontendSettings?: Record; + externalSettings?: Record; } export const UserSettingsSchema = SchemaFactory.createForClass(UserSettings); diff --git a/src/users/users.controller.spec.ts b/src/users/users.controller.spec.ts index 580e154cb..40ac85311 100644 --- a/src/users/users.controller.spec.ts +++ b/src/users/users.controller.spec.ts @@ -29,7 +29,7 @@ const mockUserSettings = { userId: "user1", datasetCount: 25, jobCount: 25, - frontendSettings: { + externalSettings: { filters: [{ LocationFilter: true }, { PidFilter: true }], conditions: [{ field: "status", value: "active", operator: "equals" }], columns: [], @@ -75,11 +75,15 @@ describe("UsersController", () => { const result = await controller.getSettings(mockRequest as Request, userId); // Assert - expect(result?.frontendSettings).toEqual(mockUserSettings); - expect(result?.frontendSettings?.filters).toBeDefined(); - expect(result?.frontendSettings?.filters.length).toBeGreaterThan(0); - expect(result?.frontendSettings?.conditions).toBeDefined(); - expect(result?.frontendSettings?.conditions.length).toBeGreaterThan(0); + expect(result?.externalSettings).toEqual(mockUserSettings); + expect(result?.externalSettings?.filters).toBeDefined(); + expect( + (result?.externalSettings?.filters as Record).length, + ).toBeGreaterThan(0); + expect(result?.externalSettings?.conditions).toBeDefined(); + expect( + (result?.externalSettings?.conditions as Record).length, + ).toBeGreaterThan(0); }); it("should update user settings with filters and conditions", async () => { @@ -88,7 +92,7 @@ describe("UsersController", () => { const updatedSettings = { ...mockUserSettings, - frontendSettings: { + externalSettings: { filters: [{ PidFilter: true }], conditions: [ { field: "status", value: "inactive", operator: "equals" }, @@ -107,7 +111,7 @@ describe("UsersController", () => { userId: userId, // Ensure all required properties are included datasetCount: updatedSettings.datasetCount, jobCount: updatedSettings.jobCount, - frontendSettings: updatedSettings.frontendSettings, + externalSettings: updatedSettings.externalSettings, }; jest @@ -120,10 +124,15 @@ describe("UsersController", () => { updatedSettings, ); - expect(result?.frontendSettings).toEqual(updatedSettings); - expect(result?.frontendSettings?.filters).toBeDefined(); - expect(result?.frontendSettings?.filters.length).toBe(1); - expect(result?.frontendSettings?.conditions).toBeDefined(); - expect(result?.frontendSettings?.conditions.length).toBe(1); + expect(result?.externalSettings).toEqual(updatedSettings); + expect(result?.externalSettings?.filters).toBeDefined(); + expect( + (result?.externalSettings?.filters as Record).length, + ).toBe(1); + expect(result?.externalSettings?.conditions).toBeDefined(); + expect( + (result?.externalSettings?.conditions as Record) + .length, + ).toBe(1); }); }); diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 2822c28c7..0747b450c 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -284,19 +284,43 @@ export class UsersController { async patchSettings( @Req() request: Request, @Param("id") id: string, - updateUserSettingsDto: PartialUpdateUserSettingsDto, + @Body() updateUserSettingsDto: PartialUpdateUserSettingsDto, ): Promise { await this.checkUserAuthorization( request, [Action.UserUpdateAny, Action.UserUpdateOwn], id, ); - return this.usersService.findOneAndUpdateUserSettings( + return this.usersService.findOneAndPatchUserSettings( id, updateUserSettingsDto, ); } + @UseGuards(AuthenticatedPoliciesGuard) + @CheckPolicies( + "users", + (ability: AppAbility) => + ability.can(Action.UserUpdateOwn, User) || + ability.can(Action.UserUpdateAny, User), + ) + @Patch("/:id/settings/external") + async patchExternalSettings( + @Req() request: Request, + @Param("id") id: string, + @Body() externalSettings: Record, + ): Promise { + await this.checkUserAuthorization( + request, + [Action.UserUpdateAny, Action.UserUpdateOwn], + id, + ); + return this.usersService.findOneAndPatchUserExternalSettings( + id, + externalSettings, + ); + } + @UseGuards(AuthenticatedPoliciesGuard) @CheckPolicies( "users", diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 4c479c1b3..c94394aa0 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -296,6 +296,34 @@ export class UsersService implements OnModuleInit { return result; } + async findOneAndPatchUserSettings( + userId: string, + updateUserSettingsDto: UpdateUserSettingsDto | PartialUpdateUserSettingsDto, + ): Promise { + const result = await this.userSettingsModel + .findOneAndUpdate( + { userId }, + { $set: updateUserSettingsDto }, + { new: true }, + ) + .exec(); + return result; + } + + async findOneAndPatchUserExternalSettings( + userId: string, + externalSettings: Record, + ): Promise { + const updateQuery: Record = {}; + + for (const [key, value] of Object.entries(externalSettings)) { + updateQuery[`externalSettings.${key}`] = value; + } + const result = await this.userSettingsModel + .findOneAndUpdate({ userId }, { $set: updateQuery }, { new: true }) + .exec(); + return result; + } async findOneAndDeleteUserSettings(userId: string): Promise { return this.userSettingsModel.findOneAndDelete({ userId }).exec(); }