refactor(server): rename settings to user-settings (#11161)

This commit is contained in:
fengmk2
2025-03-25 08:47:27 +00:00
parent dda3103d1b
commit a8c86c5ede
17 changed files with 110 additions and 105 deletions

View File

@@ -1,12 +1,12 @@
-- CreateTable
CREATE TABLE "settings" (
CREATE TABLE "user_settings" (
"user_id" VARCHAR NOT NULL,
"created_at" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMPTZ(3) NOT NULL,
"payload" JSONB NOT NULL,
CONSTRAINT "settings_pkey" PRIMARY KEY ("user_id")
CONSTRAINT "user_settings_pkey" PRIMARY KEY ("user_id")
);
-- AddForeignKey
ALTER TABLE "settings" ADD CONSTRAINT "settings_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE "user_settings" ADD CONSTRAINT "user_settings_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -40,7 +40,7 @@ model User {
createdAiJobs AiJobs[] @relation("createdAiJobs")
// receive notifications
notifications Notification[] @relation("user_notifications")
settings Settings?
settings UserSettings?
@@index([email])
@@map("users")
@@ -747,7 +747,7 @@ model Notification {
@@map("notifications")
}
model Settings {
model UserSettings {
userId String @id @map("user_id") @db.VarChar
createdAt DateTime @default(now()) @map("created_at") @db.Timestamptz(3)
updatedAt DateTime @updatedAt @map("updated_at") @db.Timestamptz(3)
@@ -755,5 +755,5 @@ model Settings {
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("settings")
@@map("user_settings")
}

View File

@@ -2,9 +2,9 @@ export * from './blobs';
export * from './invite';
export * from './notification';
export * from './permission';
export * from './settings';
export * from './testing-app';
export * from './testing-module';
export * from './user';
export * from './user-settings';
export * from './utils';
export * from './workspace';

View File

@@ -1,7 +1,12 @@
import type { SettingsType, UpdateSettingsInput } from '../../core/user/types';
import type {
UpdateUserSettingsInput,
UserSettingsType,
} from '../../core/user/types';
import type { TestingApp } from './testing-app';
export async function getSettings(app: TestingApp): Promise<SettingsType> {
export async function getUserSettings(
app: TestingApp
): Promise<UserSettingsType> {
const res = await app.gql(
`
query settings {
@@ -17,13 +22,13 @@ export async function getSettings(app: TestingApp): Promise<SettingsType> {
return res.currentUser.settings;
}
export async function updateSettings(
export async function updateUserSettings(
app: TestingApp,
input: UpdateSettingsInput
input: UpdateUserSettingsInput
): Promise<boolean> {
const res = await app.gql(
`
mutation updateSettings($input: UpdateSettingsInput!) {
mutation updateUserSettings($input: UpdateUserSettingsInput!) {
updateSettings(input: $input)
}
`,

View File

@@ -89,7 +89,7 @@ test('should create invitation notification and email', async t => {
test('should not send invitation email if user setting is not to receive invitation email', async t => {
const { notificationService } = t.context;
const inviteId = randomUUID();
await t.context.models.settings.set(member.id, {
await t.context.models.userSettings.set(member.id, {
receiveInvitationEmail: false,
});
const invitationMailCount = t.context.module.mails.count('MemberInvitation');
@@ -150,7 +150,7 @@ test('should not send invitation accepted email if user settings is not receive
const { notificationService } = t.context;
const inviteId = randomUUID();
// should not send email if user settings is not receive invitation email
await t.context.models.settings.set(owner.id, {
await t.context.models.userSettings.set(owner.id, {
receiveInvitationEmail: false,
});
const invitationAcceptedMailCount =
@@ -560,7 +560,7 @@ test('should send mention email by user setting', async t => {
// update user setting to not receive mention email
const mentionMailCount = t.context.module.mails.count('Mention');
await t.context.models.settings.set(member.id, {
await t.context.models.userSettings.set(member.id, {
receiveMentionEmail: false,
});
await notificationService.createMention({

View File

@@ -45,7 +45,7 @@ export class NotificationService {
}
private async sendMentionEmail(input: MentionNotificationCreate) {
const userSetting = await this.models.settings.get(input.userId);
const userSetting = await this.models.userSettings.get(input.userId);
if (!userSetting.receiveMentionEmail) {
return;
}
@@ -104,7 +104,7 @@ export class NotificationService {
// make it easier to test in dev mode
this.logger.debug(`Invite link: ${inviteUrl}`);
}
const userSetting = await this.models.settings.get(input.userId);
const userSetting = await this.models.userSettings.get(input.userId);
if (!userSetting.receiveInvitationEmail) {
return;
}
@@ -151,7 +151,7 @@ export class NotificationService {
const inviterUserId = input.userId;
const inviteeUserId = input.body.createdByUserId;
const workspaceId = input.body.workspaceId;
const userSetting = await this.models.settings.get(inviterUserId);
const userSetting = await this.models.userSettings.get(inviterUserId);
if (!userSetting.receiveInvitationEmail) {
return;
}

View File

@@ -2,9 +2,9 @@ import test from 'ava';
import {
createTestingApp,
getSettings,
getUserSettings,
TestingApp,
updateSettings,
updateUserSettings,
} from '../../../__tests__/utils';
let app: TestingApp;
@@ -19,7 +19,7 @@ test.after.always(async () => {
test('should get user settings', async t => {
await app.signup();
const settings = await getSettings(app);
const settings = await getUserSettings(app);
t.deepEqual(settings, {
receiveInvitationEmail: true,
receiveMentionEmail: true,
@@ -28,30 +28,30 @@ test('should get user settings', async t => {
test('should update user settings', async t => {
await app.signup();
await updateSettings(app, {
await updateUserSettings(app, {
receiveInvitationEmail: false,
receiveMentionEmail: false,
});
const settings = await getSettings(app);
const settings = await getUserSettings(app);
t.deepEqual(settings, {
receiveInvitationEmail: false,
receiveMentionEmail: false,
});
await updateSettings(app, {
await updateUserSettings(app, {
receiveMentionEmail: true,
});
const settings2 = await getSettings(app);
const settings2 = await getUserSettings(app);
t.deepEqual(settings2, {
receiveInvitationEmail: false,
receiveMentionEmail: true,
});
await updateSettings(app, {
await updateUserSettings(app, {
// ignore undefined value
receiveInvitationEmail: undefined,
});
const settings3 = await getSettings(app);
const settings3 = await getUserSettings(app);
t.deepEqual(settings3, {
receiveInvitationEmail: false,
receiveMentionEmail: true,
@@ -61,7 +61,7 @@ test('should update user settings', async t => {
test('should throw error when update user settings with invalid input', async t => {
await app.signup();
await t.throwsAsync(
updateSettings(app, {
updateUserSettings(app, {
receiveInvitationEmail: false,
// @ts-expect-error invalid value
receiveMentionEmail: null,
@@ -75,7 +75,7 @@ test('should throw error when update user settings with invalid input', async t
test('should not update user settings when not logged in', async t => {
await app.logout();
await t.throwsAsync(
updateSettings(app, {
updateUserSettings(app, {
receiveInvitationEmail: false,
receiveMentionEmail: false,
}),

View File

@@ -20,7 +20,7 @@ import {
Throttle,
UserNotFound,
} from '../../base';
import { Models, SettingsSchema } from '../../models';
import { Models, UserSettingsSchema } from '../../models';
import { Public } from '../auth/guard';
import { sessionUser } from '../auth/service';
import { CurrentUser } from '../auth/session';
@@ -32,10 +32,10 @@ import {
ManageUserInput,
PublicUserType,
RemoveAvatar,
SettingsType,
UpdateSettingsInput,
UpdateUserInput,
UpdateUserSettingsInput,
UserOrLimitedUser,
UserSettingsType,
UserType,
} from './types';
@@ -168,20 +168,20 @@ export class UserSettingsResolver {
})
async updateSettings(
@CurrentUser() user: CurrentUser,
@Args('input', { type: () => UpdateSettingsInput })
input: UpdateSettingsInput
@Args('input', { type: () => UpdateUserSettingsInput })
input: UpdateUserSettingsInput
) {
SettingsSchema.parse(input);
await this.models.settings.set(user.id, input);
UserSettingsSchema.parse(input);
await this.models.userSettings.set(user.id, input);
return true;
}
@ResolveField(() => SettingsType, {
@ResolveField(() => UserSettingsType, {
name: 'settings',
description: 'Get user settings',
})
async getSettings(@CurrentUser() me: CurrentUser): Promise<SettingsType> {
return await this.models.settings.get(me.id);
async getSettings(@CurrentUser() me: CurrentUser): Promise<UserSettingsType> {
return await this.models.userSettings.get(me.id);
}
}

View File

@@ -9,8 +9,8 @@ import type { User } from '@prisma/client';
import {
PublicUser,
Settings,
SettingsInput,
UserSettings,
UserSettingsInput,
WorkspaceUser,
} from '../../models';
import { type CurrentUser } from '../auth/session';
@@ -115,7 +115,7 @@ export class RemoveAvatar {
}
@ObjectType()
export class SettingsType implements Settings {
export class UserSettingsType implements UserSettings {
@Field({ description: 'Receive invitation email' })
receiveInvitationEmail!: boolean;
@@ -139,7 +139,7 @@ export class ManageUserInput {
}
@InputType()
export class UpdateSettingsInput implements SettingsInput {
export class UpdateUserSettingsInput implements UserSettingsInput {
@Field({ description: 'Receive invitation email', nullable: true })
receiveInvitationEmail?: boolean;

View File

@@ -43,7 +43,7 @@ test.after(async t => {
});
test('should get a user settings with default value', async t => {
const settings = await t.context.models.settings.get(user.id);
const settings = await t.context.models.userSettings.get(user.id);
t.deepEqual(settings, {
receiveInvitationEmail: true,
receiveMentionEmail: true,
@@ -51,28 +51,28 @@ test('should get a user settings with default value', async t => {
});
test('should update a user settings', async t => {
const settings = await t.context.models.settings.set(user.id, {
const settings = await t.context.models.userSettings.set(user.id, {
receiveInvitationEmail: false,
});
t.deepEqual(settings, {
receiveInvitationEmail: false,
receiveMentionEmail: true,
});
const settings2 = await t.context.models.settings.get(user.id);
const settings2 = await t.context.models.userSettings.get(user.id);
t.deepEqual(settings2, settings);
// update existing setting
const setting3 = await t.context.models.settings.set(user.id, {
const setting3 = await t.context.models.userSettings.set(user.id, {
receiveInvitationEmail: true,
});
t.deepEqual(setting3, {
receiveInvitationEmail: true,
receiveMentionEmail: true,
});
const setting4 = await t.context.models.settings.get(user.id);
const setting4 = await t.context.models.userSettings.get(user.id);
t.deepEqual(setting4, setting3);
const setting5 = await t.context.models.settings.set(user.id, {
const setting5 = await t.context.models.userSettings.set(user.id, {
receiveMentionEmail: false,
receiveInvitationEmail: false,
});
@@ -80,13 +80,13 @@ test('should update a user settings', async t => {
receiveInvitationEmail: false,
receiveMentionEmail: false,
});
const setting6 = await t.context.models.settings.get(user.id);
const setting6 = await t.context.models.userSettings.get(user.id);
t.deepEqual(setting6, setting5);
});
test('should throw error when update settings with invalid payload', async t => {
await t.throwsAsync(
t.context.models.settings.set(user.id, {
t.context.models.userSettings.set(user.id, {
// @ts-expect-error invalid setting input types
receiveInvitationEmail: 1,
}),

View File

@@ -17,10 +17,10 @@ import { HistoryModel } from './history';
import { NotificationModel } from './notification';
import { MODELS_SYMBOL } from './provider';
import { SessionModel } from './session';
import { SettingsModel } from './settings';
import { UserModel } from './user';
import { UserDocModel } from './user-doc';
import { UserFeatureModel } from './user-feature';
import { UserSettingsModel } from './user-settings';
import { VerificationTokenModel } from './verification-token';
import { WorkspaceModel } from './workspace';
import { WorkspaceFeatureModel } from './workspace-feature';
@@ -40,7 +40,7 @@ const MODELS = {
docUser: DocUserModel,
history: HistoryModel,
notification: NotificationModel,
settings: SettingsModel,
userSettings: UserSettingsModel,
copilotSession: CopilotSessionModel,
copilotContext: CopilotContextModel,
copilotJob: CopilotJobModel,
@@ -104,10 +104,10 @@ export * from './feature';
export * from './history';
export * from './notification';
export * from './session';
export * from './settings';
export * from './user';
export * from './user-doc';
export * from './user-feature';
export * from './user-settings';
export * from './verification-token';
export * from './workspace';
export * from './workspace-feature';

View File

@@ -4,27 +4,27 @@ import z from 'zod';
import { BaseModel } from './base';
export const SettingsSchema = z.object({
export const UserSettingsSchema = z.object({
receiveInvitationEmail: z.boolean().default(true),
receiveMentionEmail: z.boolean().default(true),
});
export type SettingsInput = z.input<typeof SettingsSchema>;
export type Settings = z.infer<typeof SettingsSchema>;
export type UserSettingsInput = z.input<typeof UserSettingsSchema>;
export type UserSettings = z.infer<typeof UserSettingsSchema>;
/**
* Settings Model
* UserSettings Model
*/
@Injectable()
export class SettingsModel extends BaseModel {
export class UserSettingsModel extends BaseModel {
@Transactional()
async set(userId: string, setting: SettingsInput) {
async set(userId: string, setting: UserSettingsInput) {
const existsSetting = await this.get(userId);
const payload = SettingsSchema.parse({
const payload = UserSettingsSchema.parse({
...existsSetting,
...setting,
});
await this.db.settings.upsert({
await this.db.userSettings.upsert({
where: {
userId,
},
@@ -36,16 +36,16 @@ export class SettingsModel extends BaseModel {
payload,
},
});
this.logger.log(`Settings updated for user ${userId}`);
this.logger.log(`UserSettings updated for user ${userId}`);
return payload;
}
async get(userId: string): Promise<Settings> {
const row = await this.db.settings.findUnique({
async get(userId: string): Promise<UserSettings> {
const row = await this.db.userSettings.findUnique({
where: {
userId,
},
});
return SettingsSchema.parse(row?.payload ?? {});
return UserSettingsSchema.parse(row?.payload ?? {});
}
}

View File

@@ -1018,7 +1018,7 @@ type Mutation {
updateRuntimeConfigs(updates: JSONObject!): [ServerRuntimeConfigType!]!
"""Update user settings"""
updateSettings(input: UpdateSettingsInput!): Boolean!
updateSettings(input: UpdateUserSettingsInput!): Boolean!
updateSubscriptionRecurring(idempotencyKey: String @deprecated(reason: "use header `Idempotency-Key`"), plan: SubscriptionPlan = Pro, recurring: SubscriptionRecurring!, workspaceId: String): SubscriptionType!
"""Update an user"""
@@ -1363,14 +1363,6 @@ type ServerServiceConfig {
name: String!
}
type SettingsType {
"""Receive invitation email"""
receiveInvitationEmail: Boolean!
"""Receive mention email"""
receiveMentionEmail: Boolean!
}
type SpaceAccessDeniedDataType {
spaceId: String!
}
@@ -1510,7 +1502,12 @@ input UpdateDocUserRoleInput {
workspaceId: String!
}
input UpdateSettingsInput {
input UpdateUserInput {
"""User name"""
name: String
}
input UpdateUserSettingsInput {
"""Receive invitation email"""
receiveInvitationEmail: Boolean
@@ -1518,11 +1515,6 @@ input UpdateSettingsInput {
receiveMentionEmail: Boolean
}
input UpdateUserInput {
"""User name"""
name: String
}
input UpdateWorkspaceInput {
"""Enable AI"""
enableAi: Boolean
@@ -1572,6 +1564,14 @@ type UserQuotaUsageType {
storageQuota: SafeInt! @deprecated(reason: "use `UserQuotaType['usedStorageQuota']` instead")
}
type UserSettingsType {
"""Receive invitation email"""
receiveInvitationEmail: Boolean!
"""Receive mention email"""
receiveMentionEmail: Boolean!
}
type UserType {
"""User avatar url"""
avatarUrl: String
@@ -1612,7 +1612,7 @@ type UserType {
quotaUsage: UserQuotaUsageType!
"""Get user settings"""
settings: SettingsType!
settings: UserSettingsType!
subscriptions: [SubscriptionType!]!
token: tokenType! @deprecated(reason: "use [/api/auth/sign-in?native=true] instead")
}