Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bugfix] fix get notifications + update endpoints #761

Merged
merged 2 commits into from
Jul 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions packages/api/src/controllers/v2/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { Request, Response } from 'express';
import { getAddress } from '@ethersproject/address';
import { services } from '@impactmarket/core';

import { RequestWithUser } from '../../middlewares/core';
import { standardResponse } from '../../utils/api';
import { ListUserNotificationsRequestSchema } from '~validators/user';
import { RequestWithUser } from '~middlewares/core';
import { ValidatedRequest } from '~utils/queryValidator';
import { standardResponse } from '~utils/api';

class UserController {
private userService: services.app.UserServiceV2;
Expand Down Expand Up @@ -281,7 +283,10 @@ class UserController {
.catch(e => standardResponse(res, 400, false, '', { error: e }));
};

public getNotifications = (req: RequestWithUser, res: Response) => {
public getNotifications = (
req: RequestWithUser & ValidatedRequest<ListUserNotificationsRequestSchema>,
res: Response
) => {
if (req.user === undefined) {
standardResponse(res, 401, false, '', {
error: {
Expand Down
81 changes: 29 additions & 52 deletions packages/api/src/routes/v2/user.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import { Router } from 'express';
import timeout from 'connect-timeout';

import { adminAuthentication, authenticateToken, verifySignature } from '../../middlewares';
import UserController from '../../controllers/v2/user';
import userValidators from '../../validators/user';
import { adminAuthentication, authenticateToken, verifySignature } from '~middlewares/index';
import {
queryListUserNotificationsValidator,
create as userCreate,
readNotifications as userReadNotifications,
report as userReport,
sendPushNotifications as userSendPushNotifications,
update as userUpdate
} from '~validators/user';
import UserController from '~controllers/v2/user';

export default (app: Router): void => {
const route = Router();
Expand Down Expand Up @@ -79,7 +86,7 @@ export default (app: Router): void => {
* "403":
* description: "Invalid input"
*/
route.post('/', userValidators.create, userController.create);
route.post('/', userCreate, userController.create);

/**
* @swagger
Expand Down Expand Up @@ -171,7 +178,7 @@ export default (app: Router): void => {
* - SignatureMessage: []
* - Signature: []
*/
route.put('/', authenticateToken, verifySignature, userValidators.update, userController.update);
route.put('/', authenticateToken, verifySignature, userUpdate, userController.update);

/**
* @swagger
Expand Down Expand Up @@ -254,7 +261,7 @@ export default (app: Router): void => {
* security:
* - BearerToken: []
*/
route.post('/report', authenticateToken, userValidators.report, userController.report);
route.post('/report', authenticateToken, userReport, userController.report);

/**
* @swagger
Expand Down Expand Up @@ -347,43 +354,6 @@ export default (app: Router): void => {
*/
route.get('/presigned/:query?', authenticateToken, timeout('3s'), userController.getPresignedUrlMedia);

/**
* @swagger
*
* /users/notifications/unread:
* get:
* tags:
* - "users"
* summary: Get the number of unread notifications from a user
* parameters:
* - in: query
* name: isWebApp
* schema:
* type: boolean
* required: false
* - in: query
* name: isWallet
* schema:
* type: boolean
* required: false
* responses:
* "200":
* description: OK
* content:
* application/json:
* schema:
* type: object
* properties:
* success:
* type: boolean
* data:
* type: integer
* description: number of unread notifications
* security:
* - BearerToken: []
*/
route.get('/notifications/unread/:query?', authenticateToken, timeout('3s'), userController.getUnreadNotifications);

/**
* @swagger
*
Expand Down Expand Up @@ -415,6 +385,12 @@ export default (app: Router): void => {
* schema:
* type: boolean
* required: false
* - in: query
* name: unreadOnly
* schema:
* type: boolean
* required: false
* description: filter by unread notifications only. If undefined, it will return all notifications
* responses:
* "200":
* description: OK
Expand All @@ -430,12 +406,18 @@ export default (app: Router): void => {
* security:
* - BearerToken: []
*/
route.get('/notifications/:query?', authenticateToken, timeout('3s'), userController.getNotifications);
route.get(
'/notifications/:query?',
authenticateToken,
queryListUserNotificationsValidator,
timeout('3s'),
userController.getNotifications
);

/**
* @swagger
*
* /users/notifications/read:
* /users/notifications:
* put:
* tags:
* - "users"
Expand Down Expand Up @@ -467,12 +449,7 @@ export default (app: Router): void => {
* security:
* - BearerToken: []
*/
route.put(
'/notifications/read',
authenticateToken,
userValidators.readNotifications,
userController.readNotifications
);
route.put('/notifications', authenticateToken, userReadNotifications, userController.readNotifications);

/**
* @swagger
Expand Down Expand Up @@ -502,7 +479,7 @@ export default (app: Router): void => {
route.post(
'/push-notifications',
adminAuthentication,
userValidators.sendPushNotifications,
userSendPushNotifications,
userController.sendPushNotifications
);
};
32 changes: 30 additions & 2 deletions packages/api/src/validators/user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { Joi, celebrate } from 'celebrate';

import { ContainerTypes, ValidatedRequestSchema, createValidator } from '~utils/queryValidator';
import { defaultSchema } from './defaultSchema';
import config from '~config/index';

const validator = createValidator();

const create = celebrate({
body: defaultSchema.object({
Expand Down Expand Up @@ -68,10 +72,34 @@ const sendPushNotifications = celebrate({
})
});

export default {
type ListUserNotificationsType = {
offset?: number;
limit?: number;
unreadOnly?: boolean;
isWallet?: boolean;
isWebApp?: boolean;
};

const queryListUserNotificationsSchema = defaultSchema.object<ListUserNotificationsType>({
offset: Joi.number().optional().default(0),
limit: Joi.number().optional().max(20).default(config.defaultLimit),
unreadOnly: Joi.boolean().optional(),
isWallet: Joi.boolean().optional(),
isWebApp: Joi.boolean().optional()
});

interface ListUserNotificationsRequestSchema extends ValidatedRequestSchema {
[ContainerTypes.Query]: ListUserNotificationsType;
}

const queryListUserNotificationsValidator = validator.query(queryListUserNotificationsSchema);

export {
create,
update,
report,
readNotifications,
sendPushNotifications
sendPushNotifications,
queryListUserNotificationsValidator,
ListUserNotificationsRequestSchema
};
75 changes: 56 additions & 19 deletions packages/core/src/services/app/user/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Op } from 'sequelize';
import { Op, WhereOptions } from 'sequelize';
import { ethers } from 'ethers';
import { getAddress } from '@ethersproject/address';

Expand Down Expand Up @@ -335,33 +335,70 @@ export default class UserService {

public async getNotifications(
query: {
offset?: string;
limit?: string;
isWallet?: string;
isWebApp?: string;
offset?: number;
limit?: number;
unreadOnly?: boolean;
isWallet?: boolean;
isWebApp?: boolean;
},
userId: number
): Promise<{
count: number;
rows: AppNotification[];
}> {
const notifications = await models.appNotification.findAndCountAll({
where: {
userId,
isWebApp: query.isWebApp === 'true',
isWallet: query.isWallet === 'true'
},
offset: query.offset ? parseInt(query.offset, 10) : config.defaultOffset,
limit: query.limit ? parseInt(query.limit, 10) : config.defaultLimit,
order: [['createdAt', 'DESC']]
});
const { isWallet, isWebApp, unreadOnly, offset, limit } = query;
let where: WhereOptions<AppNotification> = { userId };
if (isWebApp !== undefined) {
where = {
...where,
isWebApp
};
}
if (isWallet !== undefined) {
where = {
...where,
isWallet
};
}

let count = 0;
let rows: AppNotification[] = [];

if (unreadOnly !== undefined) {
const notifications = await models.appNotification.findAndCountAll({
where: {
...where,
read: !unreadOnly
},
offset,
limit,
order: [['createdAt', 'DESC']]
});

count = notifications.count;
rows = notifications.rows as AppNotification[];
} else {
count = await models.appNotification.count({
where: {
...where,
read: false
}
});
rows = await models.appNotification.findAll({
where,
offset,
limit,
order: [['createdAt', 'DESC']]
});
}

return {
count: notifications.count,
rows: notifications.rows as AppNotification[]
count,
rows
};
}

public async readNotifications(userId: number, notifications?: number[]): Promise<boolean> {
public async readNotifications(userId: number, notificationsId: number[]): Promise<boolean> {
const updated = await models.appNotification.update(
{
read: true
Expand All @@ -371,7 +408,7 @@ export default class UserService {
where: {
userId,
id: {
[Op.in]: notifications
[Op.in]: notificationsId
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/core/tests/integration/v2/user.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ describe('user service v2', () => {

const notifications = await userService.getNotifications(
{
isWebApp: 'true'
isWebApp: true
},
user.id
);
Expand All @@ -585,7 +585,7 @@ describe('user service v2', () => {
});
const notifications = await userService.getNotifications(
{
isWebApp: 'true'
isWebApp: true
},
user.id
);
Expand Down
Loading