diff --git a/API.md b/API.md deleted file mode 100644 index 401a8569a1..0000000000 --- a/API.md +++ /dev/null @@ -1,4 +0,0 @@ -## APIv2 - -## APIv1 (Deprecated) -Make sure to send `PUT /api/v1/user` request bodies as `{'set.this.path':value}` instead of `{set:{this:{path:value}}}` \ No newline at end of file diff --git a/src/controllers/user.js b/src/controllers/user.js index 4937e57892..d1f7f1b56f 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -326,7 +326,14 @@ api.buyGemsPaypalIPN = function(req, res) { */ api.cast = function(req, res) { var user = res.locals.user; - var type = req.body.type, target = req.body.target; + var type, target; + if (req.body.type) { // this is the method implemented in the app, it's not correct + type = req.body.type; + target = req.body.target; + } else if (req.query.targetType) { // this is the supported API method + type = req.query.targetType; + target = req.query.targetId; + } var klass = shared.content.spells.special[req.params.spell] ? 'special' : user.stats.class var spell = shared.content.spells[klass][req.params.spell]; diff --git a/src/models/user.js b/src/models/user.js index dccf7dc80e..2a284eda3b 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -14,14 +14,6 @@ var Challenge = require('./challenge').model; // User Schema // ----------- -var eggPotionMapping = _.transform(shared.content.eggs, function(m, egg){ - _.defaults(m, _.transform(shared.content.hatchingPotions, function(m2, pot){ - m2[egg.key + '-' + pot.key] = true; - })); -}) - -var specialPetsMapping = shared.content.specialPets; // may need to revisit if we add additional information about the special pets - var UserSchema = new Schema({ // ### UUID and API Token _id: { @@ -146,9 +138,9 @@ var UserSchema = new Schema({ pets: _.defaults( // First transform to a 1D eggs/potions mapping - _.transform(eggPotionMapping, function(m,v,k){ m[k] = Number; }), + _.transform(shared.content.pets, function(m,v,k){ m[k] = Number; }), // Then add additional pets (backer, contributor) - _.transform(specialPetsMapping, function(m,v,k){ m[k] = Number; }) + _.transform(shared.content.specialPets, function(m,v,k){ m[k] = Number; }) ), currentPet: String, // Cactus-Desert @@ -177,7 +169,7 @@ var UserSchema = new Schema({ // } mounts: _.defaults( // First transform to a 1D eggs/potions mapping - _.transform(eggPotionMapping, function(m,v,k){ m[k] = Boolean; }), + _.transform(shared.content.pets, function(m,v,k){ m[k] = Boolean; }), // Then add additional pets (backer, contributor) { 'LionCub-Ethereal': Boolean, diff --git a/src/routes/apiv2.coffee b/src/routes/apiv2.coffee index e33a68b4e5..f8c63d3b33 100644 --- a/src/routes/apiv2.coffee +++ b/src/routes/apiv2.coffee @@ -58,10 +58,12 @@ module.exports = (swagger, v2) -> "/user/tasks/{id}/{direction}": spec: - description: "Simple scoring of a task" + #notes: "Simple scoring of a task." + description: "Simple scoring of a task. This is most-likely the only API route you'll be using as a 3rd-party developer. The most common operation is for the user to gain or lose points based on some action (browsing Reddit, running a mile, 1 Pomodor, etc). Call this route, if the task you're trying to score doesn't exist, it will be created for you." parameters: [ path("id", "ID of the task to score. If this task doesn't exist, a task will be created automatically", "string") path("direction", "Either 'up' or 'down'", "string") + body '',"If you're creating a 3rd-party task, pass up any task attributes in the body (see TaskSchema).",'object' ] method: 'POST' action: user.score @@ -85,8 +87,8 @@ module.exports = (swagger, v2) -> description: "Update a user's task" method: 'PUT' parameters: [ - path("id", "Task ID", "string") - body("","Send up the whole task","object") + path "id", "Task ID", "string" + body "","Send up the whole task (see TaskSchema)","object" ] action: user.updateTask @@ -101,7 +103,7 @@ module.exports = (swagger, v2) -> spec: description: "Create a task" method: 'POST' - parameters: [ body("","Send up the whole task","object") ] + parameters: [ body "","Send up the whole task (see TaskSchema)","object" ] action: user.addTask @@ -119,7 +121,7 @@ module.exports = (swagger, v2) -> "/user/tasks/clear-completed": spec: method: 'POST' - description: "Clears competed To-Dos (needed periodically for performance." + description: "Clears competed To-Dos (needed periodically for performance)." action: user.clearCompleted @@ -127,8 +129,10 @@ module.exports = (swagger, v2) -> spec: method: 'POST' description: 'Unlink a task from its challenge' - # TODO query params? - parameters: [path("id", "Task ID", "string")] + parameters: [ + path("id", "Task ID", "string") + query 'keep',"When unlinking a challenge task, how to handle the orphans?",'string',['keep','keep-all','remove','remove-all'] + ] middleware: auth.auth ## removing cron since they may want to remove task first action: challenges.unlink @@ -140,7 +144,6 @@ module.exports = (swagger, v2) -> description: "Buy a gear piece and equip it automatically" parameters:[ path 'key',"The key of the item to buy (call /content route for available keys)",'string', _.keys(content.gear.flat) - #TODO embed keys ] action: user.buy @@ -171,7 +174,7 @@ module.exports = (swagger, v2) -> method: 'POST' description: "Feed your pet some food" parameters: [ - path 'pet',"The key of the pet you're feeding",'string'#,_.keys(content.pets)) + path 'pet',"The key of the pet you're feeding",'string',_.keys(content.pets) path 'food',"The key of the food to feed your pet",'string',_.keys(content.food) ] action: user.feed @@ -210,7 +213,7 @@ module.exports = (swagger, v2) -> method: 'PUT' description: "Update the user object (only certain attributes are supported)" parameters: [ - body '','The user object','object' + body '','The user object (see UserSchema)','object' ] action: user.update @@ -266,15 +269,20 @@ module.exports = (swagger, v2) -> method: 'POST' description: "Allocate one point towards an attribute" parameters: [ - query 'stat','The stat to allocate towards','string' + query 'stat','The stat to allocate towards','string',['str','per','int','con'] ] action:user.allocate "/user/class/cast/{spell}": spec: method: 'POST' - description: "Cast a spell" - #TODO finish + description: "Casts a spell on a target." + params: [ + path 'spell',"The key of the spell to cast (see habitrpg-shared#content.coffee)",'string' + query 'targetType',"The type of object you're targeting",'string',['party','self','user','task'] + query 'targetId',"The ID of the object you're targeting",'string' + + ] action: user.cast "/user/unlock": @@ -287,12 +295,12 @@ module.exports = (swagger, v2) -> action: user.unlock "/user/buy-gems": - spec: method: 'POST', description: "Do not use this route!" + spec: method: 'POST', description: "Do not use this route" middleware: auth.auth action:user.buyGems "/user/buy-gems/paypal-ipn": - spec: method: 'POST', description: "Don't use this route!" + spec: method: 'POST', description: "Don't use this route" action: user.buyGemsPaypalIPN "/user/batch-update": @@ -311,8 +319,7 @@ module.exports = (swagger, v2) -> method: 'POST' description: 'Create a new tag' parameters: [ - #TODO document - body '','New tag','object' + body '','New tag (see UserSchema.tags)','object' ] action: user.addTag @@ -323,7 +330,7 @@ module.exports = (swagger, v2) -> description: "Edit a tag" parameters: [ path 'id','The id of the tag to edit','string' - body '','Tag edits','object' + body '','Tag edits (see UserSchema.tags)','object' ] action: user.updateTag @@ -341,61 +348,106 @@ module.exports = (swagger, v2) -> # Groups # --------------------------------- "/groups:GET": - spec: path: '/groups' + spec: + path: '/groups' + description: "Get a list of groups" + params: [ + query 'type',"Comma-separated types of groups to return, eg 'party,guilds,public,tavern'",'string' + ] middleware: auth.auth action: groups.list + "/groups:POST": - spec: path: '/groups', method: 'POST' + spec: + path: '/groups', method: 'POST' + description: 'Create a group' + params: [ + body '','Group object (see GroupSchema)','object' + ] middleware: auth.auth action: groups.create "/groups/{gid}:GET": - spec: path: '/groups/{gid}' + spec: + path: '/groups/{gid}' + description: "Get a group" + params: [path('gid','Group ID','string')] middleware: auth.auth action: groups.get "/groups/{gid}": - spec: path: '/groups/{gid}', method: 'PUT' + spec: + path: '/groups/{gid}', method: 'PUT' + description: "Edit a group" + params: [body('','Group object (see GroupSchema)','object')] middleware: [auth.auth, groups.attachGroup] action: groups.update "/groups/{gid}/join": - spec: method: 'POST' + spec: + method: 'POST' + description: 'Join a group' + params: [path('gid','Id of the group to join','string')] middleware: [auth.auth, groups.attachGroup] action: groups.join "/groups/{gid}/leave": - spec: method: 'POST' + spec: + method: 'POST' + description: 'Leave a group' + params: [path('ID of the group to leave','string')] middleware: [auth.auth, groups.attachGroup] action: groups.leave "/groups/{gid}/invite": - spec: method: 'POST' + spec: + method: 'POST' + description: "Invite a user to a group" + params: [ + path 'gid','Group id','string' + query 'uuid','User id to invite','string' + ] middleware: [auth.auth, groups.attachGroup] action:groups.invite "/groups/{gid}/removeMember": - spec:method: 'POST' + spec: + method: 'POST' + description: "Remove / boot a member from a group" + params: [ + path 'gid','Group id','string' + query 'uuid','User id to boot','string' + ] middleware: [auth.auth, groups.attachGroup] action:groups.removeMember "/groups/{gid}/questAccept": spec: method: 'POST' + description: "Accept a quest invitation" parameters: [ + path 'gid',"Group id",'string' query 'key',"optional. if provided, trigger new invite, if not, accept existing invite",'string' ] middleware: [auth.auth, groups.attachGroup] action:groups.questAccept "/groups/{gid}/questReject": - spec: method: 'POST' + spec: + method: 'POST' + description: 'Reject quest invitation' + params: [ + path 'gid','Group id','string' + ] middleware: [auth.auth, groups.attachGroup] action: groups.questReject "/groups/{gid}/questAbort": - spec: method: 'POST' + spec: + method: 'POST' + description: 'Abort quest' + params: [path('gid','Group to abort quest in','string')] middleware: [auth.auth, groups.attachGroup] action: groups.questAbort @@ -403,12 +455,21 @@ module.exports = (swagger, v2) -> #TODO PUT /groups/:gid/chat/:messageId "/groups/{gid}/chat": - spec: method: 'POST' + spec: + method: 'POST' + description: "Send a chat message" + params: [ + query 'message', 'Chat message','string' + path 'gid','Group id','string' + ] middleware: [auth.auth, groups.attachGroup] action: groups.postChat "/groups/{gid}/chat/{messageId}": - spec: method: 'DELETE' + spec: + method: 'DELETE' + description: 'Delete a group' + params: [path('gid','ID of group to delete','string')] middleware: [auth.auth, groups.attachGroup] action: groups.deleteChatMessage @@ -446,46 +507,82 @@ module.exports = (swagger, v2) -> # (eg /groups/:gid/challenges/:cid), they will also be referenced by users from the "challenges" tab # without knowing which group they belong to. So to prevent unecessary lookups, we have them as a top-level resource "/challenges:GET": - spec: path: '/challenges' + spec: + path: '/challenges' + description: "Get a list of challenges" middleware: [auth.auth] action: challenges.list "/challenges:POST": - spec: path: '/challenges', method: 'POST' + spec: + path: '/challenges' + method: 'POST' + description: "Create a challenge" + params: [body('','Challenge object (see ChallengeSchema)','object')] middleware: [auth.auth] action: challenges.create "/challenges/{cid}:GET": - spec: {} + spec: + description: 'Get a challenge' + params: [path('cid','Challenge id','string')] action: challenges.get "/challenges/{cid}:POST": - spec: path: '/challenges/{cid}', method: 'POST' + spec: + path: '/challenges/{cid}' + method: 'POST' + description: "Update a challenge" + params: [ + path 'cid','Challenge id','string' + body('','Challenge object (see ChallengeSchema)','object') + ] middleware: [auth.auth] action: challenges.update "/challenges/{cid}:DELETE": - spec: path: '/challenges/{cid}', method: 'DELETE' + spec: + path: '/challenges/{cid}' + method: 'DELETE' + description: "Delete a challenge" + params: [path('cid','Challenge id','string')] middleware: [auth.auth] action: challenges["delete"] "/challenges/{cid}/close": - spec: method: 'POST' + spec: + method: 'POST' + description: 'Close a challenge' + params: [ + path 'cid','Challenge id','string' + query 'uid','User ID of the winner','string',true + ] middleware: [auth.auth] action: challenges.selectWinner "/challenges/{cid}/join": - spec: method: 'POST' + spec: + method: 'POST' + description: "Join a challenge" + params: [path('cid','Challenge id','string')] middleware: [auth.auth] action: challenges.join "/challenges/{cid}/leave": - spec: method: 'POST' + spec: + method: 'POST' + description: 'Leave a challenge' + params: [path('cid','Challenge id','string')] middleware: [auth.auth] action: challenges.leave "/challenges/{cid}/member/{uid}": - spec: {} + spec: + description: "Get a member's progress in a particular challenge" + params: [ + path 'cid','Challenge id','string' + path 'uid','User id','string' + ] middleware: [auth.auth] action: challenges.getMember diff --git a/views/static/api.jade b/views/static/api.jade index df1a73fad4..8b19ff59e1 100644 --- a/views/static/api.jade +++ b/views/static/api.jade @@ -60,7 +60,7 @@ html body #header .swagger-ui-wrap - a#logo(href='http://swagger.wordnik.com') HabitRPG API Documentation (Under Construction) + a#logo(href='http://swagger.wordnik.com') HabitRPG API Documentation .swagger-ui-wrap(style='padding:50px') form#api_selector @@ -71,7 +71,9 @@ html input#input_baseUrl(placeholder='http://example.com/api', name='baseUrl', type='text') //.input a#explore(href='#') Explore - p(style='color:red') This documentation is a work in progress. If you'd like to help finish it, see #2217. - + br + p You'll need to set x-api-user and x-api-key headers for each request, read more here. (TODO: We need to move that Wiki document into this file and delete the Wiki) + p For create & edit paths (PUT & POST), you'll need to know the schema of the object you're trying to create or edit. See Schema definitions here + p If any of the documentation is insufficient or you're having trouble with it, please post an issue to Github #message-bar.swagger-ui-wrap #swagger-ui-container.swagger-ui-wrap