Skip to content

Commit 4401def

Browse files
committed
Merge branch 'develop' of https://github.com/corpus-io/dm3 into develop
2 parents 28dceaf + 2738d06 commit 4401def

25 files changed

+837
-130
lines changed

packages/backend/src/notifications.test.ts

+361-8
Large diffs are not rendered by default.

packages/backend/src/notifications.ts

+96-16
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// Importing the necessary modules and functions
22
import cors from 'cors';
3-
import { NotificationChannelType } from '@dm3-org/dm3-lib-delivery';
43
import { normalizeEnsName } from '@dm3-org/dm3-lib-profile';
54
import express from 'express';
65
import { auth } from './utils';
6+
import { validateNotificationChannel } from './validation/notification/notificationChannelValidation';
77

88
// Exporting a function that returns an Express router
99
export default () => {
@@ -15,20 +15,24 @@ export default () => {
1515
// Adding a route parameter middleware named 'ensName'
1616
router.param('ensName', auth);
1717

18-
// Defining a route to handle POST requests for adding an email notification channel
19-
router.post('/email/:ensName', async (req, res, next) => {
18+
// Defining a route to enable/disable global notifications
19+
router.post('/global/:ensName', async (req, res, next) => {
2020
try {
2121
const account = normalizeEnsName(req.params.ensName);
2222

23-
// Extracting recipientAddress from the request body
24-
const { recipientAddress } = req.body;
23+
// Extracting isEnabled from the request body
24+
const { isEnabled } = req.body;
2525

26-
// Adding a user's notification channel to the database
27-
await req.app.locals.db.addUsersNotificationChannel(account, {
28-
type: NotificationChannelType.EMAIL,
29-
config: {
30-
recipientAddress,
31-
},
26+
// return if value is not a boolean
27+
if (typeof isEnabled !== 'boolean') {
28+
return res.sendStatus(400).json({
29+
error: 'Invalid value',
30+
});
31+
}
32+
33+
// set global notification to the database
34+
await req.app.locals.db.setGlobalNotification(account, {
35+
isEnabled,
3236
});
3337

3438
// Sending a success response
@@ -39,17 +43,93 @@ export default () => {
3943
}
4044
});
4145

46+
// Defining a route to handle GET requests for fetching global notification
47+
router.get('/global/:ensName', async (req, res, next) => {
48+
try {
49+
const account = normalizeEnsName(req.params.ensName);
50+
51+
// fetching global notification setting for a user from the database
52+
const globalNotification =
53+
await req.app.locals.db.getGlobalNotification(account);
54+
55+
// Sending the fetched global notification setting as a JSON response
56+
res.json(globalNotification);
57+
} catch (e) {
58+
// Passing the error to the next middleware
59+
next(e);
60+
}
61+
});
62+
63+
// Defining a route to handle POST requests for adding an notification channel
64+
router.post('/:ensName', async (req, res, next) => {
65+
try {
66+
const account = normalizeEnsName(req.params.ensName);
67+
68+
// Extracting recipientValue & notificationChannelType from the request body
69+
const { recipientValue, notificationChannelType } = req.body;
70+
71+
// Validate req.body data
72+
const { isValid, errorMessage } = validateNotificationChannel(
73+
notificationChannelType,
74+
recipientValue,
75+
);
76+
77+
// Return if invalid data found
78+
if (!isValid) {
79+
res.sendStatus(400).json({
80+
error: errorMessage,
81+
});
82+
}
83+
84+
// Fetch global notification data of user from database
85+
const globalNotification =
86+
await req.app.locals.db.getGlobalNotification(account);
87+
88+
// Throw error if global notification is turned off
89+
if (!globalNotification.isEnabled) {
90+
res.sendStatus(400).json({
91+
error: 'Global notifications is off',
92+
});
93+
} else {
94+
// Adding a user's notification channel to the database
95+
await req.app.locals.db.addUsersNotificationChannel(account, {
96+
type: notificationChannelType,
97+
config: {
98+
recipientValue: recipientValue,
99+
},
100+
});
101+
102+
// Sending a success response
103+
res.sendStatus(200);
104+
}
105+
} catch (e) {
106+
// Passing the error to the next middleware
107+
next(e);
108+
}
109+
});
110+
42111
// Defining a route to handle GET requests for fetching notification channels
43112
router.get('/:ensName', async (req, res, next) => {
44113
try {
45114
const account = normalizeEnsName(req.params.ensName);
46115

47-
// Getting notification channels for a user from the database
48-
const notificationChannels =
49-
await req.app.locals.db.getUsersNotificationChannels(account);
116+
// Fetch global notification data of user from database
117+
const globalNotification =
118+
await req.app.locals.db.getGlobalNotification(account);
119+
120+
// if global notification is turned off
121+
if (!globalNotification.isEnabled) {
122+
res.status(200).json({ notificationChannels: [] });
123+
} else {
124+
// Getting notification channels for a user from the database
125+
const notificationChannels =
126+
await req.app.locals.db.getUsersNotificationChannels(
127+
account,
128+
);
50129

51-
// Sending the fetched notification channels as a JSON response
52-
res.json(notificationChannels);
130+
// Sending the fetched notification channels as a JSON response
131+
res.status(200).json({ notificationChannels });
132+
}
53133
} catch (e) {
54134
// Passing the error to the next middleware
55135
next(e);

packages/backend/src/persistance/getDatabase.ts

+10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
Session as DSSession,
3+
IGlobalNotification,
34
NotificationChannel,
45
spamFilter,
56
} from '@dm3-org/dm3-lib-delivery';
@@ -22,6 +23,7 @@ export enum RedisPrefix {
2223
UserStorage = 'user.storage:',
2324
Pending = 'pending:',
2425
NotificationChannel = 'notificationChannel:',
26+
GlobalNotification = 'globalNotification:',
2527
}
2628

2729
export async function getRedisClient() {
@@ -85,6 +87,9 @@ export async function getDatabase(_redis?: Redis): Promise<IDatabase> {
8587
Notification.getUsersNotificationChannels(redis),
8688
addUsersNotificationChannel:
8789
Notification.addUsersNotificationChannel(redis),
90+
// Global Notification
91+
getGlobalNotification: Notification.getGlobalNotification(redis),
92+
setGlobalNotification: Notification.setGlobalNotification(redis),
8893
};
8994
}
9095

@@ -144,6 +149,11 @@ export interface IDatabase {
144149
ensName: string,
145150
channel: NotificationChannel,
146151
) => Promise<void>;
152+
getGlobalNotification: (ensName: string) => Promise<IGlobalNotification>;
153+
setGlobalNotification: (
154+
ensName: string,
155+
isEnabled: boolean,
156+
) => Promise<void>;
147157
}
148158

149159
export type Redis = Awaited<ReturnType<typeof getRedisClient>>;

packages/backend/src/persistance/notification/addUsersNotificationChannel.test.ts

+10-10
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe('Set Users NotificationChannel', () => {
3030
const notificationChannel: NotificationChannel = {
3131
type: NotificationChannelType.EMAIL,
3232
config: {
33-
recipientAddress: '[email protected]',
33+
recipientValue: '[email protected]',
3434
},
3535
};
3636

@@ -47,56 +47,56 @@ describe('Set Users NotificationChannel', () => {
4747
expect(afterSetSession).toEqual([notificationChannel]);
4848
});
4949

50-
it('Rejcts Notification Channel with an invalid schema', async () => {
50+
it('Rejects Notification Channel with an invalid schema', async () => {
5151
try {
5252
const notificationChannel: any = {
5353
foo: NotificationChannelType.EMAIL,
5454
config: {
55-
recipientAddress: '[email protected]',
55+
recipientValue: '[email protected]',
5656
},
5757
};
5858

5959
await db.addUsersNotificationChannel(
6060
USER_ADDRESS,
6161
notificationChannel,
6262
);
63-
fail();
63+
throw new Error('Invalid NotificationChannel');
6464
} catch (e) {
6565
expect(e).toStrictEqual(Error('Invalid NotificationChannel'));
6666
}
6767
});
68-
it('Rejcts Email Notification Channel with an invalid config', async () => {
68+
it('Rejects Email Notification Channel with an invalid config', async () => {
6969
try {
7070
const notificationChannel: any = {
7171
type: NotificationChannelType.EMAIL,
7272
config: {
73-
73+
recipientValue: '[email protected]',
7474
},
7575
};
7676

7777
await db.addUsersNotificationChannel(
7878
USER_ADDRESS,
7979
notificationChannel,
8080
);
81-
fail();
81+
throw new Error('Invalid Email config');
8282
} catch (e) {
8383
expect(e).toStrictEqual(Error('Invalid Email config'));
8484
}
8585
});
86-
it('Rejcts Email Notification Channel with an invalid email address', async () => {
86+
it('Rejects Email Notification Channel with an invalid email address', async () => {
8787
try {
8888
const notificationChannel: any = {
8989
type: NotificationChannelType.EMAIL,
9090
config: {
91-
recipientAddress: '12345',
91+
recipientValue: '12345',
9292
},
9393
};
9494

9595
await db.addUsersNotificationChannel(
9696
USER_ADDRESS,
9797
notificationChannel,
9898
);
99-
fail();
99+
throw new Error('Invalid Email config');
100100
} catch (e) {
101101
expect(e).toStrictEqual(Error('Invalid Email config'));
102102
}

packages/backend/src/persistance/notification/addUsersNotificationChannel.ts

+20-41
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {
22
NotificationChannel,
3-
NotificationChannelType,
3+
NotificationUserConfig,
44
schema,
55
} from '@dm3-org/dm3-lib-delivery';
66
import { Redis, RedisPrefix } from '../getDatabase';
@@ -17,55 +17,34 @@ export function addUsersNotificationChannel(redis: Redis) {
1717
throw Error('Invalid NotificationChannel');
1818
}
1919

20-
// Check if the type-specific config matches based on the channel type
21-
switch (channel.type) {
22-
case NotificationChannelType.EMAIL: {
23-
const isValidConfig = validateSchema(
24-
{
25-
...schema.EmailNotificationUserConfig,
26-
// Adding validation for the recipientAddress field
27-
definitions: {
28-
...schema.EmailNotificationUserConfig.definitions,
29-
EmailNotificationUserConfig: {
30-
...schema.EmailNotificationUserConfig
31-
.definitions.EmailNotificationUserConfig,
32-
properties: {
33-
...schema.EmailNotificationUserConfig
34-
.definitions.EmailNotificationUserConfig
35-
.properties,
36-
recipientAddress: {
37-
type: 'string',
38-
format: 'email',
39-
},
40-
},
41-
},
42-
},
43-
},
44-
channel.config,
45-
);
46-
47-
if (!isValidConfig) {
48-
throw Error('Invalid Email config');
49-
}
50-
}
51-
}
52-
5320
// Get previously created notification channels
5421
const existingNotificationChannelsJson = await redis.get(
5522
RedisPrefix.NotificationChannel +
5623
(await getIdEnsName(redis)(ensName)),
5724
);
5825

5926
// Parse JSON. Initialize the array if no existing channels were returned
60-
const existingNotificationChannels = existingNotificationChannelsJson
61-
? JSON.parse(existingNotificationChannelsJson)
62-
: [];
27+
const existingNotificationChannels: NotificationChannel[] =
28+
existingNotificationChannelsJson
29+
? JSON.parse(existingNotificationChannelsJson)
30+
: [];
31+
32+
// filter out channel if already exists
33+
const otherExistingChannels = existingNotificationChannels.filter(
34+
(data) => data.type !== channel.type,
35+
);
36+
37+
// new notification config
38+
const config: NotificationUserConfig = {
39+
recipientValue: channel.config.recipientValue,
40+
isEnabled: true,
41+
isVerified: false,
42+
};
43+
44+
channel.config = config;
6345

6446
// Add the new channel to the existing ones
65-
const newNotificationChannels = [
66-
...existingNotificationChannels,
67-
channel,
68-
];
47+
const newNotificationChannels = [...otherExistingChannels, channel];
6948

7049
// Store the updated channels back into Redis
7150
await redis.set(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { IGlobalNotification } from '@dm3-org/dm3-lib-delivery';
2+
import { Redis, RedisPrefix } from '../getDatabase';
3+
import { getIdEnsName } from '../getIdEnsName';
4+
5+
// Returns the global notification setting
6+
export function getGlobalNotification(redis: Redis) {
7+
return async (ensName: string): Promise<IGlobalNotification> => {
8+
// Fetch the global notification record from the Redis
9+
const globalNotification = await redis.get(
10+
RedisPrefix.GlobalNotification +
11+
(await getIdEnsName(redis)(ensName)),
12+
);
13+
14+
// return if exists else return false
15+
return globalNotification
16+
? (JSON.parse(globalNotification) as IGlobalNotification)
17+
: ({ isEnabled: false } as IGlobalNotification);
18+
};
19+
}
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
11
import { addUsersNotificationChannel } from './addUsersNotificationChannel';
22
import { getUsersNotificationChannels } from './getUsersNotificationChannels';
3+
import { setGlobalNotification } from './setGlobalNotification';
4+
import { getGlobalNotification } from './getGlobalNotification';
35

4-
export default { addUsersNotificationChannel, getUsersNotificationChannels };
6+
export default {
7+
addUsersNotificationChannel,
8+
getUsersNotificationChannels,
9+
setGlobalNotification,
10+
getGlobalNotification,
11+
};

0 commit comments

Comments
 (0)