Browse Source

serve a swagger file for each exposed api

master
Nicolas Massé 8 years ago
parent
commit
d7d7b6a320
  1. 61
      server.js
  2. 187
      swagger.json
  3. 119
      swagger.yaml
  4. 29
      test/specs.js

61
server.js

@ -4,6 +4,8 @@ var _ = require("underscore");
var bodyParser = require('body-parser'); var bodyParser = require('body-parser');
var util = require('util'); var util = require('util');
var sample_swagger = require('./swagger.json');
// ExpressJS Setup // ExpressJS Setup
var app = express(); var app = express();
var router = express.Router(); var router = express.Router();
@ -18,8 +20,8 @@ var config = {
}, },
"config": { "config": {
"fields": { "fields": {
"name": { "required": true }, "name": { "required": true, "type": "string" },
"price": { "required": true } "price": { "required": true, "type": "integer" }
} }
} }
} }
@ -27,7 +29,9 @@ var config = {
var admin_endpoints = [ var admin_endpoints = [
{ "url": "/debug/state", "verbs": [ "GET" ] }, { "url": "/debug/state", "verbs": [ "GET" ] },
{ "url": "/config/", "verbs": [ "GET" ] } { "url": "/config/", "verbs": [ "GET" ] },
{ "url": "/config/{id}", "verbs": [ "GET" ] },
{ "url": "/config/{id}/swagger", "verbs": [ "GET" ] }
]; ];
// Log every request // Log every request
@ -54,6 +58,57 @@ router.get("/config",function(req,res){
); );
}); });
router.get("/config/:object/",function(req,res){
var resource = req.params.object;
if (! (resource in config)) {
return error(res, 400, util.format("There is no resource '%s', try one of those resources : %s", resource, _.keys(config).join(", ")));
}
success(res, 200, config[resource].config);
});
router.get("/config/:object/swagger",function(req,res){
var resource = req.params.object;
if (! (resource in config)) {
return error(res, 400, util.format("There is no resource '%s', try one of those resources : %s", resource, _.keys(config).join(", ")));
}
// Search and replace all occurences of __THINGS__
var swagger = recursivedo(sample_swagger, (str) => {
return str.replace("__THINGS__", resource);
});
_.each(config[resource].config.fields, (params, id) => {
swagger.definitions.Thing.properties[id] = params;
swagger.definitions.PersistedThing.properties[id] = params;
});
success(res, 200, swagger);
});
// Recursively traverse a structure to do something
function recursivedo(input, fn) {
if (typeof input == "string") {
return fn(input);
} else if (input instanceof Array) {
var ret = [];
for (var i = 0; i < input.length; i++) {
ret.push(recursivedo(input[i], fn));
}
return ret;
} else if (input instanceof Object) {
var ret = {};
for (var property in input) {
if (input.hasOwnProperty(property)) {
ret[fn(property)] = recursivedo(input[property], fn);
}
}
return ret;
} else {
return input;
}
}
// Any GET on / ends up with a nice documentation as JSON // Any GET on / ends up with a nice documentation as JSON
router.get("/",function(req,res){ router.get("/",function(req,res){
var response = { var response = {

187
swagger.json

@ -1,192 +1,129 @@
{ {
"swagger": "2.0", "swagger": "2.0",
"info": { "info": {
"version": "1.0.0", "title": "Mockup (__THINGS__)",
"title": "Mockup API", "description": "A simple API Mock that manages __THINGS__",
"description": "A simple API that exposes CRUD operations",
"contact": { "contact": {
"name": "Nicolas Masse" "name": "Nicolas Massé",
"url": "https://github.com/nmasse-itix/API-Mockup"
}, },
"license": { "license": {
"name": "MIT" "name": "MIT License",
} "url": "https://opensource.org/licenses/MIT"
},
"version": "1.0.0"
}, },
"host": "TODO",
"basePath": "/",
"schemes": [
"https"
],
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"paths": { "paths": {
"/things": { "/__THINGS__/": {
"get": { "get": {
"description": "Returns all available things", "summary": "Get all __THINGS__",
"produces": [ "description": "Get all the __THINGS__.",
"application/json"
],
"responses": { "responses": {
"200": { "200": {
"description": "A List of Things", "description": "Success",
"schema": { "schema": {
"$ref": "#/definitions/ThingsList" "type": "array",
"items": {
"type": "#/definitions/PersistedThing"
}
}
} }
} }
}, },
"post": {
"summary": "Create a __THINGS__",
"parameters": [ "parameters": [
{ {
"$ref": "#/parameters/user-key" "name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/Thing"
}
} }
]
},
"post": {
"description": "Create a thing",
"consumes": [
"application/json"
],
"produces": [
"application/json"
], ],
"responses": { "responses": {
"200": { "201": {
"description": "The created Thing", "description": "Success",
"schema": { "schema": {
"$ref": "#/definitions/Thing" "$ref": "#/definitions/PersistedThing"
} }
} }
},
"parameters": [
{
"$ref": "#/parameters/user-key"
},
{
"$ref": "#/parameters/body"
} }
]
} }
}, },
"/things/{id}": { "/__THINGS__/{id}": {
"get": { "get": {
"description": "Returns all available things", "summary": "Get a __THINGS__ by id",
"produces": [
"application/json"
],
"responses": { "responses": {
"200": { "200": {
"description": "A List of Things", "description": "Success",
"schema": { "schema": {
"$ref": "#/definitions/ThingsList" "$ref": "#/definitions/PersistedThing"
}
} }
} }
}, },
"put": {
"summary": "Update a __THINGS__",
"parameters": [ "parameters": [
{ {
"$ref": "#/parameters/user-key" "name": "body",
}, "in": "body",
{ "schema": {
"$ref": "#/parameters/id" "$ref": "#/definitions/PersistedThing"
}
} }
]
},
"put": {
"description": "Update a Thing",
"produces": [
"application/json"
], ],
"responses": { "responses": {
"200": { "200": {
"description": "The updated Thing", "description": "Success",
"schema": { "schema": {
"$ref": "#/definitions/Thing" "$ref": "#/definitions/PersistedThing"
} }
} }
},
"parameters": [
{
"$ref": "#/parameters/user-key"
},
{
"$ref": "#/parameters/id"
},
{
"$ref": "#/parameters/body"
} }
]
}, },
"delete": { "delete": {
"description": "Delete a Thing", "summary": "Delete a __THINGS__",
"produces": [
"application/json"
],
"responses": { "responses": {
"200": { "200": {
"description": "The delete Thing", "description": "Success",
"schema": { "schema": {
"$ref": "#/definitions/Thing" "$ref": "#/definitions/PersistedThing"
}
} }
} }
}, },
"parameters": [ "parameters": [
{ {
"$ref": "#/parameters/user-key" "name": "id",
}, "in": "path",
{ "description": "The __THINGS__ id",
"$ref": "#/parameters/id" "required": true,
"type": "integer"
} }
] ]
} }
}
}, },
"definitions": { "definitions": {
"ThingsList": {
"type": "array",
"items": {
"$ref": "#/definitions/Thing"
}
},
"Thing": { "Thing": {
"title": "Definition of __THINGS__",
"description": "All __THINGS__ follow this definition",
"type": "object", "type": "object",
"properties": { "properties": { }
"name": {
"type": "string"
}, },
"PersistedThing": {
"title": "Definition of persisted __THINGS__",
"description": "All persisted __THINGS__ follow this definition",
"type": "object",
"properties": {
"id": { "id": {
"type": "number" "format": "int32",
}, "description": "The __THINGS__ id",
"price": { "type": "integer",
"type": "number" "required": true
} }
} }
} }
},
"parameters": {
"user-key": {
"name": "user-key",
"in": "header",
"required": true,
"description": "The 3scale API Key",
"type": "string",
"x-data-threescale-name": "user_keys"
},
"id": {
"name": "id",
"in": "path",
"required": true,
"description": "The things id",
"type": "number"
},
"body": {
"name": "body",
"in": "body",
"required": true,
"description": "The Thing to create/update",
"schema": {
"$ref": "#/definitions/Thing"
}
}
} }
} }

119
swagger.yaml

@ -1,119 +0,0 @@
---
swagger: "2.0"
info:
version: "1.0.0"
title: "Mockup API"
description: "A simple API that exposes CRUD operations"
contact:
name: "Nicolas Masse"
license:
name: "MIT"
host: "TODO"
basePath: "/"
schemes:
- "https"
consumes:
- "application/json"
produces:
- "application/json"
paths:
'/things':
get:
description: "Returns all available things"
produces:
- "application/json"
responses:
"200":
description: "A List of Things"
schema:
$ref: "#/definitions/ThingsList"
parameters:
- $ref: "#/parameters/user-key"
post:
description: "Create a thing"
consumes:
- "application/json"
produces:
- "application/json"
responses:
"200":
description: "The created Thing"
schema:
$ref: "#/definitions/Thing"
parameters:
- $ref: "#/parameters/user-key"
- $ref: "#/parameters/body"
'/things/{id}':
get:
description: "Returns all available things"
produces:
- "application/json"
responses:
"200":
description: "A List of Things"
schema:
$ref: "#/definitions/ThingsList"
parameters:
- $ref: "#/parameters/user-key"
- $ref: "#/parameters/id"
put:
description: "Update a Thing"
produces:
- "application/json"
responses:
"200":
description: "The updated Thing"
schema:
$ref: "#/definitions/Thing"
parameters:
- $ref: "#/parameters/user-key"
- $ref: "#/parameters/id"
- $ref: "#/parameters/body"
delete:
description: "Delete a Thing"
produces:
- "application/json"
responses:
"200":
description: "The delete Thing"
schema:
$ref: "#/definitions/Thing"
parameters:
- $ref: "#/parameters/user-key"
- $ref: "#/parameters/id"
definitions:
ThingsList:
type: "array"
items:
$ref: "#/definitions/Thing"
Thing:
type: "object"
properties:
name:
type: "string"
id:
type: "number"
price:
type: "number"
parameters:
user-key:
name: "user-key"
in: "header"
required: true
description: "The 3scale API Key"
type: "string"
x-data-threescale-name: "user_keys"
id:
name: "id"
in: "path"
required: true
description: "The things id"
type: "number"
body:
name: "body"
in: "body"
required: true
description: "The Thing to create/update"
schema:
$ref: "#/definitions/Thing"

29
test/specs.js

@ -35,6 +35,35 @@ describe('server.js tests', function () {
.end(done); .end(done);
}); });
it("Default config (by id)", (done) => {
request(server)
.get('/config/things/')
.expect('Content-Type', /^application[/]json/)
.expect(200)
.expect((res) => {
assert.ok(res.body instanceof Object, "body is Object");
assert.ok(res.body.fields instanceof Object, "body.things.fields has fields");
assert.ok('name' in res.body.fields, "body.things.fields has a field named 'name'");
assert.ok('price' in res.body.fields, "body.things.fields has a field named 'price'");
})
.end(done);
});
it("Get the swagger of the default config", (done) => {
request(server)
.get('/config/things/swagger')
.expect('Content-Type', /^application[/]json/)
.expect(200)
.expect((res) => {
assert.ok(res.body instanceof Object, "body is Object");
assert.equal(res.body.info.title, "Mockup (things)", "search-and-replace worked");
assert.equal(res.body.definitions.PersistedThing.properties.id.required, true, "PersistedThing have a mandatory id");
assert.equal(res.body.definitions.Thing.properties.name.required, true, "Thing have a mandatory name");
assert.equal(res.body.definitions.Thing.properties.price.required, true, "Thing have a mandatory price");
})
.end(done);
});
it("By default, the list of things is empty", (done) => { it("By default, the list of things is empty", (done) => {
request(server) request(server)
.get('/things/') .get('/things/')

Loading…
Cancel
Save