Skip to content

Commit

Permalink
Add gifting endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
Hassan950 committed May 29, 2020
1 parent 473272c commit fa845b3
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 6 deletions.
39 changes: 39 additions & 0 deletions src/controllers/premium.controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,42 @@ exports.subscribe = async (req, res, next) => {
});
res.status(httpStatus.OK).json(result);
};


/**
* A middleware that is called to gift a subscription to premium
*
* @function
* @author Hassan Mohamed
* @summary Gift premium plan
* @param {Object} req - Express request object
* @param {Object} res - Express response object
* @param {Function} next - Express next middleware function
*/
exports.gift = async (req, res, next) => {
const result = await premiumService.gift(req.user, req.body.userId);
if (result instanceof AppError) return next(result);

const hostURL = req.get('host');

const message = `Hello, ${result.displayName}<br>
A Gift For You From ${req.user.displayName}<br>
Your Premium subscription will end on ${result.plan}.<br>
Until then you will be able to enjoy our premium services.<br>
Make Sure to Resubscribe to keep the tunes flowing.`;

emailService
.sendEmail({
email: result.email,
subject: 'Oud Premium Subscription',
message,
button: 'ENJOY PREMIUM NOW',
link: hostURL
})
.then()
.catch(error => {
const { message, code, response } = error;
logger.error(`${code} : ${message}: ${response.body.errors[0].message}`);
});
res.status(httpStatus.OK).json(result);
};
2 changes: 2 additions & 0 deletions src/routes/v1/premium.route.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ router

router.route('/subscribe').patch(catchAsync(premiumController.subscribe));

router.route('/gift').patch(validate(premiumValidation.gift), catchAsync(premiumController.gift));

module.exports = router;
87 changes: 82 additions & 5 deletions src/services/premium.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,19 +58,96 @@ exports.subscribe = async user => {
}

// set player ads to undefined
await Player.findOneAndUpdate({ userId: user._id }, { $unset: { adsCounter: undefined } });
let player = Player.findOneAndUpdate(
{ userId: user._id },
{ $unset: { adsCounter: undefined } }
).exec();

// Update User
return await Normal.findOneAndUpdate(
let newUser = Normal.findOneAndUpdate(
{ _id: user._id },
{
$inc: { credit: -10 },
$set: {
plan: moment(user.plan ? user.plan : undefined)
.add(30, 'day'),
plan: moment(user.plan ? user.plan : undefined).add(30, 'day'),
role: 'premium'
}
},
{ new: true }
).lean();
)
.lean()
.exec();

[newUser, player] = await Promise.all([newUser, player]);
return newUser;
};

/**
* A method that gift another user a premium subscription
*
* @function
* @author Hassan Mohamed
* @summary Gift Premium Subscription
* @param {object} user The user object
* @param {object} giftedId The gifted user ID
* @returns {Object} The new user object after updating
*/

exports.gift = async (user, giftedId) => {
if (user.credit < 10) {
return new AppError(
'Your Credit is below the subscription price, Please try redeeming some coupons.',
httpStatus.BAD_REQUEST
);
}

let giftedUser = await Normal.findById(giftedId, { plan: 1 }).lean();

if (!giftedUser) {
return new AppError(
'There is no normal user associated with this ID',
httpStatus.NOT_FOUND
);
}

giftedUser = Normal.findOneAndUpdate(
{ _id: giftedId },
{
$set: {
plan: moment(giftedUser.plan ? giftedUser.plan : undefined).add(
30,
'day'
),
role: 'premium'
}
},
{ new: true }
)
.lean()
.exec();

let newUser = Normal.findOneAndUpdate(
{ _id: user._id },
{
$inc: { credit: -10 }
},
{ new: true }
)
.lean()
.exec();

// set player ads to undefined
let player = Player.findOneAndUpdate(
{ userId: giftedId },
{ $unset: { adsCounter: undefined } }
).exec();

[player, newUser, giftedUser] = await Promise.all([
player,
newUser,
giftedUser
]);

// Update User
return giftedUser;
};
9 changes: 8 additions & 1 deletion src/validations/premium.validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ exports.redeem = {
couponId: Joi.objectId().required()
})
.messages({
'string.pattern.name': 'Invalid Coupon Code, Please Check It Before Trying Again.'
'string.pattern.name':
'Invalid Coupon Code, Please Check It Before Trying Again.'
})
};

exports.gift = {
body: Joi.object().keys({
userId: Joi.objectId().required()
})
};
22 changes: 22 additions & 0 deletions tests/unit/controllers/premium.controller.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,26 @@ describe('Premium Controller', () => {
expect(json.mock.calls[0][0]).toBe(result);
});
});

describe('gift', () => {
it('should return an error if premiumService has returned an AppError instance', async () => {
req.user = { role: 'free' };
req.body.userId = "validId";
premiumService.gift = jest
.fn()
.mockResolvedValue(new AppError('An Error', httpStatus.BAD_REQUEST));
await premiumController.gift(req, res, next);
expect(next.mock.calls[0][0].statusCode).toBe(httpStatus.BAD_REQUEST);
});

it('should send json file if everything went ok', async () => {
req.user = { role: 'free' };
req.body.userId = "validId";
const result = 'someReturnValue';
premiumService.gift = jest.fn().mockResolvedValue(result);
await premiumController.gift(req, res, next);
expect(res.status.mock.calls[0][0]).toBe(httpStatus.OK);
expect(json.mock.calls[0][0]).toBe(result);
});
});
});
31 changes: 31 additions & 0 deletions tests/unit/services/premium.service.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,35 @@ describe('Premium Service', () => {
expect(result).toBe(user);
});
});

describe('gift', () => {
it('should return bad request if the credit is lower than the premium monthly price', async () => {
const result = await premiumService.gift(user, user._id);
expect(result.statusCode).toBe(httpStatus.BAD_REQUEST);
});

it('should return the user if everything is ok while plan is undefined', async () => {
user.credit = 10000;
mockingoose(Normal).toReturn(user, 'findOneAndUpdate')
.toReturn(user, 'findOne');
const result = await premiumService.gift(user, user._id);
expect(result).toBe(user);
});

it('should return an error if the user is not found in normal users', async () => {
user.credit = 10000;
mockingoose(Normal).toReturn(undefined, 'findOne');
const result = await premiumService.gift(user, user._id);
expect(result.statusCode).toBe(httpStatus.NOT_FOUND);
});

it('should return the user if everything is ok while plan has a previous value', async () => {
user.credit = 10000;
user.plan = moment();
mockingoose(Normal).toReturn(user, 'findOneAndUpdate')
.toReturn(user, 'findOne');
const result = await premiumService.gift(user, user._id);
expect(result).toBe(user);
});
});
});

0 comments on commit fa845b3

Please sign in to comment.