forked from loafle/openapi-generator-original
added server generator
This commit is contained in:
parent
a929fce3a0
commit
b4485b1196
@ -1,4 +1,4 @@
|
|||||||
package com.wordnik.client
|
package com.wordnik.petstore
|
||||||
|
|
||||||
import com.wordnik.swagger.core.util.JsonUtil
|
import com.wordnik.swagger.core.util.JsonUtil
|
||||||
import com.sun.jersey.api.client.Client
|
import com.sun.jersey.api.client.Client
|
||||||
|
48
samples/server-generator/node/NodeServerFromSpec.scala
Normal file
48
samples/server-generator/node/NodeServerFromSpec.scala
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2012 Wordnik, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import com.wordnik.swagger.codegen.BasicScalaGenerator
|
||||||
|
|
||||||
|
import com.wordnik.swagger.core._
|
||||||
|
|
||||||
|
import scala.collection.mutable.{ HashMap, ListBuffer }
|
||||||
|
|
||||||
|
object NodeServerGenerator extends BasicScalaGenerator {
|
||||||
|
def main(args: Array[String]) = generateClient(args)
|
||||||
|
|
||||||
|
override def templateDir = "samples/server-generator/node/templates"
|
||||||
|
|
||||||
|
val outputFolder = "samples/server-generator/node/output"
|
||||||
|
|
||||||
|
// where to write generated code
|
||||||
|
override def destinationDir = outputFolder + "/App"
|
||||||
|
|
||||||
|
// template used for apis
|
||||||
|
apiTemplateFiles ++= Map("api.mustache" -> ".js")
|
||||||
|
|
||||||
|
modelTemplateFiles.clear
|
||||||
|
|
||||||
|
override def apiPackage = Some("apis")
|
||||||
|
|
||||||
|
// supporting classes
|
||||||
|
override def supportingFiles = List(
|
||||||
|
("package.json", outputFolder, "package.json"),
|
||||||
|
("main.mustache", destinationDir, "main.js"),
|
||||||
|
("models.mustache", destinationDir, "models.js"),
|
||||||
|
("Common/node/paramTypes.js", destinationDir + "/Common/node", "paramTypes.js"),
|
||||||
|
("Common/node/randomizer.js", destinationDir + "/Common/node", "randomizer.js"),
|
||||||
|
("Common/node/swagger.js", destinationDir + "/Common/node", "swagger.js"))
|
||||||
|
}
|
56
samples/server-generator/node/templates/Common/node/paramTypes.js
Executable file
56
samples/server-generator/node/templates/Common/node/paramTypes.js
Executable file
@ -0,0 +1,56 @@
|
|||||||
|
function createEnum(input) {
|
||||||
|
if (input && input.toString().indexOf(",") > 0) {
|
||||||
|
var output = [];
|
||||||
|
var array = input.split(",");
|
||||||
|
array.forEach(function(item) {
|
||||||
|
output.push(item);
|
||||||
|
})
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.query = exports.q = function(name, description, dataType, required, allowMultiple, allowableValues, defaultValue) {
|
||||||
|
return {
|
||||||
|
"name" : name,
|
||||||
|
"description" : description,
|
||||||
|
"dataType" : dataType,
|
||||||
|
"required" : required,
|
||||||
|
"allowMultiple" : allowMultiple,
|
||||||
|
"allowableValues" : createEnum(allowableValues),
|
||||||
|
"defaultValue" : defaultValue,
|
||||||
|
"paramType" : "query"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.path = function(name, description, dataType, allowableValues) {
|
||||||
|
return {
|
||||||
|
"name" : name,
|
||||||
|
"description" : description,
|
||||||
|
"dataType" : dataType,
|
||||||
|
"required" : true,
|
||||||
|
"allowMultiple" : false,
|
||||||
|
"allowableValues" : createEnum(allowableValues),
|
||||||
|
"paramType" : "path"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.post = function(dataType, description) {
|
||||||
|
return {
|
||||||
|
"description" : description,
|
||||||
|
"dataType" : dataType,
|
||||||
|
"required" : true,
|
||||||
|
"paramType" : "body"
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.header = function(name, description, dataType, required) {
|
||||||
|
return {
|
||||||
|
"name" : name,
|
||||||
|
"description" : description,
|
||||||
|
"dataType" : dataType,
|
||||||
|
"required" : true,
|
||||||
|
"allowMultiple" : false,
|
||||||
|
"allowableValues" : createEnum(allowableValues),
|
||||||
|
"paramType" : "header"
|
||||||
|
};
|
||||||
|
};
|
68
samples/server-generator/node/templates/Common/node/randomizer.js
Executable file
68
samples/server-generator/node/templates/Common/node/randomizer.js
Executable file
@ -0,0 +1,68 @@
|
|||||||
|
var Randomizer = {
|
||||||
|
'intBetween': function(a, b) {
|
||||||
|
return Math.floor(Math.random()*(b-a+1)+a);
|
||||||
|
},
|
||||||
|
'string': function() {
|
||||||
|
var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
|
||||||
|
var string_length = 24;
|
||||||
|
var randomstring = '';
|
||||||
|
for (var i=0; i<string_length; i++) {
|
||||||
|
var rnum = Math.floor(Math.random() * chars.length);
|
||||||
|
randomstring += chars.substring(rnum,rnum+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return randomstring;
|
||||||
|
},
|
||||||
|
'int': function() {
|
||||||
|
return this.intBetween(0, 10000);
|
||||||
|
},
|
||||||
|
'long': function() {
|
||||||
|
return this.int();
|
||||||
|
},
|
||||||
|
'double': function() {
|
||||||
|
return Math.random()*1000000;
|
||||||
|
},
|
||||||
|
'boolean': function() {
|
||||||
|
return this.intBetween(1,2) == 1;
|
||||||
|
},
|
||||||
|
'__date': function() {
|
||||||
|
var m = this.intBetween(1, 12);
|
||||||
|
if (m < 10) { m = '0'+m; }
|
||||||
|
var d = this.intBetween(1, 28);
|
||||||
|
if (d < 10) { d = '0'+d; }
|
||||||
|
|
||||||
|
return this.intBetween(1800, 2015) + '-' + m + '-' + d;
|
||||||
|
},
|
||||||
|
'date': function() {
|
||||||
|
return this.__date() + 'T' + this.__time();
|
||||||
|
},
|
||||||
|
'__time': function() {
|
||||||
|
var h = this.intBetween(0, 23);
|
||||||
|
if (h < 10) { h = '0'+h; }
|
||||||
|
var m = this.intBetween(0, 59);
|
||||||
|
if (m < 10) { m = '0'+m; }
|
||||||
|
var s = this.intBetween(0, 59);
|
||||||
|
if (s < 10) { s = '0'+s; }
|
||||||
|
|
||||||
|
return h + ':' + m + ':' + s + '+01:00';
|
||||||
|
},
|
||||||
|
'array': function(type, length) {
|
||||||
|
var out = new Array();
|
||||||
|
if (!length) {
|
||||||
|
length = this.intBetween(1, 5); }
|
||||||
|
for (var i = 0; i < length; i++) {
|
||||||
|
out.push(this[type]()); }
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.intBetween = Randomizer.intBetween;
|
||||||
|
exports.string = Randomizer.string;
|
||||||
|
exports.int = Randomizer.int;
|
||||||
|
exports.long = Randomizer.long;
|
||||||
|
exports.double = Randomizer.double;
|
||||||
|
exports.boolean = Randomizer.boolean;
|
||||||
|
exports.array = Randomizer.array;
|
||||||
|
exports.__date = Randomizer.__date;
|
||||||
|
exports.date = Randomizer.date;
|
||||||
|
exports.__time = Randomizer.__time;
|
612
samples/server-generator/node/templates/Common/node/swagger.js
Executable file
612
samples/server-generator/node/templates/Common/node/swagger.js
Executable file
@ -0,0 +1,612 @@
|
|||||||
|
var resourcePath = "/resources.json";
|
||||||
|
var basePath = "/";
|
||||||
|
var swaggerVersion = "1.1";
|
||||||
|
var apiVersion = "0.0";
|
||||||
|
var resources = {};
|
||||||
|
var validators = [];
|
||||||
|
var appHandler = null;
|
||||||
|
var allowedMethods = ['get', 'post', 'put', 'delete'];
|
||||||
|
var allowedDataTypes = ['string', 'int', 'long', 'double', 'boolean', 'date', 'array'];
|
||||||
|
var Randomizer = require(__dirname + '/randomizer.js');
|
||||||
|
var params = require(__dirname + '/paramTypes.js');
|
||||||
|
var allModels = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize Randomizer Caching
|
||||||
|
*/
|
||||||
|
var RandomStorage = {};
|
||||||
|
for (var i = 0; i < allowedDataTypes.length; i++) {
|
||||||
|
RandomStorage[allowedDataTypes[i]] = {}; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sets the base path, api version
|
||||||
|
*
|
||||||
|
* @param app
|
||||||
|
* @param bp
|
||||||
|
* @param av
|
||||||
|
*/
|
||||||
|
function configure(bp, av) {
|
||||||
|
basePath = bp;
|
||||||
|
apiVersion = av;
|
||||||
|
setResourceListingPaths(appHandler);
|
||||||
|
appHandler.get(resourcePath, resourceListing);
|
||||||
|
// update resources if already configured
|
||||||
|
for(key in resources) {
|
||||||
|
var r = resources[key];
|
||||||
|
r.apiVersion = av;
|
||||||
|
r.basePath = bp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates declarations for each resource
|
||||||
|
*
|
||||||
|
* @param app
|
||||||
|
*/
|
||||||
|
function setResourceListingPaths(app) {
|
||||||
|
for (var key in resources) {
|
||||||
|
app.get("/" + key.replace("\.\{format\}", ".json"), function(req, res) {
|
||||||
|
var r = resources[req.url.substr(1).split('?')[0].replace('.json', '.{format}')];
|
||||||
|
if (!r) {
|
||||||
|
return stopWithError(res, {'description': 'internal error', 'code': 500}); }
|
||||||
|
else {
|
||||||
|
res.header('Access-Control-Allow-Origin', "*");
|
||||||
|
res.header("Content-Type", "application/json; charset=utf-8");
|
||||||
|
var key = req.url.substr(1).replace('.json', '.{format}').split('?')[0];
|
||||||
|
var data = applyFilter(req, res, resources[key]);
|
||||||
|
data.basePath = basePath;
|
||||||
|
if (data.code) {
|
||||||
|
res.send(data, data.code); }
|
||||||
|
else {
|
||||||
|
res.header('Access-Control-Allow-Origin', "*");
|
||||||
|
res.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
|
||||||
|
res.header("Access-Control-Allow-Headers", "Content-Type");
|
||||||
|
res.header("Content-Type", "application/json; charset=utf-8");
|
||||||
|
res.send(JSON.stringify(applyFilter(req, res, r)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* generate random date for type
|
||||||
|
*
|
||||||
|
* @param type type of data (must be defined in allowedDataTypes)
|
||||||
|
* @param withRandom fill with random data
|
||||||
|
* @return value
|
||||||
|
*/
|
||||||
|
function randomDataByType(type, withRandom, subType) {
|
||||||
|
type = type.toLowerCase();
|
||||||
|
if (allowedDataTypes.indexOf(type)<0) {
|
||||||
|
return null; }
|
||||||
|
return Randomizer[type](subType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache for type and identifier
|
||||||
|
* @param type
|
||||||
|
* @param id
|
||||||
|
* @param key
|
||||||
|
* @return value
|
||||||
|
*/
|
||||||
|
function getCache(type, id, key) {
|
||||||
|
if (id && id != -1 && RandomStorage[type] && RandomStorage[type][key+id]) {
|
||||||
|
return RandomStorage[type][key + id]; }
|
||||||
|
else {
|
||||||
|
return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set cache for type and identifier
|
||||||
|
* @param type
|
||||||
|
* @param id
|
||||||
|
* @param key
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
function setCache(curType, id, key, value) {
|
||||||
|
if (id && id != -1 && RandomStorage[curType]) {
|
||||||
|
RandomStorage[curType][key + id] = value; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* try to generate object from model defintion
|
||||||
|
* @param model
|
||||||
|
* @param withData fill model with data
|
||||||
|
* @param withRandom generate random values
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
function containerByModel(model, withData, withRandom) {
|
||||||
|
var item = {};
|
||||||
|
for (key in model.properties) {
|
||||||
|
var curType = model.properties[key].type.toLowerCase();
|
||||||
|
|
||||||
|
var value = '';
|
||||||
|
if (withData && withData[key]) {
|
||||||
|
value = withData[key]; }
|
||||||
|
if (value == '' && withRandom) {
|
||||||
|
var cache = getCache(curType, withRandom, key);
|
||||||
|
if (cache) {
|
||||||
|
value = cache; }
|
||||||
|
else {
|
||||||
|
if (model.properties[key].enum) {
|
||||||
|
value = model.properties[key].enum[Randomizer.intBetween(0, model.properties[key].enum.length-1)]; }
|
||||||
|
else {
|
||||||
|
var subType = false;
|
||||||
|
if (model.properties[key].items && model.properties[key].items.type) {
|
||||||
|
subType = model.properties[key].items.type; }
|
||||||
|
var curKey = key;
|
||||||
|
value = randomDataByType(curType, withRandom, subType);
|
||||||
|
var key = curKey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setCache(curType, withRandom, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value == '' && curType == 'Array') {
|
||||||
|
value = []; }
|
||||||
|
|
||||||
|
item[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* filters resource listing by access
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @param res
|
||||||
|
* @param r
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
function applyFilter(req, res, r) {
|
||||||
|
var route = req.route;
|
||||||
|
var excludedPaths = [];
|
||||||
|
|
||||||
|
if (!r || !r.apis) {
|
||||||
|
return stopWithError(res, {'description': 'internal error', 'code': 500}); }
|
||||||
|
|
||||||
|
for (var key in r.apis) {
|
||||||
|
var api = r.apis[key];
|
||||||
|
for (var opKey in api.operations) {
|
||||||
|
var op = api.operations[opKey];
|
||||||
|
var path = api.path.replace(/{.*\}/, "*");
|
||||||
|
if (!canAccessResource(req, route + path, op.httpMethod)) {
|
||||||
|
excludedPaths.push(op.httpMethod + ":" + api.path); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone attributes if any
|
||||||
|
var output = shallowClone(r);
|
||||||
|
|
||||||
|
// clone models
|
||||||
|
var requiredModels = [];
|
||||||
|
|
||||||
|
// clone methods that have access
|
||||||
|
output.apis = new Array();
|
||||||
|
var apis = JSON.parse(JSON.stringify(r.apis));
|
||||||
|
for (var i in apis) {
|
||||||
|
var api = apis[i];
|
||||||
|
var clonedApi = shallowClone(api);
|
||||||
|
|
||||||
|
clonedApi.operations = new Array();
|
||||||
|
var shouldAdd = true;
|
||||||
|
for (var o in api.operations){
|
||||||
|
var operation = api.operations[o];
|
||||||
|
if (excludedPaths.indexOf(operation.httpMethod + ":" + api.path)>=0) {
|
||||||
|
break; }
|
||||||
|
else {
|
||||||
|
clonedApi.operations.push(JSON.parse(JSON.stringify(operation)));
|
||||||
|
addModelsFromResponse(operation, requiredModels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (clonedApi.operations.length > 0) {
|
||||||
|
// only add cloned api if there are operations
|
||||||
|
output.apis.push(clonedApi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add models to output
|
||||||
|
output.models = {};
|
||||||
|
for (var i in requiredModels){
|
||||||
|
var modelName = requiredModels[i];
|
||||||
|
var model = allModels.models[modelName];
|
||||||
|
if(model){
|
||||||
|
output.models[requiredModels[i]] = model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// look in object graph
|
||||||
|
for (key in output.models) {
|
||||||
|
var model = output.models[key];
|
||||||
|
if (model && model.properties) {
|
||||||
|
for (var key in model.properties) {
|
||||||
|
var t = model.properties[key].type;
|
||||||
|
|
||||||
|
switch (t){
|
||||||
|
case "Array":
|
||||||
|
if (model.properties[key].items) {
|
||||||
|
var ref = model.properties[key].items.$ref;
|
||||||
|
if (ref && requiredModels.indexOf(ref) < 0) {
|
||||||
|
requiredModels.push(ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "string":
|
||||||
|
case "long":
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (requiredModels.indexOf(t) < 0) {
|
||||||
|
requiredModels.push(t);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var i in requiredModels){
|
||||||
|
var modelName = requiredModels[i];
|
||||||
|
if(!output[modelName]) {
|
||||||
|
var model = allModels.models[modelName];
|
||||||
|
if(model){
|
||||||
|
output.models[requiredModels[i]] = model;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add model to list and parse List[model] elements
|
||||||
|
* @param operation
|
||||||
|
* @param models
|
||||||
|
*/
|
||||||
|
function addModelsFromResponse(operation, models){
|
||||||
|
var responseModel = operation.responseClass;
|
||||||
|
if (responseModel) {
|
||||||
|
responseModel = responseModel.replace(/^List\[/,"").replace(/\]/,"");
|
||||||
|
if (models.indexOf(responseModel) < 0) {
|
||||||
|
models.push(responseModel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function shallowClone(obj) {
|
||||||
|
var cloned = new Object();
|
||||||
|
for (var i in obj) {
|
||||||
|
if (typeof (obj[i]) != "object") {
|
||||||
|
cloned[i] = obj[i]; }
|
||||||
|
}
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* function for filtering a resource. override this with your own implementation
|
||||||
|
*
|
||||||
|
* @param req
|
||||||
|
* @param path
|
||||||
|
* @param httpMethod
|
||||||
|
* @returns {Boolean}
|
||||||
|
*/
|
||||||
|
function canAccessResource(req, path, httpMethod) {
|
||||||
|
for (var i in validators) {
|
||||||
|
if (!validators[i](req,path,httpMethod)) {
|
||||||
|
return false; }
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns the json representation of a resource
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @param response
|
||||||
|
*/
|
||||||
|
function resourceListing(request, response) {
|
||||||
|
var r = {"apiVersion" : apiVersion, "swaggerVersion": swaggerVersion, "basePath": basePath, "apis": []};
|
||||||
|
|
||||||
|
for (var key in resources) {
|
||||||
|
r.apis.push({"path": "/" + key, "description": "none"});
|
||||||
|
}
|
||||||
|
|
||||||
|
response.header('Access-Control-Allow-Origin', "*");
|
||||||
|
response.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
|
||||||
|
response.header("Access-Control-Allow-Headers", "Content-Type");
|
||||||
|
response.header("Content-Type", "application/json; charset=utf-8");
|
||||||
|
|
||||||
|
response.write(JSON.stringify(r));
|
||||||
|
response.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* adds a method to the api along with a spec. If the spec fails to validate, it won't be added
|
||||||
|
*
|
||||||
|
* @param app
|
||||||
|
* @param callback
|
||||||
|
* @param spec
|
||||||
|
*/
|
||||||
|
function addMethod(app, callback, spec) {
|
||||||
|
var rootPath = spec.path.split("/")[1];
|
||||||
|
var root = resources[rootPath];
|
||||||
|
|
||||||
|
if (root && root.apis) {
|
||||||
|
for (var key in root.apis) {
|
||||||
|
var api = root.apis[key];
|
||||||
|
if (api && api.path == spec.path && api.method == spec.method) {
|
||||||
|
// found matching path and method, add & return
|
||||||
|
appendToApi(root, api, spec);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var api = {"path" : spec.path};
|
||||||
|
if (!resources[rootPath]) {
|
||||||
|
if (!root) {
|
||||||
|
var resourcePath = "/" + rootPath.replace("\.\{format\}", "");
|
||||||
|
root = {
|
||||||
|
"apiVersion" : apiVersion, "swaggerVersion": swaggerVersion, "basePath": basePath, "resourcePath": resourcePath, "apis": [], "models" : []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
resources[rootPath] = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
root.apis.push(api);
|
||||||
|
appendToApi(root, api, spec);
|
||||||
|
|
||||||
|
// TODO: add some XML support
|
||||||
|
// convert .{format} to .json, make path params happy
|
||||||
|
var fullPath = spec.path.replace("\.\{format\}", ".json").replace(/\/{/g, "/:").replace(/\}/g,"");
|
||||||
|
var currentMethod = spec.method.toLowerCase();
|
||||||
|
if (allowedMethods.indexOf(currentMethod)>-1) {
|
||||||
|
app[currentMethod](fullPath, function(req,res) {
|
||||||
|
res.header('Access-Control-Allow-Origin', "*");
|
||||||
|
res.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
|
||||||
|
res.header("Access-Control-Allow-Headers", "Content-Type");
|
||||||
|
|
||||||
|
res.header("Content-Type", "application/json; charset=utf-8");
|
||||||
|
|
||||||
|
if (!canAccessResource(req, req.url.substr(1).split('?')[0].replace('.json', '.*'), req.method)) {
|
||||||
|
res.send(JSON.stringify({"description":"forbidden", "code":403}), 403);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
callback(req,res); }
|
||||||
|
catch (ex) {
|
||||||
|
if (ex.code && ex.description) {
|
||||||
|
res.send(JSON.stringify(ex), ex.code); }
|
||||||
|
else {
|
||||||
|
console.error(spec.method + " failed for path '" + require('url').parse(req.url).href + "': " + ex);
|
||||||
|
res.send(JSON.stringify({"description":"unknown error","code":500}), 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log('unable to add ' + currentMethod.toUpperCase() + ' handler');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set expressjs app handler
|
||||||
|
* @param app
|
||||||
|
*/
|
||||||
|
function setAppHandler(app) {
|
||||||
|
appHandler = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add swagger handlers to express
|
||||||
|
* @param type http method
|
||||||
|
* @param handlers list of handlers to be added
|
||||||
|
*/
|
||||||
|
function addHandlers(type, handlers) {
|
||||||
|
for (var i = 0; i < handlers.length; i++) {
|
||||||
|
var handler = handlers[i];
|
||||||
|
handler.spec.method = type;
|
||||||
|
addMethod(appHandler, handler.action, handler.spec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover swagger handler from resource
|
||||||
|
*/
|
||||||
|
function discover(resource) {
|
||||||
|
for (var key in resource) {
|
||||||
|
if (resource[key].spec && resource[key].spec.method && allowedMethods.indexOf(resource[key].spec.method.toLowerCase())>-1) {
|
||||||
|
addMethod(appHandler, resource[key].action, resource[key].spec); }
|
||||||
|
else {
|
||||||
|
console.log('auto discover failed for: ' + key); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover swagger handler from resource file path
|
||||||
|
*/
|
||||||
|
function discoverFile(file) {
|
||||||
|
return discover(require(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addGet() {
|
||||||
|
addHandlers('GET', arguments);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPost() {
|
||||||
|
addHandlers('POST', arguments);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addDelete() {
|
||||||
|
addHandlers('DELETE', arguments);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addPut() {
|
||||||
|
addHandlers('PUT', arguments);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function addModels(models) {
|
||||||
|
allModels = models;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
function wrap(callback, req, resp){
|
||||||
|
callback(req,resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendToApi(rootResource, api, spec) {
|
||||||
|
if (!api.description) {
|
||||||
|
api.description = spec.description;
|
||||||
|
}
|
||||||
|
var validationErrors = [];
|
||||||
|
|
||||||
|
if(!spec.nickname || spec.nickname.indexOf(" ")>=0){
|
||||||
|
// nicknames don't allow spaces
|
||||||
|
validationErrors.push({"path": api.path, "error": "invalid nickname '" + spec.nickname + "'"});
|
||||||
|
}
|
||||||
|
// validate params
|
||||||
|
for ( var paramKey in spec.params) {
|
||||||
|
var param = spec.params[paramKey];
|
||||||
|
if(param.allowableValues) {
|
||||||
|
var avs = param.allowableValues.toString();
|
||||||
|
var type = avs.split('[')[0];
|
||||||
|
if(type == 'LIST'){
|
||||||
|
var values = avs.match(/\[(.*)\]/g).toString().replace('\[','').replace('\]', '').split(',');
|
||||||
|
param.allowableValues = {valueType: type, values: values};
|
||||||
|
}
|
||||||
|
else if (type == 'RANGE') {
|
||||||
|
var values = avs.match(/\[(.*)\]/g).toString().replace('\[','').replace('\]', '').split(',');
|
||||||
|
param.allowableValues = {valueType: type, min: values[0], max: values[1]};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (param.paramType) {
|
||||||
|
case "path":
|
||||||
|
if (api.path.indexOf("{" + param.name + "}") < 0) {
|
||||||
|
validationErrors.push({"path": api.path, "name": param.name, "error": "invalid path"}); }
|
||||||
|
break;
|
||||||
|
case "query":
|
||||||
|
break;
|
||||||
|
case "body":
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
validationErrors.push({"path": api.path, "name": param.name, "error": "invalid param type " + param.paramType});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validationErrors.length > 0) {
|
||||||
|
console.log(validationErrors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!api.operations) {
|
||||||
|
api.operations = []; }
|
||||||
|
|
||||||
|
// TODO: replace if existing HTTP operation in same api path
|
||||||
|
var op = {
|
||||||
|
"parameters" : spec.params,
|
||||||
|
"httpMethod" : spec.method,
|
||||||
|
"notes" : spec.notes,
|
||||||
|
"errorResponses" : spec.errorResponses,
|
||||||
|
"nickname" : spec.nickname,
|
||||||
|
"summary" : spec.summary
|
||||||
|
};
|
||||||
|
|
||||||
|
if (spec.responseClass) {
|
||||||
|
op.responseClass = spec.responseClass;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
op.responseClass = "void";
|
||||||
|
}
|
||||||
|
api.operations.push(op);
|
||||||
|
|
||||||
|
if (!rootResource.models) {
|
||||||
|
rootResource.models = {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addValidator(v) {
|
||||||
|
validators.push(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create Error JSON by code and text
|
||||||
|
* @param int code
|
||||||
|
* @param string description
|
||||||
|
* @return obj
|
||||||
|
*/
|
||||||
|
function error(code, description) {
|
||||||
|
return {"code" : code, "description" : description};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop express ressource with error code
|
||||||
|
* @param obj res expresso response
|
||||||
|
* @param obj error error object with code and description
|
||||||
|
*/
|
||||||
|
function stopWithError(res, error) {
|
||||||
|
res.header('Access-Control-Allow-Origin', "*");
|
||||||
|
res.header("Content-Type", "application/json; charset=utf-8");
|
||||||
|
|
||||||
|
if (error && error.description && error.code) {
|
||||||
|
res.send(JSON.stringify(error), error.code); }
|
||||||
|
else {
|
||||||
|
res.send(JSON.stringify({'description': 'internal error', 'code': 500}), 500); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export most needed error types for easier handling
|
||||||
|
*/
|
||||||
|
exports.errors = {
|
||||||
|
'notFound': function(field, res) {
|
||||||
|
if (!res) {
|
||||||
|
return {"code": 404, "description": field + ' not found'}; }
|
||||||
|
else {
|
||||||
|
res.send({"code": 404, "description": field + ' not found'}, 404); }
|
||||||
|
},
|
||||||
|
'invalid': function(field, res) {
|
||||||
|
if (!res) {
|
||||||
|
return {"code": 400, "description": 'invalid ' + field}; }
|
||||||
|
else {
|
||||||
|
res.send({"code": 400, "description": 'invalid ' + field}, 404); }
|
||||||
|
},
|
||||||
|
'forbidden': function(res) {
|
||||||
|
if (!res) {
|
||||||
|
return {"code": 403, "description": 'forbidden' }; }
|
||||||
|
else {
|
||||||
|
res.send({"code": 403, "description": 'forbidden'}, 403); }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.params = params;
|
||||||
|
exports.queryParam = exports.params.query;
|
||||||
|
exports.pathParam = exports.params.path;
|
||||||
|
exports.postParam = exports.params.post;
|
||||||
|
exports.getModels = allModels;
|
||||||
|
|
||||||
|
exports.error = error;
|
||||||
|
exports.stopWithError = stopWithError;
|
||||||
|
exports.stop = stopWithError;
|
||||||
|
exports.addValidator = addValidator;
|
||||||
|
exports.configure = configure;
|
||||||
|
exports.canAccessResource = canAccessResource;
|
||||||
|
exports.resourcePath = resourcePath;
|
||||||
|
exports.resourceListing = resourceListing;
|
||||||
|
exports.addGet = addGet;
|
||||||
|
exports.addPost = addPost;
|
||||||
|
exports.addPut = addPut;
|
||||||
|
exports.addDelete = addDelete;
|
||||||
|
exports.addGET = addGet;
|
||||||
|
exports.addPOST = addPost;
|
||||||
|
exports.addPUT = addPut;
|
||||||
|
exports.addDELETE = addDelete;
|
||||||
|
exports.addModels = addModels;
|
||||||
|
exports.setAppHandler = setAppHandler;
|
||||||
|
exports.discover = discover;
|
||||||
|
exports.discoverFile = discoverFile;
|
||||||
|
exports.containerByModel = containerByModel;
|
||||||
|
exports.Randomizer = Randomizer;
|
52
samples/server-generator/node/templates/api.mustache
Normal file
52
samples/server-generator/node/templates/api.mustache
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
var sw = require("../Common/node/swagger.js");
|
||||||
|
var param = require("../Common/node/paramTypes.js");
|
||||||
|
var url = require("url");
|
||||||
|
var swe = sw.errors;
|
||||||
|
|
||||||
|
/* add model includes */
|
||||||
|
|
||||||
|
function writeResponse (response, data) {
|
||||||
|
response.header('Access-Control-Allow-Origin', "*");
|
||||||
|
response.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
|
||||||
|
response.header("Access-Control-Allow-Headers", "Content-Type");
|
||||||
|
response.header("Content-Type", "application/json; charset=utf-8");
|
||||||
|
response.send(JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.models = models = require("../models.js");
|
||||||
|
|
||||||
|
{{#operations}}
|
||||||
|
{{#operation}}
|
||||||
|
exports.{{nickname}} = {
|
||||||
|
'spec': {
|
||||||
|
"description" : "Operations about pets",
|
||||||
|
"path" : "{{path}}",
|
||||||
|
"notes" : "{{{notes}}}",
|
||||||
|
"summary" : "{{{summary}}}",
|
||||||
|
"method": "{{httpMethod}}",
|
||||||
|
"params" : [{{#queryParams}}
|
||||||
|
param.query("{{paramName}}", "{{description}}", "{{dataType}}", {{required}}, {{allowMultiple}}, "{{{allowableValues}}}"{{#defaultValue}}, {{{defaultValue}}}{{/defaultValue}}){{#hasMore}},{{/hasMore}}
|
||||||
|
{{/queryParams}}].concat([{{#pathParams}}
|
||||||
|
param.path("{{paramName}}", "{{description}}"){{#hasMore}},{{/hasMore}}
|
||||||
|
{{/pathParams}}]).concat([{{#headerParams}}
|
||||||
|
param.header("{{paramName}}", "{{description}}"){{#hasMore}},{{/hasMore}}
|
||||||
|
{{/headerParams}}]).concat([{{#bodyParams}}
|
||||||
|
param.post("{{dataType}}", "{{description}}", {{required}})
|
||||||
|
{{/bodyParams}}]),
|
||||||
|
"responseClass" : "{{responseClass}}",
|
||||||
|
"errorResponses" : [swe.invalid('id'), swe.notFound('pet')],
|
||||||
|
"nickname" : "{{nickname}}"
|
||||||
|
},
|
||||||
|
'action': function (req,res) {
|
||||||
|
{{#requiredParamCount}}
|
||||||
|
{{#requiredParams}}
|
||||||
|
if (!req.params.{{baseName}}) {
|
||||||
|
throw swe.invalid('{{baseName}}');
|
||||||
|
}
|
||||||
|
{{/requiredParams}}
|
||||||
|
{{/requiredParamCount}}
|
||||||
|
writeResponse(res, {message: "how about implementing {{nickname}} as a {{httpMethod}} method?"});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
{{/operation}}
|
||||||
|
{{/operations}}
|
27
samples/server-generator/node/templates/main.mustache
Normal file
27
samples/server-generator/node/templates/main.mustache
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
var express = require("express")
|
||||||
|
, url = require("url")
|
||||||
|
, swagger = require("./Common/node/swagger.js")
|
||||||
|
, db = false
|
||||||
|
|
||||||
|
var app = express.createServer(
|
||||||
|
function(req, res, next) { if (req.db === undefined) { req.db = db; } next(); });
|
||||||
|
app.use(express.bodyParser());
|
||||||
|
swagger.setAppHandler(app);
|
||||||
|
|
||||||
|
// resources for the demo
|
||||||
|
{{#apis}}
|
||||||
|
var {{name}} = require("./apis/{{name}}.js");
|
||||||
|
{{/apis}}
|
||||||
|
|
||||||
|
swagger.addModels(models)
|
||||||
|
{{#apis}}
|
||||||
|
{{#operations}}
|
||||||
|
{{#operation}}.add{{httpMethod}}({{name}}.{{nickname}}){{/operation}}{{newline}}
|
||||||
|
{{/operations}}
|
||||||
|
{{/apis}};
|
||||||
|
|
||||||
|
// configures the app
|
||||||
|
swagger.configure("http://localhost:8002", "0.1");
|
||||||
|
|
||||||
|
// start the server
|
||||||
|
app.listen(8002);
|
5
samples/server-generator/node/templates/models.mustache
Normal file
5
samples/server-generator/node/templates/models.mustache
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
exports.models = {
|
||||||
|
{{#models}}
|
||||||
|
"{{modelName}}": {{{modelJson}}}{{#hasMore}},{{/hasMore}}
|
||||||
|
{{/models}}
|
||||||
|
}
|
17
samples/server-generator/node/templates/package.json
Executable file
17
samples/server-generator/node/templates/package.json
Executable file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "sample-app",
|
||||||
|
"description": "Wordnik node.js server generator",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"homepage": "https://github.com/wordnik/swagger-codegen",
|
||||||
|
"main": "./Common/node/swagger.js",
|
||||||
|
"directories": {
|
||||||
|
"lib": "./Common/node"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.8.x"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"connect": ">= 1.8.x",
|
||||||
|
"express": "3.x"
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user