mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-18 15:17:25 +01:00
Fixed concurrency issues with push devices (#10598)
* Fixed concurrency issues with push devices * Fixed push notificaiton response and model adding
This commit is contained in:
@@ -50,11 +50,24 @@ describe('POST /user/push-devices', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('adds a push device to the user', async () => {
|
it('adds a push device to the user', async () => {
|
||||||
let response = await user.post('/user/push-devices', {type, regId});
|
const response = await user.post('/user/push-devices', {type, regId});
|
||||||
await user.sync();
|
await user.sync();
|
||||||
|
|
||||||
expect(response.message).to.equal(t('pushDeviceAdded'));
|
expect(response.message).to.equal(t('pushDeviceAdded'));
|
||||||
|
expect(response.data[0].type).to.equal(type);
|
||||||
|
expect(response.data[0].regId).to.equal(regId);
|
||||||
expect(user.pushDevices[0].type).to.equal(type);
|
expect(user.pushDevices[0].type).to.equal(type);
|
||||||
expect(user.pushDevices[0].regId).to.equal(regId);
|
expect(user.pushDevices[0].regId).to.equal(regId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('removes a push device to the user', async () => {
|
||||||
|
await user.post('/user/push-devices', {type, regId});
|
||||||
|
|
||||||
|
const response = await user.del(`/user/push-devices/${regId}`);
|
||||||
|
await user.sync();
|
||||||
|
|
||||||
|
expect(response.message).to.equal(t('pushDeviceRemoved'));
|
||||||
|
expect(response.data[0]).to.not.exist;
|
||||||
|
expect(user.pushDevices[0]).to.not.exist;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
NotAuthorized,
|
NotAuthorized,
|
||||||
NotFound,
|
NotFound,
|
||||||
} from '../../libs/errors';
|
} from '../../libs/errors';
|
||||||
|
import { model as PushDevice } from '../../models/pushDevice';
|
||||||
|
|
||||||
let api = {};
|
let api = {};
|
||||||
|
|
||||||
@@ -25,17 +26,17 @@ api.addPushDevice = {
|
|||||||
userFieldsToExclude: ['inbox'],
|
userFieldsToExclude: ['inbox'],
|
||||||
})],
|
})],
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
let user = res.locals.user;
|
const user = res.locals.user;
|
||||||
|
|
||||||
req.checkBody('regId', res.t('regIdRequired')).notEmpty();
|
req.checkBody('regId', res.t('regIdRequired')).notEmpty();
|
||||||
req.checkBody('type', res.t('typeRequired')).notEmpty().isIn(['ios', 'android']);
|
req.checkBody('type', res.t('typeRequired')).notEmpty().isIn(['ios', 'android']);
|
||||||
|
|
||||||
let validationErrors = req.validationErrors();
|
const validationErrors = req.validationErrors();
|
||||||
if (validationErrors) throw validationErrors;
|
if (validationErrors) throw validationErrors;
|
||||||
|
|
||||||
let pushDevices = user.pushDevices;
|
const pushDevices = user.pushDevices;
|
||||||
|
|
||||||
let item = {
|
const item = {
|
||||||
regId: req.body.regId,
|
regId: req.body.regId,
|
||||||
type: req.body.type,
|
type: req.body.type,
|
||||||
};
|
};
|
||||||
@@ -44,9 +45,14 @@ api.addPushDevice = {
|
|||||||
throw new NotAuthorized(res.t('pushDeviceAlreadyAdded'));
|
throw new NotAuthorized(res.t('pushDeviceAlreadyAdded'));
|
||||||
}
|
}
|
||||||
|
|
||||||
pushDevices.push(item);
|
// Concurrency safe update
|
||||||
|
const pushDevice = (new PushDevice(item)).toJSON(); // Create a mongo doc
|
||||||
|
await user.update({
|
||||||
|
$push: { pushDevices: pushDevice },
|
||||||
|
}).exec();
|
||||||
|
|
||||||
await user.save();
|
// Update the response
|
||||||
|
user.pushDevices.push(pushDevice);
|
||||||
|
|
||||||
res.respond(200, user.pushDevices, res.t('pushDeviceAdded'));
|
res.respond(200, user.pushDevices, res.t('pushDeviceAdded'));
|
||||||
},
|
},
|
||||||
@@ -70,16 +76,18 @@ api.removePushDevice = {
|
|||||||
userFieldsToExclude: ['inbox'],
|
userFieldsToExclude: ['inbox'],
|
||||||
})],
|
})],
|
||||||
async handler (req, res) {
|
async handler (req, res) {
|
||||||
let user = res.locals.user;
|
const user = res.locals.user;
|
||||||
|
|
||||||
req.checkParams('regId', res.t('regIdRequired')).notEmpty();
|
req.checkParams('regId', res.t('regIdRequired')).notEmpty();
|
||||||
let validationErrors = req.validationErrors();
|
|
||||||
|
const validationErrors = req.validationErrors();
|
||||||
if (validationErrors) throw validationErrors;
|
if (validationErrors) throw validationErrors;
|
||||||
let regId = req.params.regId;
|
|
||||||
|
|
||||||
let pushDevices = user.pushDevices;
|
const regId = req.params.regId;
|
||||||
|
|
||||||
let indexOfPushDevice = pushDevices.findIndex((element) => {
|
const pushDevices = user.pushDevices;
|
||||||
|
|
||||||
|
const indexOfPushDevice = pushDevices.findIndex((element) => {
|
||||||
return element.regId === regId;
|
return element.regId === regId;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -87,8 +95,12 @@ api.removePushDevice = {
|
|||||||
throw new NotFound(res.t('pushDeviceNotFound'));
|
throw new NotFound(res.t('pushDeviceNotFound'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Concurrency safe update
|
||||||
|
const pullQuery = { $pull: { pushDevices: { $elemMatch: { regId } } } };
|
||||||
|
await user.update(pullQuery).exec();
|
||||||
|
|
||||||
|
// Update the response
|
||||||
pushDevices.splice(indexOfPushDevice, 1);
|
pushDevices.splice(indexOfPushDevice, 1);
|
||||||
await user.save();
|
|
||||||
|
|
||||||
res.respond(200, user.pushDevices, res.t('pushDeviceRemoved'));
|
res.respond(200, user.pushDevices, res.t('pushDeviceRemoved'));
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user