Skip to content

Commit 6a396aa

Browse files
authored
739 notification apis (#742)
* add notification channel * added test cases for notification API's * PR changes
1 parent c80b8b6 commit 6a396aa

File tree

4 files changed

+317
-28
lines changed

4 files changed

+317
-28
lines changed

packages/backend/src/notifications.test.ts

+184-6
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import express from 'express';
33
import auth from './auth';
44
import request from 'supertest';
55
import notifications from './notifications';
6+
import { NotificationChannelType } from '@dm3-org/dm3-lib-delivery';
67

78
const keysA = {
89
encryptionKeyPair: {
@@ -20,7 +21,7 @@ const keysA = {
2021

2122
describe('Notifications', () => {
2223
describe('get NotificationChannels', () => {
23-
it('Returns 200 and an empty array when the user has no chanels set up', async () => {
24+
it('Returns empty array as global notification is turned off', async () => {
2425
const app = express();
2526
app.use(bodyParser.json());
2627
app.use(notifications());
@@ -46,6 +47,8 @@ describe('Notifications', () => {
4647
return {};
4748
},
4849
getIdEnsName: async (ensName: string) => ensName,
50+
getGlobalNotification: async (ensName: string) =>
51+
Promise.resolve({ isEnabled: false }),
4952
getUsersNotificationChannels: async (ensName: string) =>
5053
Promise.resolve([]),
5154
};
@@ -59,22 +62,151 @@ describe('Notifications', () => {
5962
.set({
6063
authorization: `Bearer ${token}`,
6164
})
65+
.send();
66+
67+
expect(status).toBe(200);
68+
expect(body).toEqual({
69+
notificationChannels: [],
70+
});
71+
});
72+
73+
it('Returns 200 with empty notification channels as global notification is turned on', async () => {
74+
const app = express();
75+
app.use(bodyParser.json());
76+
app.use(notifications());
77+
78+
const token = await createAuthToken();
79+
80+
app.locals.db = {
81+
getSession: async (ensName: string) =>
82+
Promise.resolve({
83+
challenge: '123',
84+
token,
85+
signedUserProfile: {
86+
profile: {
87+
publicSigningKey:
88+
keysA.signingKeyPair.publicKey,
89+
},
90+
},
91+
}),
92+
setSession: async (_: string, __: any) => {
93+
return (_: any, __: any, ___: any) => {};
94+
},
95+
getUserStorage: async (addr: string) => {
96+
return {};
97+
},
98+
getIdEnsName: async (ensName: string) => ensName,
99+
getUsersNotificationChannels: async (ensName: string) =>
100+
Promise.resolve([]),
101+
getGlobalNotification: async (ensName: string) =>
102+
Promise.resolve({ isEnabled: true }),
103+
};
62104

105+
app.locals.web3Provider = {
106+
resolveName: async () =>
107+
'0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1',
108+
};
109+
const { status, body } = await request(app)
110+
.get(`/bob.eth`)
111+
.set({
112+
authorization: `Bearer ${token}`,
113+
})
63114
.send();
64115

65116
expect(status).toBe(200);
66-
expect(body).toEqual([]);
117+
expect(body).toEqual({
118+
notificationChannels: [],
119+
});
67120
});
68121
});
69122

70123
describe('setUserStorage', () => {
71-
it('User can setup email notifications', async () => {
124+
it('Returns 400 on setup email notifications as email ID is invalid', async () => {
125+
const app = express();
126+
app.use(bodyParser.json());
127+
app.use(notifications());
128+
129+
const token = await createAuthToken();
130+
const addUsersNotificationChannelMock = jest.fn();
131+
132+
app.locals.db = {
133+
getSession: async (ensName: string) =>
134+
Promise.resolve({
135+
challenge: '123',
136+
token,
137+
}),
138+
setSession: async (_: string, __: any) => {
139+
return (_: any, __: any, ___: any) => {};
140+
},
141+
setUserStorage: (_: string, __: string) => {},
142+
getIdEnsName: async (ensName: string) => ensName,
143+
addUsersNotificationChannel: addUsersNotificationChannelMock,
144+
};
145+
146+
app.locals.web3Provider = {
147+
resolveName: async () =>
148+
'0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1',
149+
};
150+
151+
const { status } = await request(app)
152+
.post(`/bob.eth`)
153+
.set({
154+
authorization: `Bearer ${token}`,
155+
})
156+
.send({
157+
recipientValue: 'bob.eth',
158+
notificationChannelType: NotificationChannelType.EMAIL,
159+
});
160+
161+
expect(status).toBe(400);
162+
});
163+
164+
it('Returns 400 on setup email notifications as notificationChannelType is invalid', async () => {
72165
const app = express();
73166
app.use(bodyParser.json());
74167
app.use(notifications());
75168

76169
const token = await createAuthToken();
170+
const addUsersNotificationChannelMock = jest.fn();
171+
172+
app.locals.db = {
173+
getSession: async (ensName: string) =>
174+
Promise.resolve({
175+
challenge: '123',
176+
token,
177+
}),
178+
setSession: async (_: string, __: any) => {
179+
return (_: any, __: any, ___: any) => {};
180+
},
181+
setUserStorage: (_: string, __: string) => {},
182+
getIdEnsName: async (ensName: string) => ensName,
183+
addUsersNotificationChannel: addUsersNotificationChannelMock,
184+
};
77185

186+
app.locals.web3Provider = {
187+
resolveName: async () =>
188+
'0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1',
189+
};
190+
191+
const { status } = await request(app)
192+
.post(`/bob.eth`)
193+
.set({
194+
authorization: `Bearer ${token}`,
195+
})
196+
.send({
197+
recipientValue: '[email protected]',
198+
notificationChannelType: '',
199+
});
200+
201+
expect(status).toBe(400);
202+
});
203+
204+
it('Returns 400 on setup email notifications as globalNotifications is turned off', async () => {
205+
const app = express();
206+
app.use(bodyParser.json());
207+
app.use(notifications());
208+
209+
const token = await createAuthToken();
78210
const addUsersNotificationChannelMock = jest.fn();
79211

80212
app.locals.db = {
@@ -88,20 +220,66 @@ describe('Notifications', () => {
88220
},
89221
setUserStorage: (_: string, __: string) => {},
90222
getIdEnsName: async (ensName: string) => ensName,
223+
getGlobalNotification: async (ensName: string) =>
224+
Promise.resolve({ isEnabled: false }),
91225
addUsersNotificationChannel: addUsersNotificationChannelMock,
92226
};
227+
93228
app.locals.web3Provider = {
94229
resolveName: async () =>
95230
'0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1',
96231
};
97232

98233
const { status } = await request(app)
99-
.post(`/email/bob.eth`)
234+
.post(`/bob.eth`)
235+
.set({
236+
authorization: `Bearer ${token}`,
237+
})
238+
.send({
239+
recipientValue: '[email protected]',
240+
notificationChannelType: NotificationChannelType.EMAIL,
241+
});
242+
243+
expect(status).toBe(400);
244+
});
245+
246+
it('User can setup email notifications', async () => {
247+
const app = express();
248+
app.use(bodyParser.json());
249+
app.use(notifications());
250+
251+
const token = await createAuthToken();
252+
253+
const addUsersNotificationChannelMock = jest.fn();
254+
255+
app.locals.db = {
256+
getSession: async (ensName: string) =>
257+
Promise.resolve({
258+
challenge: '123',
259+
token,
260+
}),
261+
setSession: async (_: string, __: any) => {
262+
return (_: any, __: any, ___: any) => {};
263+
},
264+
setUserStorage: (_: string, __: string) => {},
265+
getIdEnsName: async (ensName: string) => ensName,
266+
getGlobalNotification: async (ensName: string) =>
267+
Promise.resolve({ isEnabled: true }),
268+
addUsersNotificationChannel: addUsersNotificationChannelMock,
269+
};
270+
app.locals.web3Provider = {
271+
resolveName: async () =>
272+
'0x71CB05EE1b1F506fF321Da3dac38f25c0c9ce6E1',
273+
};
274+
275+
const { status, body } = await request(app)
276+
.post(`/bob.eth`)
100277
.set({
101278
authorization: `Bearer ${token}`,
102279
})
103280
.send({
104-
recipientAddress: 'bob.eth',
281+
recipientValue: '[email protected]',
282+
notificationChannelType: NotificationChannelType.EMAIL,
105283
});
106284

107285
expect(status).toBe(200);
@@ -110,7 +288,7 @@ describe('Notifications', () => {
110288
{
111289
type: 'EMAIL',
112290
config: {
113-
recipientValue: 'bob.eth',
291+
recipientValue: 'bob@gmail.com',
114292
},
115293
},
116294
);

packages/backend/src/notifications.ts

+55-22
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 () => {
@@ -60,26 +60,48 @@ export default () => {
6060
}
6161
});
6262

63-
// Defining a route to handle POST requests for adding an email notification channel
64-
router.post('/email/:ensName', async (req, res, next) => {
63+
// Defining a route to handle POST requests for adding an notification channel
64+
router.post('/:ensName', async (req, res, next) => {
6565
try {
6666
const account = normalizeEnsName(req.params.ensName);
6767

68-
// Extracting recipientAddress from the request body
69-
const { recipientAddress } = req.body;
70-
71-
// Adding a user's notification channel to the database
72-
await req.app.locals.db.addUsersNotificationChannel(account, {
73-
type: NotificationChannelType.EMAIL,
74-
config: {
75-
// TODO: This endpoint will be modified as generic,
76-
// not specific to email ID
77-
recipientValue: recipientAddress,
78-
},
79-
});
68+
// Extracting recipientValue & notificationChannelType from the request body
69+
const { recipientValue, notificationChannelType } = req.body;
8070

81-
// Sending a success response
82-
res.sendStatus(200);
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+
}
83105
} catch (e) {
84106
// Passing the error to the next middleware
85107
next(e);
@@ -91,12 +113,23 @@ export default () => {
91113
try {
92114
const account = normalizeEnsName(req.params.ensName);
93115

94-
// Getting notification channels for a user from the database
95-
const notificationChannels =
96-
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);
97119

98-
// Sending the fetched notification channels as a JSON response
99-
res.json(notificationChannels);
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+
);
129+
130+
// Sending the fetched notification channels as a JSON response
131+
res.status(200).json({ notificationChannels });
132+
}
100133
} catch (e) {
101134
// Passing the error to the next middleware
102135
next(e);

0 commit comments

Comments
 (0)