Merge pull request #2979 from specklesystems/fabians/notifications-ioc-2

chore(server): notifications IoC 2 - updateNotificationPreferencesFactory
This commit is contained in:
Alessandro Magionami
2024-09-13 10:29:23 +02:00
committed by GitHub
5 changed files with 73 additions and 49 deletions
@@ -3,3 +3,8 @@ import { NotificationPreferences } from '@/modules/notifications/helpers/types'
export type GetSavedUserNotificationPreferences = (
userId: string
) => Promise<NotificationPreferences>
export type SaveUserNotificationPreferences = (
userId: string,
preferences: NotificationPreferences
) => Promise<void>
@@ -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) {
@@ -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<void> {
await UserNotificationPreferences.knex()
.insert({ userId, preferences })
.onConflict('userId')
.merge()
}
export const saveUserNotificationPreferencesFactory =
(deps: { db: Knex }): SaveUserNotificationPreferences =>
async (userId: string, preferences: NotificationPreferences): Promise<void> => {
await tables
.userNotificationPreferences(deps.db)
.insert({ userId, preferences })
.onConflict('userId')
.merge()
}
@@ -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<string, unknown>
): Promise<void> {
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<Record<NotificationChannel, boolean>> =
{}
const notificationTypeSettings = rawPreferences[nt] as Record<string, unknown>
for (const ncKey in notificationTypeSettings) {
if (!Object.values(NotificationChannel).includes(ncKey as NotificationChannel))
export const updateNotificationPreferencesFactory =
(deps: { saveUserNotificationPreferences: SaveUserNotificationPreferences }) =>
async (userId: string, rawPreferences: Record<string, unknown>): Promise<void> => {
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<Record<NotificationChannel, boolean>> =
{}
const notificationTypeSettings = rawPreferences[nt] as Record<string, unknown>
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)
}
@@ -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