diff --git a/test/api/v3/integration/meta/models/GET-model_paths.test.js b/test/api/v3/integration/models/GET-model_paths.test.js similarity index 69% rename from test/api/v3/integration/meta/models/GET-model_paths.test.js rename to test/api/v3/integration/models/GET-model_paths.test.js index f878d391df..205269bcbe 100644 --- a/test/api/v3/integration/meta/models/GET-model_paths.test.js +++ b/test/api/v3/integration/models/GET-model_paths.test.js @@ -1,9 +1,9 @@ import { generateUser, translate as t, -} from '../../../../../helpers/api-integration/v3'; +} from '../../../../helpers/api-integration/v3'; -describe('GET /meta/models/:model/paths', () => { +describe('GET /models/:model/paths', () => { let user; before(async () => { @@ -11,7 +11,7 @@ describe('GET /meta/models/:model/paths', () => { }); it('returns an error when model is not accessible or doesn\'t exists', async () => { - await expect(user.get('/meta/models/1234/paths')).to.eventually.be.rejected.and.eql({ + await expect(user.get('/models/1234/paths')).to.eventually.be.rejected.and.eql({ code: 400, error: 'BadRequest', message: t('invalidReqParams'), @@ -21,7 +21,7 @@ describe('GET /meta/models/:model/paths', () => { let models = ['habit', 'daily', 'todo', 'reward', 'user', 'tag', 'challenge', 'group']; models.forEach(model => { it(`returns the model paths for ${model}`, async () => { - let res = await user.get(`/meta/models/${model}/paths`); + let res = await user.get(`/models/${model}/paths`); expect(res._id).to.equal('String'); expect(res).to.not.have.keys('__v'); diff --git a/website/src/controllers/api-v3/auth.js b/website/src/controllers/api-v3/auth.js index 640003bb6d..08c405424e 100644 --- a/website/src/controllers/api-v3/auth.js +++ b/website/src/controllers/api-v3/auth.js @@ -62,7 +62,7 @@ async function _handleGroupInvitation (user, invite) { * @apiParam {String} password Body parameter - Password for the new user * @apiParam {String} confirmPassword Body parameter - Password confirmation * - * @apiSuccess {Object} user The user object, if local auth was just attached to a social user then only user.auth.local + * @apiSuccess {Object} data The user object, if local auth was just attached to a social user then only user.auth.local */ api.registerLocal = { method: 'POST', @@ -174,8 +174,8 @@ function _loginRes (user, req, res) { * @apiParam {String} username Body parameter - Username or email of the user * @apiParam {String} password Body parameter - The user's password * - * @apiSuccess {String} _id The user's unique identifier - * @apiSuccess {String} apiToken The user's api token that must be used to authenticate requests. + * @apiSuccess {String} data._id The user's unique identifier + * @apiSuccess {String} data.apiToken The user's api token that must be used to authenticate requests. */ api.loginLocal = { method: 'POST', @@ -229,6 +229,7 @@ function _passportFbProfile (accessToken) { } // Called as a callback by Facebook (or other social providers). Internal route +// TODO move to top-level/auth? api.loginSocial = { method: 'POST', url: '/user/auth/social', // this isn't the most appropriate url but must be the same as v2 @@ -290,7 +291,7 @@ api.loginSocial = { * @apiParam {string} password Body parameter - The current user password * @apiParam {string} username Body parameter - The new username - * @apiSuccess {String} username The new username + * @apiSuccess {String} data.username The new username **/ api.updateUsername = { method: 'PUT', @@ -339,7 +340,7 @@ api.updateUsername = { * @apiParam {string} newPassword Body parameter - The new password * @apiParam {string} confirmPassword Body parameter - New password confirmation * - * @apiSuccess {Object} emoty An empty object + * @apiSuccess {Object} data An empty object **/ api.updatePassword = { method: 'PUT', @@ -379,7 +380,7 @@ api.updatePassword = { * * @apiParam {string} email Body parameter - The email address of the user * - * @apiSuccess {string} message The localized success message + * @apiSuccess {string} data.message The localized success message **/ api.resetPassword = { method: 'POST', @@ -433,7 +434,7 @@ api.resetPassword = { * @apiParam {string} Body parameter - newEmail The new email address. * @apiParam {string} Body parameter - password The user password. * - * @apiSuccess {string} email The updated email address + * @apiSuccess {string} data.email The updated email address */ api.updateEmail = { method: 'PUT', @@ -488,7 +489,7 @@ api.getFirebaseToken = { * @apiName UserDeleteSocial * @apiGroup User * - * @apiSuccess {Object} empty Empty object + * @apiSuccess {Object} data Empty object */ api.deleteSocial = { method: 'DELETE', diff --git a/website/src/controllers/api-v3/challenges.js b/website/src/controllers/api-v3/challenges.js index 50249b0faf..8ea8558fb3 100644 --- a/website/src/controllers/api-v3/challenges.js +++ b/website/src/controllers/api-v3/challenges.js @@ -29,7 +29,7 @@ let api = {}; * @apiName CreateChallenge * @apiGroup Challenge * - * @apiSuccess {object} challenge The newly created challenge + * @apiSuccess {object} data The newly created challenge */ api.createChallenge = { method: 'POST', @@ -119,7 +119,7 @@ api.createChallenge = { * @apiGroup Challenge * @apiParam {UUID} challengeId The challenge _id * - * @apiSuccess {object} challenge The challenge the user joined + * @apiSuccess {object} data The challenge the user joined */ api.joinChallenge = { method: 'POST', @@ -165,7 +165,7 @@ api.joinChallenge = { * @apiGroup Challenge * @apiParam {UUID} challengeId The challenge _id * - * @apiSuccess {object} empty An empty object + * @apiSuccess {object} data An empty object */ api.leaveChallenge = { method: 'POST', @@ -202,7 +202,7 @@ api.leaveChallenge = { * @apiName GetUserChallenges * @apiGroup Challenge * - * @apiSuccess {Array} challenges An array of challenges + * @apiSuccess {Array} data An array of challenges */ api.getUserChallenges = { method: 'GET', @@ -220,12 +220,13 @@ api.getUserChallenges = { _id: {$ne: '95533e05-1ff9-4e46-970b-d77219f199e9'}, // remove the Spread the Word Challenge for now, will revisit when we fix the closing-challenge bug TODO revisit }) .sort('-official -timestamp') + // see below why we're not using populate // .populate('group', basicGroupFields) // .populate('leader', nameFields) .exec(); let resChals = challenges.map(challenge => challenge.toJSON()); - // TODO Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 + // Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 await Q.all(resChals.map((chal, index) => { return Q.all([ User.findById(chal.leader).select(nameFields).exec(), @@ -242,13 +243,14 @@ api.getUserChallenges = { /** * @api {get} /api/v3/challenges/group/group:Id Get challenges for a group + * @apiDescription Get challenges that the user is a member, public challenges and the ones from the user's groups. * @apiVersion 3.0.0 * @apiName GetGroupChallenges * @apiGroup Challenge * * @apiParam {groupId} groupId The group _id * - * @apiSuccess {Array} challenges An array of challenges + * @apiSuccess {Array} data An array of challenges */ api.getGroupChallenges = { method: 'GET', @@ -272,7 +274,7 @@ api.getGroupChallenges = { .exec(); let resChals = challenges.map(challenge => challenge.toJSON()); - // TODO Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 + // Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 await Q.all(resChals.map((chal, index) => { return User.findById(chal.leader).select(nameFields).exec().then(populatedLeader => { resChals[index].leader = populatedLeader.toJSON({minimize: true}); @@ -291,7 +293,7 @@ api.getGroupChallenges = { * * @apiParam {UUID} challengeId The challenge _id * - * @apiSuccess {object} challenge The challenge object + * @apiSuccess {object} data The challenge object */ api.getChallenge = { method: 'GET', @@ -318,7 +320,7 @@ api.getChallenge = { let chalRes = challenge.toJSON(); chalRes.group = group.toJSON({minimize: true}); - // TODO Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 + // Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 chalRes.leader = (await User.findById(chalRes.leader).select(nameFields).exec()).toJSON({minimize: true}); res.respond(200, chalRes); @@ -333,7 +335,7 @@ api.getChallenge = { * * @apiParam {UUID} challengeId The challenge _id * - * @apiSuccess {object} challenge The challenge object + * @apiSuccess {string} challenge A csv file */ api.exportChallengeCsv = { method: 'GET', @@ -406,7 +408,7 @@ api.exportChallengeCsv = { * * @apiParam {UUID} challengeId The challenge _id * - * @apiSuccess {object} challenge The updated challenge object + * @apiSuccess {object} data The updated challenge */ api.updateChallenge = { method: 'PUT', @@ -507,7 +509,9 @@ export async function _closeChal (challenge, broken = {}) { * @apiName DeleteChallenge * @apiGroup Challenge * - * @apiSuccess {object} empty An empty object + * challengeId {UUID} The _id for the challenge to delete + * + * @apiSuccess {object} data An empty object */ api.deleteChallenge = { method: 'DELETE', @@ -537,7 +541,10 @@ api.deleteChallenge = { * @apiName SelectChallengeWinner * @apiGroup Challenge * - * @apiSuccess {object} empty An empty object + * challengeId {UUID} The _id for the challenge to close with a winner + * winnerId {UUID} The _id of the winning user + * + * @apiSuccess {object} data An empty object */ api.selectChallengeWinner = { method: 'POST', diff --git a/website/src/controllers/api-v3/chat.js b/website/src/controllers/api-v3/chat.js index 24e4ea8bab..42864fdab7 100644 --- a/website/src/controllers/api-v3/chat.js +++ b/website/src/controllers/api-v3/chat.js @@ -27,7 +27,7 @@ let api = {}; * * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * - * @apiSuccess {Array} chat An array of chat messages + * @apiSuccess {Array} data An array of chat messages */ api.getChat = { method: 'GET', @@ -54,11 +54,11 @@ api.getChat = { * @apiName PostCat * @apiGroup Chat * - * @apiParam {UUID} groupId The group _id - * @apiParam {message} message The chat's message - * @apiParam {previousMsg} previousMsg The previous chat message which will force a return of the full group chat + * @apiParam {UUID} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) + * @apiParam {message} Body parameter - message The message to post + * @apiParam {previousMsg} previousMsg Query parameter - The previous chat message which will force a return of the full group chat * - * @apiSuccess {Array} chat An array of chat messages + * @apiSuccess data An array of chat messages if a new message was posted after previousMsg, otherwise the posted message */ api.postChat = { method: 'POST', @@ -107,10 +107,10 @@ api.postChat = { * @apiName LikeChat * @apiGroup Chat * - * @apiParam {groupId} groupId The group _id + * @apiParam {groupId} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * @apiParam {chatId} chatId The chat message _id * - * @apiSuccess {Array} chat An array of chat messages + * @apiSuccess {Object} data The liked chat message */ api.likeChat = { method: 'POST', @@ -154,10 +154,10 @@ api.likeChat = { * @apiName LikeChat * @apiGroup Chat * - * @apiParam {groupId} groupId The group _id - * @apiParam {chatId} chatId The chat message _id + * @apiParam {groupId} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) + * @apiParam {chatId} chatId The chat message id * - * @apiSuccess {Array} chat An array of chat messages + * @apiSuccess {object} data The flagged chat message */ api.flagChat = { method: 'POST', @@ -255,14 +255,15 @@ api.flagChat = { /** * @api {post} /api/v3/groups/:groupId/chat/:chatId/clear-flags Clear a group chat message's flags + * @apiDescription Admin-only * @apiVersion 3.0.0 * @apiName ClearFlags * @apiGroup Chat * - * @apiParam {groupId} groupId The group _id - * @apiParam {chatId} chatId The chat message _id + * @apiParam {groupId} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) + * @apiParam {chatId} chatId The chat message id * - * @apiSuccess {Object} An empty object + * @apiSuccess {Object} data An empty object */ api.clearChatFlags = { method: 'Post', @@ -306,7 +307,9 @@ api.clearChatFlags = { * @apiName SeenChat * @apiGroup Chat * - * @apiParam {groupId} groupId The group _id + * @apiParam {groupId} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) + * + * @apiSuccess {Object} data An empty object */ api.seenChat = { method: 'POST', @@ -328,7 +331,7 @@ api.seenChat = { update.$unset[`newMessages.${groupId}`] = true; await User.update({_id: user._id}, update).exec(); - res.respond(200); + res.respond(200, {}); }, }; @@ -338,11 +341,12 @@ api.seenChat = { * @apiName DeleteChat * @apiGroup Chat * + * @apiParam {string} previousMsg Query parameter - The last message fetched by the client so that the whole chat will be returned only if new messages have been posted in the meantime * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) - * @apiParam {string} chatId The chat _id + * @apiParam {string} chatId The chat message id * - * @apiSuccess {Array} The update chat array - * @apiSuccess {Object} An empty object when the previous message was deleted + * @apiSuccess data The updated chat array or an empty object if no message was posted after previousMsg + * @apiSuccess {Object} data An empty object when the previous message was deleted */ api.deleteChat = { method: 'DELETE', diff --git a/website/src/controllers/api-v3/content.js b/website/src/controllers/api-v3/content.js index 780758cf48..46eedf9b91 100644 --- a/website/src/controllers/api-v3/content.js +++ b/website/src/controllers/api-v3/content.js @@ -61,14 +61,15 @@ async function saveContentToDisk (language, content) { } /** - * @api {get} /api/v3/content Get all available content objects. Does not require authentication. + * @api {get} /api/v3/content Get all available content objects. + * @apiDescription Does not require authentication. * @apiVersion 3.0.0 * @apiName ContentGet * @apiGroup Content * - * @apiParam {string} language Optional query parameter, the language code used for the items' strings. Defaulting to english + * @apiParam {string} language Query parameter, the language code used for the items' strings. Defaulting to english * - * @apiSuccess {Object} content All the content available on Habitica + * @apiSuccess {Object} data All the content available on Habitica */ api.getContent = { method: 'GET', @@ -95,7 +96,7 @@ api.getContent = { res.set({ 'Content-Type': 'application/json', }); - res.status(200).send(content); + res.status(200).send(content); // TODO how to use res.respond here? // save the file in background unless it's already cached or being written right now if (cachedContentResponses[language] !== true && cacheBeingWritten[language] !== true) { diff --git a/website/src/controllers/api-v3/coupon.js b/website/src/controllers/api-v3/coupon.js index 8ecae368ee..4dc434aa8c 100644 --- a/website/src/controllers/api-v3/coupon.js +++ b/website/src/controllers/api-v3/coupon.js @@ -11,12 +11,13 @@ import couponCode from 'coupon-code'; let api = {}; /** - * @api {get} /api/v3/coupons Get coupons (sudo users only) + * @api {get} /api/v3/coupons Get coupons + * @apiDescription Sudo users only * @apiVersion 3.0.0 * @apiName GetCoupons * @apiGroup Coupon * - * @apiSuccess string Coupons in CSV format + * @apiSuccess {string} Coupons in CSV format */ api.getCoupons = { method: 'GET', @@ -39,7 +40,8 @@ api.getCoupons = { }; /** - * @api {post} /api/v3/coupons/generate/:event Generate coupons for an event (sudo users only) + * @api {post} /api/v3/coupons/generate/:event Generate coupons for an event + * @apiDescription Sudo users only * @apiVersion 3.0.0 * @apiName GenerateCoupons * @apiGroup Coupon @@ -47,7 +49,7 @@ api.getCoupons = { * @apiParam {string} event The event for which the coupon should be generated * @apiParam {number} count Query parameter to specify the number of coupon codes to generate * - * @apiSuccess array Generated coupons + * @apiSuccess {array} data Generated coupons */ api.generateCoupons = { method: 'POST', @@ -73,7 +75,7 @@ api.generateCoupons = { * * @apiParam {string} code The coupon code to apply * - * @apiSuccess object User object + * @apiSuccess {object} data User object */ api.enterCouponCode = { method: 'POST', @@ -98,7 +100,7 @@ api.enterCouponCode = { * @apiName ValidateCoupon * @apiGroup Coupon * - * @apiSuccess valid {boolean} true or false + * @apiSuccess {boolean} data.valid True or false */ api.validateCoupon = { method: 'POST', diff --git a/website/src/controllers/api-v3/debug.js b/website/src/controllers/api-v3/debug.js index a9d9c0cb08..dfd462a7e7 100644 --- a/website/src/controllers/api-v3/debug.js +++ b/website/src/controllers/api-v3/debug.js @@ -4,12 +4,13 @@ import ensureDevelpmentMode from '../../middlewares/api-v3/ensureDevelpmentMode' let api = {}; /** - * @api {post} /api/v3/debug/add-ten-gems Add ten gems to the current user + * @api {post} /api/v3/debug/add-ten-gems Add ten gems to the current user. + * @apiDescription Only available in development mode. * @apiVersion 3.0.0 * @apiName AddTenGems * @apiGroup Development * - * @apiSuccess {Object} empty An empty Object + * @apiSuccess {Object} data An empty Object */ api.addTenGems = { method: 'POST', @@ -27,12 +28,13 @@ api.addTenGems = { }; /** - * @api {post} /api/v3/debug/add-hourglass Add Hourglass to the current user + * @api {post} /api/v3/debug/add-hourglass Add Hourglass to the current user. + * @apiDescription Only available in development mode. * @apiVersion 3.0.0 * @apiName AddHourglass * @apiGroup Development * - * @apiSuccess {Object} empty An empty Object + * @apiSuccess {Object} data An empty Object */ api.addHourglass = { method: 'POST', diff --git a/website/src/controllers/api-v3/email.js b/website/src/controllers/api-v3/email.js index 01b7109e19..f8e5e4effe 100644 --- a/website/src/controllers/api-v3/email.js +++ b/website/src/controllers/api-v3/email.js @@ -7,21 +7,22 @@ import { let api = {}; +// TODO move to top-level controllers? /** * @api {get} /api/v3/email/unsubscribe Unsubscribe an email or user from email notifications + * @apiDescription Does not require authentication * @apiVersion 3.0.0 * @apiName UnsubscribeEmail * @apiGroup Unsubscribe * @apiDescription This is a GET method so that you can put the unsubscribe link in emails. * - * @apiParam {String} code An unsubscription code + * @apiParam {String} code Query parameter - An unsubscription code * - * @apiSuccess {String} okRes An message stating the user/email unsubscribed successfully + * @apiSuccess {String} An html success message */ api.unsubscribe = { method: 'GET', url: '/email/unsubscribe', - middlewares: [], async handler (req, res) { req.checkQuery({ code: { diff --git a/website/src/controllers/api-v3/groups.js b/website/src/controllers/api-v3/groups.js index 6563f1289e..60df7d1d15 100644 --- a/website/src/controllers/api-v3/groups.js +++ b/website/src/controllers/api-v3/groups.js @@ -24,15 +24,13 @@ import common from '../../../../common'; import sendPushNotification from '../../libs/api-v3/pushNotifications'; let api = {}; -// TODO shall we accept party as groupId in all routes? - /** * @api {post} /api/v3/groups Create group * @apiVersion 3.0.0 * @apiName CreateGroup * @apiGroup Group * - * @apiSuccess {Object} group The group object + * @apiSuccess {Object} data The create group */ api.createGroup = { method: 'POST', @@ -40,7 +38,7 @@ api.createGroup = { middlewares: [authWithHeaders()], async handler (req, res) { let user = res.locals.user; - let group = new Group(Group.sanitize(req.body)); // TODO validate empty req.body + let group = new Group(Group.sanitize(req.body)); group.leader = user._id; if (group.type === 'guild') { @@ -60,7 +58,7 @@ api.createGroup = { let results = await Q.all([user.save(), group.save()]); let savedGroup = results[1]; - // TODO Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 + // Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 // await Q.ninvoke(savedGroup, 'populate', ['leader', nameFields]); // doc.populate doesn't return a promise let response = savedGroup.toJSON(); // the leader is the authenticated user @@ -76,14 +74,14 @@ api.createGroup = { }; /** - * @api {get} /api/v3/groups Get groups + * @api {get} /api/v3/groups Get groups for a user * @apiVersion 3.0.0 * @apiName GetGroups * @apiGroup Group * * @apiParam {string} type The type of groups to retrieve. Must be a query string representing a list of values like 'tavern,party'. Possible values are party, privateGuilds, publicGuilds, tavern * - * @apiSuccess {Array} groups An array of the requested groups + * @apiSuccess {Array} data An array of the requested groups */ api.getGroups = { method: 'GET', @@ -92,7 +90,7 @@ api.getGroups = { async handler (req, res) { let user = res.locals.user; - req.checkQuery('type', res.t('groupTypesRequired')).notEmpty(); // TODO better validation + req.checkQuery('type', res.t('groupTypesRequired')).notEmpty(); let validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; @@ -119,7 +117,7 @@ api.getGroups = { * * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * - * @apiSuccess {Object} group The group object + * @apiSuccess {Object} data The group object */ api.getGroup = { method: 'GET', @@ -137,7 +135,7 @@ api.getGroup = { if (!group) throw new NotFound(res.t('groupNotFound')); group = Group.toJSONCleanChat(group, user); - // TODO Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 + // Instead of populate we make a find call manually because of https://github.com/Automattic/mongoose/issues/3833 let leader = await User.findById(group.leader).select(nameFields).exec(); if (leader) group.leader = leader.toJSON({minimize: true}); @@ -153,7 +151,7 @@ api.getGroup = { * * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * - * @apiSuccess {Object} group The updated group object + * @apiSuccess {Object} data The updated group */ api.updateGroup = { method: 'PUT', @@ -197,9 +195,9 @@ api.updateGroup = { * @apiName JoinGroup * @apiGroup Group * - * @apiParam {UUID} groupId The group _id + * @apiParam {UUID} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * - * @apiSuccess {Object} group The group + * @apiSuccess {Object} data The joined group */ api.joinGroup = { method: 'POST', @@ -288,9 +286,9 @@ api.joinGroup = { * @apiName RejectGroupInvite * @apiGroup Group * - * @apiParam {UUID} groupId The group _id + * @apiParam {UUID} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * - * @apiSuccess {Object} group The group + * @apiSuccess {Object} data An empty object */ api.rejectGroupInvite = { method: 'POST', @@ -334,9 +332,9 @@ api.rejectGroupInvite = { * @apiGroup Group * * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) - * @apiParam {string="remove-all","keep-all"} keep Wheter to keep or not challenges' tasks, as an optional query string + * @apiParam {string="remove-all","keep-all"} keep Query parameter - Whether to keep or not challenges' tasks. Defaults to keep-all * - * @apiSuccess {Object} empty An empty object + * @apiSuccess {Object} data An empty object */ api.leaveGroup = { method: 'POST', @@ -391,9 +389,9 @@ function _sendMessageToRemoved (group, removedUser, message) { * * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * @apiParam {UUID} memberId The _id of the member to remove - * @apiParam {string} message The message to send to the removed members, as a query string // TODO in req.body? + * @apiParam {string} message Query parameter - The message to send to the removed members * - * @apiSuccess {Object} empty An empty object + * @apiSuccess {Object} data An empty object */ api.removeGroupMember = { method: 'POST', @@ -448,7 +446,7 @@ api.removeGroupMember = { if (isInGroup === 'guild') { removeFromArray(member.guilds, group._id); } - if (isInGroup === 'party') member.party._id = undefined; // TODO remove quest information too? + if (isInGroup === 'party') member.party._id = undefined; // TODO remove quest information too? Use group.leave()? if (member.newMessages[group._id]) { member.newMessages[group._id] = undefined; @@ -456,13 +454,16 @@ api.removeGroupMember = { } if (group.quest && group.quest.active && group.quest.leader === member._id) { - member.items.quests[group.quest.key] += 1; // TODO why this? + member.items.quests[group.quest.key] += 1; } } else if (isInvited) { if (isInvited === 'guild') { removeFromArray(member.invitations.guilds, { id: group._id }); } - if (isInvited === 'party') user.invitations.party = {}; // TODO mark modified? + if (isInvited === 'party') { + user.invitations.party = {}; + user.markModified('invitations.party'); + } } else { throw new NotFound(res.t('groupMemberNotFound')); } @@ -592,11 +593,11 @@ async function _inviteByEmail (invite, group, inviter, req, res) { * * @apiParam {string} groupId The group _id ('party' for the user party and 'habitrpg' for tavern are accepted) * - * @apiParam {array} emails An array of emails addresses to invite (optional) (inside body) - * @apiParam {array} uuids An array of uuids to invite (optional) (inside body) - * @apiParam {string} inviter The inviters' name (optional) (inside body) + * @apiParam {array} emails Body parameter - An array of emails addresses to invite (optional) + * @apiParam {array} uuids Body parameter - An array of uuids to invite (optional) + * @apiParam {string} inviter Body parameter - The inviters' name (optional) * - * @apiSuccess {Object} empty An empty object + * @apiSuccess {array} data The invites */ api.inviteToGroup = { method: 'POST', diff --git a/website/src/controllers/api-v3/hall.js b/website/src/controllers/api-v3/hall.js index 55bc191063..c8f11fdfa8 100644 --- a/website/src/controllers/api-v3/hall.js +++ b/website/src/controllers/api-v3/hall.js @@ -9,14 +9,15 @@ import _ from 'lodash'; let api = {}; /** - * @api {get} /api/v3/hall/patrons Get all Patrons. Only the first 50 patrons are returned. More can be accessed passing ?page=n. + * @api {get} /api/v3/hall/patrons Get all Patrons. + * @apiDescription Only the first 50 patrons are returned. More can be accessed passing ?page=n. * @apiVersion 3.0.0 * @apiName GetPatrons * @apiGroup Hall * - * @apiParam {Number} page The result page. Default is 0 + * @apiParam {Number} page Query Parameter - The result page. Default is 0 * - * @apiSuccess {Array} patron An array of patrons + * @apiSuccess {Array} data An array of patrons */ api.getPatrons = { method: 'GET', @@ -52,7 +53,7 @@ api.getPatrons = { * @apiName GetHeroes * @apiGroup Hall * - * @apiSuccess {Array} hero An array of heroes + * @apiSuccess {Array} data An array of heroes */ api.getHeroes = { method: 'GET', @@ -74,17 +75,17 @@ api.getHeroes = { // Note, while the following routes are called getHero / updateHero // they can be used by admins to get/update any user -// TODO rename? const heroAdminFields = 'contributor balance profile.name purchased items auth'; /** - * @api {get} /api/v3/hall/heroes/:heroId Get an hero given his _id. Must be an admin to make this request + * @api {get} /api/v3/hall/heroes/:heroId Get an hero given his _id. + * @apiDescription Must be an admin to make this request * @apiVersion 3.0.0 * @apiName GetHero * @apiGroup Hall * - * @apiSuccess {Object} hero The hero object + * @apiSuccess {Object} data The hero object */ api.getHero = { method: 'GET', @@ -116,12 +117,13 @@ api.getHero = { const gemsPerTier = {1: 3, 2: 3, 3: 3, 4: 4, 5: 4, 6: 4, 7: 4, 8: 0, 9: 0}; /** - * @api {put} /api/v3/hall/heroes/:heroId Update an hero. Must be an admin to make this request + * @api {put} /api/v3/hall/heroes/:heroId Update an hero. + * @apiDescription Must be an admin to make this request * @apiVersion 3.0.0 * @apiName UpdateHero * @apiGroup Hall * - * @apiSuccess {Object} hero The updated hero object + * @apiSuccess {Object} data The updated hero object */ api.updateHero = { method: 'PUT', @@ -162,7 +164,7 @@ api.updateHero = { if (updateData.itemPath && updateData.itemVal && updateData.itemPath.indexOf('items.') === 0 && User.schema.paths[updateData.itemPath]) { - _.set(hero, updateData.itemPath, updateData.itemVal); // Sanitization at 5c30944 (deemed unnecessary) TODO review + _.set(hero, updateData.itemPath, updateData.itemVal); // Sanitization at 5c30944 (deemed unnecessary) } if (updateData.auth && _.isBoolean(updateData.auth.blocked)) hero.auth.blocked = updateData.auth.blocked; diff --git a/website/src/controllers/api-v3/members.js b/website/src/controllers/api-v3/members.js index 9e4c45d5d2..1a2257f5fe 100644 --- a/website/src/controllers/api-v3/members.js +++ b/website/src/controllers/api-v3/members.js @@ -27,7 +27,7 @@ let api = {}; * * @apiParam {UUID} memberId The member's id * - * @apiSuccess {object} member The member object + * @apiSuccess {object} data The member object */ api.getMember = { method: 'GET', @@ -129,7 +129,8 @@ function _getMembersForItem (type) { } /** - * @api {get} /api/v3/groups/:groupId/members Get members for a group with a limit of 30 member per request. To get all members run requests against this routes (updating the lastId query parameter) until you get less than 30 results. + * @api {get} /api/v3/groups/:groupId/members Get members for a group + * @apiDescription With a limit of 30 member per request. To get all members run requests against this routes (updating the lastId query parameter) until you get less than 30 results. * @apiVersion 3.0.0 * @apiName GetMembersForGroup * @apiGroup Member @@ -138,7 +139,7 @@ function _getMembersForItem (type) { * @apiParam {UUID} lastId Query parameter to specify the last member returned in a previous request to this route and get the next batch of results * @apiParam {boolean} includeAllPublicFields Query parameter available only when fetching a party. If === `true` then all public fields for members will be returned (liek when making a request for a single member) * - * @apiSuccess {array} members An array of members, sorted by _id + * @apiSuccess {array} data An array of members, sorted by _id */ api.getMembersForGroup = { method: 'GET', @@ -148,7 +149,8 @@ api.getMembersForGroup = { }; /** - * @api {get} /api/v3/groups/:groupId/invites Get invites for a group with a limit of 30 member per request. To get all invites run requests against this routes (updating the lastId query parameter) until you get less than 30 results. + * @api {get} /api/v3/groups/:groupId/invites Get invites for a group + * @apiDescription With a limit of 30 member per request. To get all invites run requests against this routes (updating the lastId query parameter) until you get less than 30 results. * @apiVersion 3.0.0 * @apiName GetInvitesForGroup * @apiGroup Member @@ -156,7 +158,7 @@ api.getMembersForGroup = { * @apiParam {UUID} groupId The group id * @apiParam {UUID} lastId Query parameter to specify the last invite returned in a previous request to this route and get the next batch of results * - * @apiSuccess {array} invites An array of invites, sorted by _id + * @apiSuccess {array} data An array of invites, sorted by _id */ api.getInvitesForGroup = { method: 'GET', @@ -166,7 +168,8 @@ api.getInvitesForGroup = { }; /** - * @api {get} /api/v3/challenges/:challengeId/members Get members for a challenge with a limit of 30 member per request. To get all members run requests against this routes (updating the lastId query parameter) until you get less than 30 results. + * @api {get} /api/v3/challenges/:challengeId/members Get members for a challenge + * @apiDescription With a limit of 30 member per request. To get all members run requests against this routes (updating the lastId query parameter) until you get less than 30 results. * @apiVersion 3.0.0 * @apiName GetMembersForChallenge * @apiGroup Member @@ -174,7 +177,7 @@ api.getInvitesForGroup = { * @apiParam {UUID} challengeId The challenge id * @apiParam {UUID} lastId Query parameter to specify the last member returned in a previous request to this route and get the next batch of results * - * @apiSuccess {array} members An array of members, sorted by _id + * @apiSuccess {array} data An array of members, sorted by _id */ api.getMembersForChallenge = { method: 'GET', @@ -192,7 +195,7 @@ api.getMembersForChallenge = { * @apiParam {UUID} challengeId The challenge _id * @apiParam {UUID} member The member _id * - * @apiSuccess {object} member Return an object with member _id, profile.name and a tasks object with the challenge tasks for the member + * @apiSuccess {object} data Return an object with member _id, profile.name and a tasks object with the challenge tasks for the member */ api.getChallengeMemberProgress = { method: 'GET', @@ -242,10 +245,10 @@ api.getChallengeMemberProgress = { * @apiName SendPrivateMessage * @apiGroup Members * - * @apiParam {String} message The message - * @apiParam {UUID} toUserId The toUser _id + * @apiParam {String} message Body parameter - The message + * @apiParam {UUID} toUserId Body parameter - The user to contact * - * @apiSuccess {Object} empty An empty Object + * @apiSuccess {Object} data An empty Object */ api.sendPrivateMessage = { method: 'POST', @@ -291,10 +294,10 @@ api.sendPrivateMessage = { * @apiName TransferGems * @apiGroup Members * - * @apiParam {String} message The message - * @apiParam {UUID} toUserId The toUser _id + * @apiParam {String} message Body parameter The message + * @apiParam {UUID} toUserId Body parameter The toUser _id * - * @apiSuccess {Object} empty An empty Object + * @apiSuccess {Object} data An empty Object */ api.transferGems = { method: 'POST', @@ -346,8 +349,8 @@ api.transferGems = { ]); } - // @TODO: Add push notifications - // pushNotify.sendNotify(sender, res.t('giftedGems'), res.t('giftedGemsInfo', { amount: gemAmount, name: byUsername })); + // TODO: Add push notifications + // pushNotify.sendNotify(sender, res.t('giftedGems'), res.t('giftedGemsInfo', { amount: gemAmount, name: byUsername })); res.respond(200, {}); }, diff --git a/website/src/controllers/api-v3/modelsPaths.js b/website/src/controllers/api-v3/modelsPaths.js index b023521722..b14d88b2b7 100644 --- a/website/src/controllers/api-v3/modelsPaths.js +++ b/website/src/controllers/api-v3/modelsPaths.js @@ -6,18 +6,19 @@ let tasksModels = ['habit', 'daily', 'todo', 'reward']; let allModels = ['user', 'tag', 'challenge', 'group'].concat(tasksModels); /** - * @api {get} /api/v3/meta/models/:model/paths Get all paths for the specified model. Doesn't require authentication + * @api {get} /api/v3s/models/:model/paths Get all paths for the specified model. + * @apiDescription Doesn't require authentication * @apiVersion 3.0.0 * @apiName GetUserModelPaths * @apiGroup Meta * * @apiParam {string="user","group","challenge","tag","habit","daily","todo","reward"} model The name of the model * - * @apiSuccess {object} paths A key-value object made of fieldPath: fieldType (like {'field.nested': Boolean}) + * @apiSuccess {object} data A key-value object made of fieldPath: fieldType (like {'field.nested': Boolean}) */ api.getModelPaths = { method: 'GET', - url: '/meta/models/:model/paths', + url: '/models/:model/paths', async handler (req, res) { req.checkParams('model', res.t('modelNotFound')).notEmpty().isIn(allModels); diff --git a/website/src/controllers/api-v3/quests.js b/website/src/controllers/api-v3/quests.js index bd076610e6..05b3020f86 100644 --- a/website/src/controllers/api-v3/quests.js +++ b/website/src/controllers/api-v3/quests.js @@ -35,8 +35,9 @@ let api = {}; * @apiGroup Group * * @apiParam {string} groupId The group _id (or 'party') + * @apiParam {string} questKey * - * @apiSuccess {Object} quest Quest Object + * @apiSuccess {Object} data Quest object */ api.inviteToQuest = { method: 'POST', @@ -139,7 +140,7 @@ api.inviteToQuest = { * * @apiParam {string} groupId The group _id (or 'party') * - * @apiSuccess {Object} quest Quest Object + * @apiSuccess {Object} data Quest Object */ api.acceptQuest = { method: 'POST', @@ -196,7 +197,7 @@ api.acceptQuest = { * * @apiParam {string} groupId The group _id (or 'party') * - * @apiSuccess {Object} quest Quest Object + * @apiSuccess {Object} data Quest Object */ api.rejectQuest = { method: 'POST', @@ -250,12 +251,12 @@ api.rejectQuest = { /** * @api {post} /api/v3/groups/:groupId/quests/force-start Accept a pending quest * @apiVersion 3.0.0 - * @apiName forceStart + * @apiName ForceQuestStart * @apiGroup Group * * @apiParam {string} groupId The group _id (or 'party') * - * @apiSuccess {Object} quest Quest Object + * @apiSuccess {Object} data Quest Object */ api.forceStart = { method: 'POST', @@ -307,7 +308,7 @@ api.forceStart = { * * @apiParam {string} groupId The group _id (or 'party') * - * @apiSuccess {Object} quest Quest Object + * @apiSuccess {Object} data Quest Object */ api.cancelQuest = { method: 'POST', @@ -356,7 +357,7 @@ api.cancelQuest = { * * @apiParam {string} groupId The group _id (or 'party') * - * @apiSuccess {Object} quest Quest Object + * @apiSuccess {Object} data Quest Object */ api.abortQuest = { method: 'POST', @@ -410,7 +411,7 @@ api.abortQuest = { * * @apiParam {string} groupId The group _id (or 'party') * - * @apiSuccess {Object} quest Quest Object + * @apiSuccess {Object} data Quest Object */ api.leaveQuest = { method: 'POST', diff --git a/website/src/controllers/api-v3/status.js b/website/src/controllers/api-v3/status.js index a3f59726a4..94bcf63ed0 100644 --- a/website/src/controllers/api-v3/status.js +++ b/website/src/controllers/api-v3/status.js @@ -6,7 +6,7 @@ let api = {}; * @apiName GetStatus * @apiGroup Status * - * @apiSuccess {status} string 'up' if everything is ok + * @apiSuccess {status} data.status 'up' if everything is ok */ api.getStatus = { method: 'GET', diff --git a/website/src/controllers/api-v3/tags.js b/website/src/controllers/api-v3/tags.js index 3028ee568c..03170b96b3 100644 --- a/website/src/controllers/api-v3/tags.js +++ b/website/src/controllers/api-v3/tags.js @@ -14,7 +14,7 @@ let api = {}; * @apiName CreateTag * @apiGroup Tag * - * @apiSuccess {Object} tag The newly created tag + * @apiSuccess {Object} data The newly created tag */ api.createTag = { method: 'POST', @@ -38,7 +38,7 @@ api.createTag = { * @apiName GetTags * @apiGroup Tag * - * @apiSuccess {Array} tags An array of tag objects + * @apiSuccess {Array} data An array of tags */ api.getTags = { method: 'GET', @@ -58,7 +58,7 @@ api.getTags = { * * @apiParam {UUID} tagId The tag _id * - * @apiSuccess {object} tag The tag object + * @apiSuccess {object} data The tag object */ api.getTag = { method: 'GET', @@ -86,7 +86,7 @@ api.getTag = { * * @apiParam {UUID} tagId The tag _id * - * @apiSuccess {object} tag The updated tag + * @apiSuccess {object} data The updated tag */ api.updateTag = { method: 'PUT', @@ -120,7 +120,7 @@ api.updateTag = { * * @apiParam {UUID} tagId The tag _id * - * @apiSuccess {object} empty An empty object + * @apiSuccess {object} data An empty object */ api.deleteTag = { method: 'DELETE', diff --git a/website/src/controllers/api-v3/tasks.js b/website/src/controllers/api-v3/tasks.js index 5c293bdd49..c6cd66debc 100644 --- a/website/src/controllers/api-v3/tasks.js +++ b/website/src/controllers/api-v3/tasks.js @@ -54,13 +54,13 @@ async function _createTasks (req, res, user, challenge) { } /** - * @api {post} /api/v3/tasks/user Create a new task belonging to the autheticated user. Can be passed an object to create a single task or an array of objects to create multiple tasks. + * @api {post} /api/v3/tasks/user Create a new task the user. + * @apiDescription Can be passed an object to create a single task or an array of objects to create multiple tasks. * @apiVersion 3.0.0 * @apiName CreateUserTasks * @apiGroup Task * - * @apiSuccess {Object} task The newly created task - * @apiSuccess {Object[]} tasks The newly created tasks (if more than one was created) + * @apiSuccess data An object if a single task was created, otherwise an array of tasks */ api.createUserTasks = { method: 'POST', @@ -73,15 +73,15 @@ api.createUserTasks = { }; /** - * @api {post} /api/v3/tasks/challenge/:challengeId Create a new task belonging to the challenge. Can be passed an object to create a single task or an array of objects to create multiple tasks. + * @api {post} /api/v3/tasks/challenge/:challengeId Create a new task belonging to a challenge. + * @apiDescription Can be passed an object to create a single task or an array of objects to create multiple tasks. * @apiVersion 3.0.0 * @apiName CreateChallengeTasks * @apiGroup Task * * @apiParam {UUID} challengeId The id of the challenge the new task(s) will belong to. * - * @apiSuccess {Object} task The newly created task - * @apiSuccess {Object[]} tasks The newly created tasks (if more than one was created) + * @apiSuccess data An object if a single task was created, otherwise an array of tasks */ api.createChallengeTasks = { method: 'POST', @@ -171,7 +171,7 @@ async function _getTasks (req, res, user, challenge) { * * @apiParam {string="habits","dailys","todos","rewards","completedTodos"} type Optional query parameter to return just a type of tasks. By default all types will be returned except completed todos that requested separately. * - * @apiSuccess {Array} tasks An array of task objects + * @apiSuccess {Array} data An array of tasks */ api.getUserTasks = { method: 'GET', @@ -198,7 +198,7 @@ api.getUserTasks = { * @apiParam {UUID} challengeId The id of the challenge from which to retrieve the tasks. * @apiParam {string="habits","dailys","todos","rewards"} type Optional query parameter to return just a type of tasks * - * @apiSuccess {Array} tasks An array of task objects + * @apiSuccess {Array} data An array of tasks */ api.getChallengeTasks = { method: 'GET', @@ -225,14 +225,14 @@ api.getChallengeTasks = { }; /** - * @api {get} /api/v3/task/:taskId Get a task given its id + * @api {get} /api/v3/task/:taskId Get a task * @apiVersion 3.0.0 * @apiName GetTask * @apiGroup Task * * @apiParam {UUID} taskId The task _id * - * @apiSuccess {object} task The task object + * @apiSuccess {object} data The task object */ api.getTask = { method: 'GET', @@ -273,7 +273,7 @@ api.getTask = { * * @apiParam {UUID} taskId The task _id * - * @apiSuccess {object} task The updated task + * @apiSuccess {object} data The updated task */ api.updateTask = { method: 'PUT', @@ -284,8 +284,6 @@ api.updateTask = { let challenge; req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID(); - // TODO check that req.body isn't empty - // TODO make sure tags are updated correctly (they aren't set as modified!) maybe use specific routes let validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; @@ -304,10 +302,10 @@ api.updateTask = { throw new NotFound(res.t('taskNotFound')); } - // TODO we have to convert task to an object because otherwise things don't get merged correctly. Bad for performances? + // we have to convert task to an object because otherwise things don't get merged correctly. Bad for performances? // TODO regarding comment above, make sure other models with nested fields are using this trick too _.assign(task, Tasks.Task.sanitize(common.ops.updateTask(task.toObject(), req))); - // TODO console.log(task.modifiedPaths(), task.toObject().repeat === tep) + // console.log(task.modifiedPaths(), task.toObject().repeat === tep) // repeat is always among modifiedPaths because mongoose changes the other of the keys when using .toObject() // see https://github.com/Automattic/mongoose/issues/2749 @@ -351,7 +349,9 @@ function _generateWebhookTaskData (task, direction, delta, stats, user) { * @apiParam {UUID} taskId The task _id * @apiParam {string="up","down"} direction The direction for scoring the task * - * @apiSuccess {object} empty An empty object + * @apiSuccess {object} data._tmp If an item was dropped it'll be returned in te _tmp object + * @apiSuccess {number} data.delta + * @apiSuccess {object} data The user stats */ api.scoreTask = { method: 'POST', @@ -389,7 +389,7 @@ api.scoreTask = { let hasTask = removeFromArray(user.tasksOrder.todos, task._id); if (!hasTask) { user.tasksOrder.todos.push(task._id); // TODO push at the top? - } // If for some reason it hadn't been removed previously don't do anything TODO ok? + } // If for some reason it hadn't been removed previously don't do anything } } @@ -406,7 +406,6 @@ api.scoreTask = { sendTaskWebhook(user.preferences.webhooks, _generateWebhookTaskData(task, direction, delta, userStats, user)); - // TODO test? if (task.challenge.id && task.challenge.taskId && !task.challenge.broken && task.type !== 'reward') { // Wrapping everything in a try/catch block because if an error occurs using `await` it MUST NOT bubble up because the request has already been handled try { @@ -423,7 +422,6 @@ api.scoreTask = { }; // completed todos cannot be moved, they'll be returned ordered by date of completion -// TODO check that it works when a tag is selected or todos are split between dated and due // TODO support challenges? /** * @api {post} /api/v3/tasks/:taskId/move/to/:position Move a task to a new position @@ -432,9 +430,9 @@ api.scoreTask = { * @apiGroup Task * * @apiParam {UUID} taskId The task _id - * @apiParam {Number} position Where to move the task (-1 means push to bottom). First position is 0 + * @apiParam {Number} position Query parameter - Where to move the task (-1 means push to bottom). First position is 0 * - * @apiSuccess {object} tasksOrder The new tasks order (user.tasksOrder.{task.type}s) + * @apiSuccess {array} data The new tasks order (user.tasksOrder.{task.type}s) */ api.moveTask = { method: 'POST', @@ -481,14 +479,14 @@ api.moveTask = { }; /** - * @api {post} /api/v3/tasks/:taskId/checklist Add an item to a checklist, creating the checklist if it doesn't exist + * @api {post} /api/v3/tasks/:taskId/checklist Add an item to the task's checklist * @apiVersion 3.0.0 * @apiName AddChecklistItem * @apiGroup Task * * @apiParam {UUID} taskId The task _id * - * @apiSuccess {object} task The updated task + * @apiSuccess {object} data The updated task */ api.addChecklistItem = { method: 'POST', @@ -499,7 +497,6 @@ api.addChecklistItem = { let challenge; req.checkParams('taskId', res.t('taskIdRequired')).notEmpty().isUUID(); - // TODO check that req.body isn't empty and is an array let validationErrors = req.validationErrors(); if (validationErrors) throw validationErrors; @@ -523,7 +520,7 @@ api.addChecklistItem = { task.checklist.push(Tasks.Task.sanitizeChecklist(req.body)); // TODO why not allow to supply _id on creation? let savedTask = await task.save(); - res.respond(200, savedTask); // TODO what to return + res.respond(200, savedTask); if (challenge) challenge.updateTask(savedTask); }, }; @@ -537,7 +534,7 @@ api.addChecklistItem = { * @apiParam {UUID} taskId The task _id * @apiParam {UUID} itemId The checklist item _id * - * @apiSuccess {object} task The updated task + * @apiSuccess {object} data The updated task */ api.scoreCheckListItem = { method: 'POST', @@ -566,7 +563,7 @@ api.scoreCheckListItem = { item.completed = !item.completed; let savedTask = await task.save(); - res.respond(200, savedTask); // TODO what to return + res.respond(200, savedTask); }, }; @@ -579,7 +576,7 @@ api.scoreCheckListItem = { * @apiParam {UUID} taskId The task _id * @apiParam {UUID} itemId The checklist item _id * - * @apiSuccess {object} task The updated task + * @apiSuccess {object} data The updated task */ api.updateChecklistItem = { method: 'PUT', @@ -616,7 +613,7 @@ api.updateChecklistItem = { _.merge(item, Tasks.Task.sanitizeChecklist(req.body)); let savedTask = await task.save(); - res.respond(200, savedTask); // TODO what to return + res.respond(200, savedTask); if (challenge) challenge.updateTask(savedTask); }, }; @@ -630,7 +627,7 @@ api.updateChecklistItem = { * @apiParam {UUID} taskId The task _id * @apiParam {UUID} itemId The checklist item _id * - * @apiSuccess {object} empty An empty object + * @apiSuccess {object} data The updated task */ api.removeChecklistItem = { method: 'DELETE', @@ -665,7 +662,7 @@ api.removeChecklistItem = { if (!hasItem) throw new NotFound(res.t('checklistItemNotFound')); let savedTask = await task.save(); - res.respond(200, {}); // TODO what to return + res.respond(200, savedTask); if (challenge) challenge.updateTask(savedTask); }, }; @@ -679,7 +676,7 @@ api.removeChecklistItem = { * @apiParam {UUID} taskId The task _id * @apiParam {UUID} tagId The tag id * - * @apiSuccess {object} task The updated task + * @apiSuccess {object} data The updated task */ api.addTagToTask = { method: 'POST', @@ -709,12 +706,12 @@ api.addTagToTask = { task.tags.push(tagId); let savedTask = await task.save(); - res.respond(200, savedTask); // TODO what to return + res.respond(200, savedTask); }, }; /** - * @api {delete} /api/v3/tasks/:taskId/tags/:tagId Remove a tag + * @api {delete} /api/v3/tasks/:taskId/tags/:tagId Remove a tag from atask * @apiVersion 3.0.0 * @apiName RemoveTagFromTask * @apiGroup Task @@ -722,7 +719,7 @@ api.addTagToTask = { * @apiParam {UUID} taskId The task _id * @apiParam {UUID} tagId The tag id * - * @apiSuccess {object} empty An empty object + * @apiSuccess {object} data The updated task */ api.removeTagFromTask = { method: 'DELETE', @@ -747,8 +744,8 @@ api.removeTagFromTask = { let hasTag = removeFromArray(task.tags, req.params.tagId); if (!hasTag) throw new NotFound(res.t('tagNotFound')); - await task.save(); - res.respond(200, {}); // TODO what to return + let savedTask = await task.save(); + res.respond(200, savedTask); }, }; @@ -761,7 +758,7 @@ api.removeTagFromTask = { * * @apiParam {UUID} taskId The task _id * - * @apiSuccess {object} empty An empty object + * @apiSuccess {object} data An empty object */ api.unlinkTask = { method: 'POST', @@ -798,7 +795,7 @@ api.unlinkTask = { } } - res.respond(200, {}); // TODO what to return + res.respond(200, {}); }, }; @@ -808,7 +805,7 @@ api.unlinkTask = { * @apiName ClearCompletedTodos * @apiGroup Task * - * @apiSuccess {object} empty An empty object + * @apiSuccess {object} data An empty object */ api.clearCompletedTodos = { method: 'POST', @@ -841,7 +838,7 @@ api.clearCompletedTodos = { * * @apiParam {UUID} taskId The task _id * - * @apiSuccess {object} empty An empty object + * @apiSuccess {object} data An empty object */ api.deleteTask = { method: 'DELETE', diff --git a/website/src/controllers/api-v3/user.js b/website/src/controllers/api-v3/user.js index f0d4b8d925..f13c6d0a73 100644 --- a/website/src/controllers/api-v3/user.js +++ b/website/src/controllers/api-v3/user.js @@ -24,7 +24,7 @@ let api = {}; * @apiName UserGet * @apiGroup User * - * @apiSuccess {Object} user The user object + * @apiSuccess {Object} data The user object */ api.getUser = { method: 'GET', @@ -51,7 +51,7 @@ api.getUser = { * @apiName UserGetBuyList * @apiGroup User * - * @apiSuccess {Object} list The buy list + * @apiSuccess {Object} data The buy list */ api.getBuyList = { method: 'GET', @@ -141,12 +141,13 @@ let checkPreferencePurchase = (user, path, item) => { }; /** - * @api {put} /api/v3/user Update the user. Example body: {'stats.hp':50, 'preferences.background': 'beach'} + * @api {put} /api/v3/user Update the user. + * @apiDescription Example body: {'stats.hp':50, 'preferences.background': 'beach'} * @apiVersion 3.0.0 * @apiName UserUpdate * @apiGroup User * - * @apiSuccess user object The updated user object + * @apiSuccess {object} data The updated user object */ api.updateUser = { method: 'PUT', @@ -175,14 +176,14 @@ api.updateUser = { }; /** - * @api {delete} /api/v3/user DELETE an authenticated user's profile + * @api {delete} /api/v3/user DELETE an authenticated user's account * @apiVersion 3.0.0 * @apiName UserDelete * @apiGroup User * - * @apiParam {string} password The user's password unless it's a Facebook account + * @apiParam {string} password The user's password (unless it's a Facebook account) * - * @apiSuccess {Object} empty An empty Object + * @apiSuccess {Object} data An empty Object */ api.deleteUser = { method: 'DELETE', @@ -242,7 +243,9 @@ function _cleanChecklist (task) { * @apiVersion 3.0.0 * @apiName UserGetAnonymized * @apiGroup User - * @apiSuccess {Object} object The object { user, tasks } + * + * @apiSuccess {Object} data.user + * @apiSuccess {Array} data.tasks **/ api.getUserAnonymized = { method: 'GET', @@ -309,7 +312,7 @@ const partyMembersFields = 'profile.name stats achievements items.special'; * @apiParam {string} spellId The spell to cast. * @apiParam {UUID} targetId Optional query parameter, the id of the target when casting a spell on a party member or a task. * - * @apiSuccess mixed Will return the modified targets. For party members only the necessary fields will be populated. + * @apiSuccess data Will return the modified targets. For party members only the necessary fields will be populated. */ api.castSpell = { method: 'POST', @@ -419,7 +422,7 @@ api.castSpell = { * @apiName UserSleep * @apiGroup User * - * @apiSuccess {Object} Will return an object with the new `user.preferences.sleep` value. Example `{preferences: {sleep: true}}` + * @apiSuccess {Object} data Will return an object with the new `user.preferences.sleep` value. Example `{preferences: {sleep: true}}` */ api.sleep = { method: 'POST', diff --git a/website/src/controllers/top-level/auth.js b/website/src/controllers/top-level/auth.js index cf66b5996f..90724ab1f4 100644 --- a/website/src/controllers/top-level/auth.js +++ b/website/src/controllers/top-level/auth.js @@ -1,7 +1,3 @@ -import { - authWithSession, -} from '../../middlewares/api-v3/auth'; - let api = {}; // Internal authentication routes diff --git a/website/src/controllers/top-level/dataexport.js b/website/src/controllers/top-level/dataexport.js index f76e11421c..33e2fee5c2 100644 --- a/website/src/controllers/top-level/dataexport.js +++ b/website/src/controllers/top-level/dataexport.js @@ -26,11 +26,11 @@ const BASE_URL = nconf.get('BASE_URL'); let api = {}; /** - * @api {get} /export/history.csv Export user tasks history in CSV format. History is only available for habits and dailys so todos and rewards won't be included + * @api {get} /export/history.csv Export user tasks history in CSV format. + * @apiDescription History is only available for habits and dailys so todos and rewards won't be included NOTE: Part of the private API that may change at any time. * @apiVersion 3.0.0 * @apiName ExportUserHistory * @apiGroup DataExport - * @apiDescription NOTE: Part of the private API that may change at any time. * * @apiSuccess {string} A cvs file */ diff --git a/website/src/libs/api-v3/i18n.js b/website/src/libs/api-v3/i18n.js index 0486cce3f6..95a7b5c60d 100644 --- a/website/src/libs/api-v3/i18n.js +++ b/website/src/libs/api-v3/i18n.js @@ -2,6 +2,7 @@ import fs from 'fs'; import path from 'path'; import _ from 'lodash'; import shared from '../../../../common'; +import logger from './logger'; export const localePath = path.join(__dirname, '/../../../../common/locales/'); @@ -68,8 +69,7 @@ langCodes.forEach((code) => { momentLangs[code] = f; } catch (e) { // eslint-disable-lint no-empty - // TODO implement some type of error loggin? - // The catch block is mandatory so can't be removed + // The catch block is mandatory so it won't crash the server } }); diff --git a/website/src/models/group.js b/website/src/models/group.js index a5a43a10ce..9542500d91 100644 --- a/website/src/models/group.js +++ b/website/src/models/group.js @@ -31,7 +31,7 @@ export let schema = new Schema({ type: {type: String, enum: ['guild', 'party'], required: true}, privacy: {type: String, enum: ['private', 'public'], default: 'private', required: true}, // _v: {type: Number,'default': 0}, // TODO ? - chat: Array, // TODO ? + chat: Array, /* # [{ # timestamp: Date @@ -44,7 +44,7 @@ export let schema = new Schema({ */ leaderOnly: { // restrict group actions to leader (members can't do them) challenges: {type: Boolean, default: false, required: true}, - // invites: {type:Boolean, 'default':false} // TODO ? + // invites: {type:Boolean, 'default':false} }, memberCount: {type: Number, default: 1}, challengeCount: {type: Number, default: 0}, @@ -66,7 +66,6 @@ export let schema = new Schema({ // Shows boolean for each party-member who has accepted the quest. Eg {UUID: true, UUID: false}. Once all users click // 'Accept', the quest begins. If a false user waits too long, probably a good sign to prod them or boot them. // TODO when booting user, remove from .joined and check again if we can now start the quest - // TODO as long as quests are party only we can keep it here members: {type: Schema.Types.Mixed, default: () => { return {}; }}, @@ -277,7 +276,7 @@ schema.methods.sendChat = function sendChat (message, user) { this.chat.unshift(chatDefaults(message, user)); this.chat.splice(200); - // Kick off chat notifications in the background. // TODO refactor + // Kick off chat notifications in the background. let lastSeenUpdate = {$set: {}, $inc: {_v: 1}}; lastSeenUpdate.$set[`newMessages.${this._id}`] = {name: this.name, value: true}; diff --git a/website/src/models/user.js b/website/src/models/user.js index 76c730ec26..6ee6b59ca8 100644 --- a/website/src/models/user.js +++ b/website/src/models/user.js @@ -231,7 +231,6 @@ export let schema = new Schema({ }, history: { - // TODO absolutely preen these for everyone exp: Array, // [{date: Date, value: Number}], // big peformance issues if these are defined todos: Array, // [{data: Date, value: Number}] // big peformance issues if these are defined },