diff --git a/packages/server/modules/notifications/domain/operations.ts b/packages/server/modules/notifications/domain/operations.ts index 1c659dc96..74b76d0fd 100644 --- a/packages/server/modules/notifications/domain/operations.ts +++ b/packages/server/modules/notifications/domain/operations.ts @@ -3,3 +3,8 @@ import { NotificationPreferences } from '@/modules/notifications/helpers/types' export type GetSavedUserNotificationPreferences = ( userId: string ) => Promise + +export type SaveUserNotificationPreferences = ( + userId: string, + preferences: NotificationPreferences +) => Promise diff --git a/packages/server/modules/notifications/graph/resolvers/userNotificationPreferences.ts b/packages/server/modules/notifications/graph/resolvers/userNotificationPreferences.ts index debe9c522..f4f4faee7 100644 --- a/packages/server/modules/notifications/graph/resolvers/userNotificationPreferences.ts +++ b/packages/server/modules/notifications/graph/resolvers/userNotificationPreferences.ts @@ -1,9 +1,12 @@ import { db } from '@/db/knex' import { Resolvers } from '@/modules/core/graph/generated/graphql' -import { getSavedUserNotificationPreferencesFactory } from '@/modules/notifications/repositories' import { - updateNotificationPreferences, - getUserNotificationPreferencesFactory + getSavedUserNotificationPreferencesFactory, + saveUserNotificationPreferencesFactory +} from '@/modules/notifications/repositories' +import { + getUserNotificationPreferencesFactory, + updateNotificationPreferencesFactory } from '@/modules/notifications/services/notificationPreferences' const getUserNotificationPreferences = getUserNotificationPreferencesFactory({ @@ -12,6 +15,10 @@ const getUserNotificationPreferences = getUserNotificationPreferencesFactory({ }) }) +const updateNotificationPreferences = updateNotificationPreferencesFactory({ + saveUserNotificationPreferences: saveUserNotificationPreferencesFactory({ db }) +}) + export = { User: { async notificationPreferences(parent) { diff --git a/packages/server/modules/notifications/repositories.ts b/packages/server/modules/notifications/repositories.ts index 4a2569302..fd78ad0d4 100644 --- a/packages/server/modules/notifications/repositories.ts +++ b/packages/server/modules/notifications/repositories.ts @@ -1,5 +1,8 @@ import { UserNotificationPreferences } from '@/modules/core/dbSchema' -import { GetSavedUserNotificationPreferences } from '@/modules/notifications/domain/operations' +import { + GetSavedUserNotificationPreferences, + SaveUserNotificationPreferences +} from '@/modules/notifications/domain/operations' import { NotificationPreferences, UserNotificationPreferencesRecord @@ -22,12 +25,12 @@ export const getSavedUserNotificationPreferencesFactory = return userPreferences?.preferences ?? {} } -export async function saveUserNotificationPreferences( - userId: string, - preferences: NotificationPreferences -): Promise { - await UserNotificationPreferences.knex() - .insert({ userId, preferences }) - .onConflict('userId') - .merge() -} +export const saveUserNotificationPreferencesFactory = + (deps: { db: Knex }): SaveUserNotificationPreferences => + async (userId: string, preferences: NotificationPreferences): Promise => { + await tables + .userNotificationPreferences(deps.db) + .insert({ userId, preferences }) + .onConflict('userId') + .merge() + } diff --git a/packages/server/modules/notifications/services/notificationPreferences.ts b/packages/server/modules/notifications/services/notificationPreferences.ts index 3134091ac..115e26458 100644 --- a/packages/server/modules/notifications/services/notificationPreferences.ts +++ b/packages/server/modules/notifications/services/notificationPreferences.ts @@ -1,11 +1,13 @@ -import * as repo from '@/modules/notifications/repositories' import { NotificationChannel, NotificationType, NotificationPreferences } from '@/modules/notifications/helpers/types' import { InvalidArgumentError } from '@/modules/shared/errors' -import { GetSavedUserNotificationPreferences } from '@/modules/notifications/domain/operations' +import { + GetSavedUserNotificationPreferences, + SaveUserNotificationPreferences +} from '@/modules/notifications/domain/operations' export const getUserNotificationPreferencesFactory = (deps: { @@ -32,35 +34,34 @@ function addDefaultPreferenceValues( return savedPreferences } -export async function updateNotificationPreferences( - userId: string, - rawPreferences: Record -): Promise { - const parsedPreferences: NotificationPreferences = {} - // lets do some nested attribute copying, to sanitize the input - for (const key in rawPreferences) { - if (!Object.values(NotificationType).includes(key as NotificationType)) - throw new InvalidArgumentError( - `Notification preferences input contains an unknown setting: ${key}` - ) - const nt = key as NotificationType - const notificationTypePreferences: Partial> = - {} - const notificationTypeSettings = rawPreferences[nt] as Record - for (const ncKey in notificationTypeSettings) { - if (!Object.values(NotificationChannel).includes(ncKey as NotificationChannel)) +export const updateNotificationPreferencesFactory = + (deps: { saveUserNotificationPreferences: SaveUserNotificationPreferences }) => + async (userId: string, rawPreferences: Record): Promise => { + const parsedPreferences: NotificationPreferences = {} + // lets do some nested attribute copying, to sanitize the input + for (const key in rawPreferences) { + if (!Object.values(NotificationType).includes(key as NotificationType)) throw new InvalidArgumentError( - `Notification preferences input contains an unknown setting: ${ncKey}` + `Notification preferences input contains an unknown setting: ${key}` ) - const nc = ncKey as NotificationChannel - const preferenceValue = notificationTypeSettings[nc] - if (typeof preferenceValue !== 'boolean') - throw new InvalidArgumentError( - `Notification preferences input contains and invalid value: ${preferenceValue}` - ) - notificationTypePreferences[nc] = preferenceValue + const nt = key as NotificationType + const notificationTypePreferences: Partial> = + {} + const notificationTypeSettings = rawPreferences[nt] as Record + for (const ncKey in notificationTypeSettings) { + if (!Object.values(NotificationChannel).includes(ncKey as NotificationChannel)) + throw new InvalidArgumentError( + `Notification preferences input contains an unknown setting: ${ncKey}` + ) + const nc = ncKey as NotificationChannel + const preferenceValue = notificationTypeSettings[nc] + if (typeof preferenceValue !== 'boolean') + throw new InvalidArgumentError( + `Notification preferences input contains and invalid value: ${preferenceValue}` + ) + notificationTypePreferences[nc] = preferenceValue + } + parsedPreferences[nt] = notificationTypePreferences } - parsedPreferences[nt] = notificationTypePreferences + return await deps.saveUserNotificationPreferences(userId, parsedPreferences) } - return await repo.saveUserNotificationPreferences(userId, parsedPreferences) -} diff --git a/packages/server/modules/notifications/tests/notificationsPreferences.spec.ts b/packages/server/modules/notifications/tests/notificationsPreferences.spec.ts index 372d9a16f..559291328 100644 --- a/packages/server/modules/notifications/tests/notificationsPreferences.spec.ts +++ b/packages/server/modules/notifications/tests/notificationsPreferences.spec.ts @@ -1,15 +1,20 @@ import { truncateTables } from '@/test/hooks' import { UserNotificationPreferences, Users } from '@/modules/core/dbSchema' import { BasicTestUser, createTestUsers } from '@/test/authHelper' -import * as services from '@/modules/notifications/services/notificationPreferences' import { expect } from 'chai' import { NotificationType, NotificationChannel } from '@/modules/notifications/helpers/types' import { BaseError } from '@/modules/shared/errors' -import { getUserNotificationPreferencesFactory } from '@/modules/notifications/services/notificationPreferences' -import { getSavedUserNotificationPreferencesFactory } from '@/modules/notifications/repositories' +import { + getUserNotificationPreferencesFactory, + updateNotificationPreferencesFactory +} from '@/modules/notifications/services/notificationPreferences' +import { + getSavedUserNotificationPreferencesFactory, + saveUserNotificationPreferencesFactory +} from '@/modules/notifications/repositories' import { db } from '@/db/knex' const getSavedUserNotificationPreferences = getSavedUserNotificationPreferencesFactory({ @@ -18,6 +23,9 @@ const getSavedUserNotificationPreferences = getSavedUserNotificationPreferencesF const getUserNotificationPreferences = getUserNotificationPreferencesFactory({ getSavedUserNotificationPreferences }) +const updateNotificationPreferences = updateNotificationPreferencesFactory({ + saveUserNotificationPreferences: saveUserNotificationPreferencesFactory({ db }) +}) const cleanup = async () => { await truncateTables([Users.name, UserNotificationPreferences.name]) @@ -49,13 +57,13 @@ describe('User notification preferences @notifications', () => { } }) it('store notification settings', async () => { - await services.updateNotificationPreferences(userA.id, { + await updateNotificationPreferences(userA.id, { activityDigest: { email: false } }) let preferences = await getUserNotificationPreferences(userA.id) expect(preferences).to.not.be.empty expect(preferences.activityDigest?.email).to.be.false - await services.updateNotificationPreferences(userA.id, { + await updateNotificationPreferences(userA.id, { activityDigest: { email: true } }) preferences = await getUserNotificationPreferences(userA.id) @@ -81,7 +89,7 @@ describe('User notification preferences @notifications', () => { // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore preferences[nt][nc] = value - await services.updateNotificationPreferences(userA.id, preferences) + await updateNotificationPreferences(userA.id, preferences) } catch (err) { expect(err instanceof BaseError) const error = err as BaseError