Challenge small refactor (#9964)

* Removed challenge group duplication

* Removed duplication of joined challenge

* Moved helpers to challenge library

* Passed group to function

* Fixed group response
This commit is contained in:
Keith Holliday
2018-02-19 08:15:43 -07:00
committed by GitHub
parent b619496f8e
commit faa068f690
2 changed files with 120 additions and 110 deletions

View File

@@ -1,8 +1,6 @@
import { authWithHeaders, authWithSession } from '../../middlewares/auth'; import { authWithHeaders, authWithSession } from '../../middlewares/auth';
import _ from 'lodash'; import _ from 'lodash';
import cloneDeep from 'lodash/cloneDeep'; import cloneDeep from 'lodash/cloneDeep';
import omit from 'lodash/omit';
import uuid from 'uuid';
import { model as Challenge } from '../../models/challenge'; import { model as Challenge } from '../../models/challenge';
import { import {
model as Group, model as Group,
@@ -24,77 +22,15 @@ import {
createTasks, createTasks,
} from '../../libs/taskManager'; } from '../../libs/taskManager';
const TASK_KEYS_TO_REMOVE = ['_id', 'completed', 'dateCompleted', 'history', 'id', 'streak', 'createdAt', 'challenge']; import {
addUserJoinChallengeNotification,
getChallengeGroupResponse,
createChallenge,
cleanUpTask,
} from '../../libs/challenges';
let api = {}; let api = {};
async function createChallenge (user, req, res) {
let groupId = req.body.group;
let prize = req.body.prize;
let group = await Group.getGroup({user, groupId, fields: '-chat', mustBeMember: true});
if (!group) throw new NotFound(res.t('groupNotFound'));
if (!group.isMember(user)) throw new NotAuthorized(res.t('mustBeGroupMember'));
if (group.leaderOnly && group.leaderOnly.challenges && group.leader !== user._id) {
throw new NotAuthorized(res.t('onlyGroupLeaderChal'));
}
if (group._id === TAVERN_ID && prize < 1) {
throw new NotAuthorized(res.t('tavChalsMinPrize'));
}
if (prize > 0) {
let groupBalance = group.balance && group.leader === user._id ? group.balance : 0;
let prizeCost = prize / 4;
if (prizeCost > user.balance + groupBalance) {
throw new NotAuthorized(res.t('cantAfford'));
}
if (groupBalance >= prizeCost) {
// Group pays for all of prize
group.balance -= prizeCost;
} else if (groupBalance > 0) {
// User pays remainder of prize cost after group
let remainder = prizeCost - group.balance;
group.balance = 0;
user.balance -= remainder;
} else {
// User pays for all of prize
user.balance -= prizeCost;
}
}
group.challengeCount += 1;
if (!req.body.summary) {
req.body.summary = req.body.name;
}
req.body.leader = user._id;
req.body.official = user.contributor.admin && req.body.official ? true : false;
let challenge = new Challenge(Challenge.sanitize(req.body));
// First validate challenge so we don't save group if it's invalid (only runs sync validators)
let challengeValidationErrors = challenge.validateSync();
if (challengeValidationErrors) throw challengeValidationErrors;
// Add achievement if user's first challenge
if (!user.achievements.joinedChallenge) {
user.achievements.joinedChallenge = true;
user.addNotification('CHALLENGE_JOINED_ACHIEVEMENT');
}
let results = await Bluebird.all([challenge.save({
validateBeforeSave: false, // already validate
}), group.save()]);
let savedChal = results[0];
await savedChal.syncToUser(user); // (it also saves the user)
return {savedChal, group};
}
/** /**
* @apiDefine ChallengeLeader Challenge Leader * @apiDefine ChallengeLeader Challenge Leader
* The leader of the challenge can use this route. * The leader of the challenge can use this route.
@@ -264,12 +200,7 @@ api.createChallenge = {
_id: user._id, _id: user._id,
profile: {name: user.profile.name}, profile: {name: user.profile.name},
}; };
response.group = { // we already have the group data response.group = getChallengeGroupResponse(group);
_id: group._id,
name: group.name,
type: group.type,
privacy: group.privacy,
};
res.analytics.track('challenge create', { res.analytics.track('challenge create', {
uuid: user._id, uuid: user._id,
@@ -320,22 +251,13 @@ api.joinChallenge = {
challenge.memberCount += 1; challenge.memberCount += 1;
// Add achievement if user's first challenge addUserJoinChallengeNotification(user);
if (!user.achievements.joinedChallenge) {
user.achievements.joinedChallenge = true;
user.addNotification('CHALLENGE_JOINED_ACHIEVEMENT');
}
// Add all challenge's tasks to user's tasks and save the challenge // Add all challenge's tasks to user's tasks and save the challenge
let results = await Bluebird.all([challenge.syncToUser(user), challenge.save()]); let results = await Bluebird.all([challenge.syncToUser(user), challenge.save()]);
let response = results[1].toJSON(); let response = results[1].toJSON();
response.group = { // we already have the group data response.group = getChallengeGroupResponse(group);
_id: group._id,
name: group.name,
type: group.type,
privacy: group.privacy,
};
let chalLeader = await User.findById(response.leader).select(nameFields).exec(); let chalLeader = await User.findById(response.leader).select(nameFields).exec();
response.leader = chalLeader ? chalLeader.toJSON({minimize: true}) : null; response.leader = chalLeader ? chalLeader.toJSON({minimize: true}) : null;
@@ -692,12 +614,7 @@ api.updateChallenge = {
let savedChal = await challenge.save(); let savedChal = await challenge.save();
let response = savedChal.toJSON(); let response = savedChal.toJSON();
response.group = { // we already have the group data response.group = getChallengeGroupResponse(group);
_id: group._id,
name: group.name,
type: group.type,
privacy: group.privacy,
};
let chalLeader = await User.findById(response.leader).select(nameFields).exec(); let chalLeader = await User.findById(response.leader).select(nameFields).exec();
response.leader = chalLeader ? chalLeader.toJSON({minimize: true}) : null; response.leader = chalLeader ? chalLeader.toJSON({minimize: true}) : null;
res.respond(200, response); res.respond(200, response);
@@ -798,23 +715,6 @@ api.selectChallengeWinner = {
}, },
}; };
function cleanUpTask (task) {
let cleansedTask = omit(task, TASK_KEYS_TO_REMOVE);
// Copy checklists but reset to uncomplete and assign new id
if (!cleansedTask.checklist) cleansedTask.checklist = [];
cleansedTask.checklist.forEach((item) => {
item.completed = false;
item.id = uuid();
});
if (cleansedTask.type !== 'reward') {
delete cleansedTask.value;
}
return cleansedTask;
}
/** /**
* @api {post} /api/v3/challenges/:challengeId/clone Clone a challenge * @api {post} /api/v3/challenges/:challengeId/clone Clone a challenge
* @apiName CloneChallenge * @apiName CloneChallenge

View File

@@ -0,0 +1,110 @@
// Currently this holds helpers for challenge api, but we should break this up into submodules as it expands
import omit from 'lodash/omit';
import uuid from 'uuid';
import Bluebird from 'bluebird';
import { model as Challenge } from '../../models/challenge';
import {
model as Group,
TAVERN_ID,
} from '../../models/group';
import {
NotFound,
NotAuthorized,
} from '../errors';
const TASK_KEYS_TO_REMOVE = ['_id', 'completed', 'dateCompleted', 'history', 'id', 'streak', 'createdAt', 'challenge'];
export function addUserJoinChallengeNotification (user) {
if (user.achievements.joinedChallenge) return;
user.achievements.joinedChallenge = true;
user.addNotification('CHALLENGE_JOINED_ACHIEVEMENT');
}
export function getChallengeGroupResponse (group) {
return {
_id: group._id,
name: group.name,
type: group.type,
privacy: group.privacy,
};
}
export async function createChallenge (user, req, res) {
let groupId = req.body.group;
let prize = req.body.prize;
let group = await Group.getGroup({user, groupId, fields: '-chat', mustBeMember: true});
if (!group) throw new NotFound(res.t('groupNotFound'));
if (!group.isMember(user)) throw new NotAuthorized(res.t('mustBeGroupMember'));
if (group.leaderOnly && group.leaderOnly.challenges && group.leader !== user._id) {
throw new NotAuthorized(res.t('onlyGroupLeaderChal'));
}
if (group._id === TAVERN_ID && prize < 1) {
throw new NotAuthorized(res.t('tavChalsMinPrize'));
}
if (prize > 0) {
let groupBalance = group.balance && group.leader === user._id ? group.balance : 0;
let prizeCost = prize / 4;
if (prizeCost > user.balance + groupBalance) {
throw new NotAuthorized(res.t('cantAfford'));
}
if (groupBalance >= prizeCost) {
// Group pays for all of prize
group.balance -= prizeCost;
} else if (groupBalance > 0) {
// User pays remainder of prize cost after group
let remainder = prizeCost - group.balance;
group.balance = 0;
user.balance -= remainder;
} else {
// User pays for all of prize
user.balance -= prizeCost;
}
}
group.challengeCount += 1;
if (!req.body.summary) {
req.body.summary = req.body.name;
}
req.body.leader = user._id;
req.body.official = user.contributor.admin && req.body.official ? true : false;
let challenge = new Challenge(Challenge.sanitize(req.body));
// First validate challenge so we don't save group if it's invalid (only runs sync validators)
let challengeValidationErrors = challenge.validateSync();
if (challengeValidationErrors) throw challengeValidationErrors;
addUserJoinChallengeNotification(user);
let results = await Bluebird.all([challenge.save({
validateBeforeSave: false, // already validate
}), group.save()]);
let savedChal = results[0];
await savedChal.syncToUser(user); // (it also saves the user)
return {savedChal, group};
}
export function cleanUpTask (task) {
let cleansedTask = omit(task, TASK_KEYS_TO_REMOVE);
// Copy checklists but reset to uncomplete and assign new id
if (!cleansedTask.checklist) cleansedTask.checklist = [];
cleansedTask.checklist.forEach((item) => {
item.completed = false;
item.id = uuid();
});
if (cleansedTask.type !== 'reward') {
delete cleansedTask.value;
}
return cleansedTask;
}