mirror of
https://github.com/sudoxnym/habitica-self-host.git
synced 2026-05-22 13:48:37 +00:00
#2217 start adding APIv2 generated documentation via swagger-node-express
This commit is contained in:
parent
0f3202f964
commit
0edcfac46b
6 changed files with 307 additions and 4 deletions
|
|
@ -38,13 +38,13 @@
|
|||
"gemoji": "git://github.com/github/gemoji",
|
||||
"sticky": "*",
|
||||
"bootstrap-tour": "~0.8.0",
|
||||
"angular-ui-utils": "~0.1.0"
|
||||
"angular-ui-utils": "~0.1.0",
|
||||
"swagger-ui": "~2.0.3"
|
||||
},
|
||||
"resolutions": {
|
||||
"jquery": "~2.0.3",
|
||||
"bootstrap": "v2.3.2",
|
||||
"angular": "~1.2.1",
|
||||
"angular-ui-utils": "~0.1.0"
|
||||
"angular": "~1.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular-mocks": "~1.2.1"
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@
|
|||
"pretty-data": "git://github.com/vkiryukhin/pretty-data#master",
|
||||
"js2xmlparser": "~0.1.2",
|
||||
"mongoose": "~3.8.1",
|
||||
"domain-middleware": "~0.1.0"
|
||||
"domain-middleware": "~0.1.0",
|
||||
"swagger-node-express": "~1.3.2"
|
||||
},
|
||||
"private": true,
|
||||
"subdomain": "habitrpg",
|
||||
|
|
|
|||
225
src/apidoc.coffee
Normal file
225
src/apidoc.coffee
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
# see https://github.com/wordnik/swagger-node-express
|
||||
|
||||
_ = require('lodash')
|
||||
|
||||
module.exports = (swagger) ->
|
||||
|
||||
swagger.configureSwaggerPaths("", "/api-docs", "")
|
||||
|
||||
api =
|
||||
"/content":
|
||||
description: "Get all available content objects. This is essential, since Habit often depends on item keys (eg, when purchasing a weapon)."
|
||||
method: 'GET'
|
||||
|
||||
"/export/history":
|
||||
description: "Export user history"
|
||||
method: 'GET'
|
||||
|
||||
# ---------------------------------
|
||||
# User
|
||||
# ---------------------------------
|
||||
|
||||
# Scoring
|
||||
|
||||
"/user/tasks/{id}/{direction}":
|
||||
description: "Simple scoring of a task"
|
||||
params: [
|
||||
swagger.pathParam("id", "ID of the task to score. If this task doesn't exist, a task will be created automatically", "string")
|
||||
swagger.pathParam("direction", "Either 'up' or 'down'", "string")
|
||||
]
|
||||
method: 'POST'
|
||||
|
||||
# Tasks
|
||||
"/user/tasks":
|
||||
description: "Get all user's tasks"
|
||||
|
||||
"/user/tasks/{id}":
|
||||
description: "Get an individual task"
|
||||
params: [
|
||||
swagger.pathParam("id", "Task ID", "string")
|
||||
]
|
||||
|
||||
"/user/tasks/{id}":
|
||||
description: "Update a user's task"
|
||||
method: 'PUT'
|
||||
params: [
|
||||
swagger.pathParam("id", "Task ID", "string")
|
||||
]
|
||||
body: [
|
||||
swagger.bodyParam("task","Send up the whole task","string")
|
||||
]
|
||||
|
||||
"/user/tasks/{id}":
|
||||
method: 'DELETE'
|
||||
"/user/tasks":
|
||||
method: 'POST'
|
||||
#body={}
|
||||
"/user/tasks/{id}/sort":
|
||||
method: 'POST'
|
||||
#query={to,from}
|
||||
"/user/tasks/clear-completed":
|
||||
method: 'POST'
|
||||
"/user/tasks/{id}/unlink":
|
||||
method: 'POST'
|
||||
|
||||
# Inventory
|
||||
"/user/inventory/buy/{key}":
|
||||
method: 'POST'
|
||||
"/user/inventory/sell/{type}/{key}":
|
||||
method: 'POST'
|
||||
"/user/inventory/purchase/{type}/{key}":
|
||||
method: 'POST'
|
||||
"/user/inventory/feed/{pet}/{food}":
|
||||
method: 'POST'
|
||||
"/user/inventory/equip/{type}/{key}":
|
||||
method: 'POST'
|
||||
"/user/inventory/hatch/{egg}/{hatchingPotion}":
|
||||
method: 'POST'
|
||||
|
||||
# User
|
||||
"/user:GET":
|
||||
path: '/user'
|
||||
"/user:PUT":
|
||||
path: '/user'
|
||||
method: 'PUT'
|
||||
# body={}
|
||||
"/user:DELETE":
|
||||
path: '/user'
|
||||
method: 'DELETE'
|
||||
"/user/revive":
|
||||
method: 'POST'
|
||||
"/user/reroll":
|
||||
method: 'POST'
|
||||
"/user/reset":
|
||||
method: 'POST'
|
||||
"/user/sleep":
|
||||
method: 'POST'
|
||||
"/user/rebirth":
|
||||
method: 'POST'
|
||||
"/user/class/change":
|
||||
method: 'POST'
|
||||
#query={class}
|
||||
"/user/class/allocate":
|
||||
method: 'POST'
|
||||
#query={stat}
|
||||
"/user/class/cast/:spell":
|
||||
method: 'POST'
|
||||
"/user/unlock":
|
||||
method: 'POST'
|
||||
"/user/buy-gems":
|
||||
method: 'POST'
|
||||
"/user/batch-update":
|
||||
method: 'POST'
|
||||
|
||||
# Tags
|
||||
"/user/tags":
|
||||
method: 'POST'
|
||||
#body={}
|
||||
"/user/tags/{id}:PUT":
|
||||
path: 'user/tags/{id}'
|
||||
method: 'PUT'
|
||||
#body={}
|
||||
"/user/tags/{id}:DELETE":
|
||||
path: 'user/tags/{id}'
|
||||
method: 'DELETE'
|
||||
|
||||
# ---------------------------------
|
||||
# Groups
|
||||
# ---------------------------------
|
||||
"/groups:GET":
|
||||
path: '/groups'
|
||||
"/groups:POST":
|
||||
path: '/groups'
|
||||
method: 'POST'
|
||||
"/groups/{gid}:GET":
|
||||
path: '/groups/{gid}'
|
||||
"/groups/{gid}:POST":
|
||||
path: '/groups/{gid}'
|
||||
method: 'POST'
|
||||
"/groups/{gid}":
|
||||
path: '/groups/{gid}'
|
||||
method: 'PUT'
|
||||
|
||||
"/groups/{gid}/join":
|
||||
method: 'POST'
|
||||
"/groups/{gid}/leave":
|
||||
method: 'POST'
|
||||
"/groups/{gid}/invite":
|
||||
method: 'POST'
|
||||
"/groups/{gid}/removeMember":
|
||||
method: 'POST'
|
||||
"/groups/{gid}/questAccept":
|
||||
method: 'POST'
|
||||
# query={key} (optional. if provided, trigger new invite, if not, accept existing invite)
|
||||
"/groups/{gid}/questReject":
|
||||
method: 'POST'
|
||||
"/groups/{gid}/questAbort":
|
||||
method: 'POST'
|
||||
|
||||
#GET /groups/:gid/chat
|
||||
"/groups/{gid}/chat":
|
||||
method: 'POST'
|
||||
"/groups/{gid}/chat/{messageId}":
|
||||
method: 'DELETE'
|
||||
|
||||
|
||||
# ---------------------------------
|
||||
# Members
|
||||
# ---------------------------------
|
||||
"/members/{uid}":{}
|
||||
|
||||
# ---------------------------------
|
||||
# Challenges
|
||||
# ---------------------------------
|
||||
|
||||
# Note: while challenges belong to groups, and would therefore make sense as a nested resource
|
||||
# (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":
|
||||
path: '/challenges'
|
||||
"/challenges:POST":
|
||||
path: '/challenges'
|
||||
method: 'POST'
|
||||
"/challenges/{cid}:GET": {}
|
||||
"/challenges/{cid}:POST":
|
||||
path: '/challenges/{cid}'
|
||||
method: 'POST'
|
||||
"/challenges/{cid}:DELETE":
|
||||
path: '/challenges/{cid}'
|
||||
method: 'DELETE'
|
||||
"/challenges/{cid}/close":
|
||||
method: 'POST'
|
||||
"/challenges/{cid}/join":
|
||||
method: 'POST'
|
||||
"/challenges/{cid}/leave":
|
||||
method: 'POST'
|
||||
"/challenges/{cid}/member/{uid}":{}
|
||||
|
||||
_.each api, (spec, path) ->
|
||||
## Spec format is:
|
||||
# spec:
|
||||
# path: "/pet/{petId}"
|
||||
# description: "Operations about pets"
|
||||
# notes: "Returns a pet based on ID"
|
||||
# summary: "Find pet by ID"
|
||||
# method: "GET"
|
||||
# params: [swagger.pathParam("petId", "ID of pet that needs to be fetched", "string")]
|
||||
# type: "Pet"
|
||||
# errorResponses: [swagger.errors.invalid("id"), swagger.errors.notFound("pet")]
|
||||
# nickname: "getPetById"
|
||||
|
||||
spec.description ?= ''
|
||||
_.defaults spec,
|
||||
path: path
|
||||
nickname: path
|
||||
notes: spec.description
|
||||
summary: spec.description
|
||||
params: []
|
||||
#type: 'Pet'
|
||||
errorResponses: []
|
||||
method: 'GET'
|
||||
route = {spec}
|
||||
console.log(spec.params)
|
||||
swagger["add#{route.spec.method}"](route);true
|
||||
|
||||
swagger.configure("http://localhost:3000", "0.1")
|
||||
|
|
@ -31,6 +31,10 @@ router.get('/static/terms', middleware.locals, function(req, res) {
|
|||
res.render('static/terms', {env: res.locals.habitrpg});
|
||||
});
|
||||
|
||||
router.get('/static/api', middleware.locals, function(req, res) {
|
||||
res.render('static/api', {env: res.locals.habitrpg});
|
||||
});
|
||||
|
||||
// --------- Redirects --------
|
||||
|
||||
router.get('/splash.html', function(req, res) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ var nconf = require('nconf');
|
|||
var utils = require('./utils');
|
||||
var middleware = require('./middleware');
|
||||
var domainMiddleware = require('domain-middleware');
|
||||
var swagger = require("swagger-node-express");
|
||||
var server;
|
||||
var TWO_WEEKS = 1000 * 60 * 60 * 24 * 14;
|
||||
|
||||
|
|
@ -110,6 +111,9 @@ app.use('/export', require('./routes/dataexport').middleware);
|
|||
|
||||
app.use(utils.errorHandler);
|
||||
|
||||
swagger.setAppHandler(app);
|
||||
require('./apidoc.coffee')(swagger);
|
||||
|
||||
server = http.createServer(app).listen(app.get("port"), function() {
|
||||
return console.log("Express server listening on port " + app.get("port"));
|
||||
});
|
||||
|
|
|
|||
69
views/static/api.jade
Normal file
69
views/static/api.jade
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
!!! 5
|
||||
html
|
||||
head
|
||||
title Swagger UI
|
||||
link(href='//fonts.googleapis.com/css?family=Droid+Sans:400,700', rel='stylesheet', type='text/css')
|
||||
link(href='/bower_components/swagger-ui/dist/css/highlight.default.css', media='screen', rel='stylesheet', type='text/css')
|
||||
link(href='/bower_components/swagger-ui/dist/css/screen.css', media='screen', rel='stylesheet', type='text/css')
|
||||
script(src='/bower_components/swagger-ui/dist/lib/shred.bundle.js', type='text/javascript')
|
||||
script(src='/bower_components/swagger-ui/dist/lib/jquery-1.8.0.min.js', type='text/javascript')
|
||||
script(src='/bower_components/swagger-ui/dist/lib/jquery.slideto.min.js', type='text/javascript')
|
||||
script(src='/bower_components/swagger-ui/dist/lib/jquery.wiggle.min.js', type='text/javascript')
|
||||
script(src='/bower_components/swagger-ui/dist/lib/jquery.ba-bbq.min.js', type='text/javascript')
|
||||
script(src='/bower_components/swagger-ui/dist/lib/handlebars-1.0.0.js', type='text/javascript')
|
||||
script(src='/bower_components/swagger-ui/dist/lib/underscore-min.js', type='text/javascript')
|
||||
script(src='/bower_components/swagger-ui/dist/lib/backbone-min.js', type='text/javascript')
|
||||
script(src='/bower_components/swagger-ui/dist/lib/swagger.js', type='text/javascript')
|
||||
script(src='/bower_components/swagger-ui/dist/swagger-ui.js', type='text/javascript')
|
||||
script(src='/bower_components/swagger-ui/dist/lib/highlight.7.3.pack.js', type='text/javascript')
|
||||
script(type='text/javascript')
|
||||
$(function () {
|
||||
window.swaggerUi = new SwaggerUi({
|
||||
url: "/api-docs",
|
||||
dom_id: "swagger-ui-container",
|
||||
supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
|
||||
onComplete: function(swaggerApi, swaggerUi){
|
||||
if(console) {
|
||||
console.log("Loaded SwaggerUI")
|
||||
}
|
||||
$('pre code').each(function(i, e) {hljs.highlightBlock(e)});
|
||||
},
|
||||
onFailure: function(data) {
|
||||
if(console) {
|
||||
console.log("Unable to Load SwaggerUI");
|
||||
console.log(data);
|
||||
}
|
||||
},
|
||||
docExpansion: "none"
|
||||
});
|
||||
|
||||
$('#input_apiKey').change(function() {
|
||||
var key = $('#input_apiKey')[0].value;
|
||||
console.log("key: " + key);
|
||||
if(key && key.trim() != "") {
|
||||
console.log("added key " + key);
|
||||
window.authorizations.add("key", new ApiKeyAuthorization("api_key", key, "query"));
|
||||
}
|
||||
})
|
||||
window.swaggerUi.load();
|
||||
});
|
||||
body
|
||||
#header
|
||||
.swagger-ui-wrap
|
||||
a#logo(href='http://swagger.wordnik.com') swagger
|
||||
//-form#api_selector
|
||||
//-
|
||||
<div class='input icon-btn'>
|
||||
<img id="show-pet-store-icon" src="images/pet_store_api.png" title="Show Swagger Petstore Example Apis">
|
||||
</div>
|
||||
<div class='input icon-btn'>
|
||||
<img id="show-wordnik-dev-icon" src="images/wordnik_api.png" title="Show Wordnik Developer Apis">
|
||||
</div>
|
||||
.input
|
||||
input#input_baseUrl(placeholder='http://example.com/api', name='baseUrl', type='text')
|
||||
.input
|
||||
input#input_apiKey(placeholder='api_key', name='apiKey', type='text')
|
||||
.input
|
||||
a#explore(href='#') Explore
|
||||
#message-bar.swagger-ui-wrap
|
||||
#swagger-ui-container.swagger-ui-wrap
|
||||
Loading…
Reference in a new issue