diff --git a/samples/server-generator/node/NodeServerFromSpec.scala b/samples/server-generator/node/NodeServerFromSpec.scala index 5a00eee3f8dc..d91aace07076 100644 --- a/samples/server-generator/node/NodeServerFromSpec.scala +++ b/samples/server-generator/node/NodeServerFromSpec.scala @@ -40,6 +40,7 @@ object NodeServerGenerator extends BasicScalaGenerator { // supporting classes override def supportingFiles = List( ("package.json", outputFolder, "package.json"), + ("README.mustache", outputFolder, "README.md"), ("main.mustache", destinationDir, "main.js"), ("models.mustache", destinationDir, "models.js"), ("Common/node/paramTypes.js", destinationDir + "/Common/node", "paramTypes.js"), diff --git a/samples/server-generator/node/output/App/Common/node/paramTypes.js b/samples/server-generator/node/output/App/Common/node/paramTypes.js new file mode 100644 index 000000000000..f73ffee2b8b9 --- /dev/null +++ b/samples/server-generator/node/output/App/Common/node/paramTypes.js @@ -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" + }; +}; \ No newline at end of file diff --git a/samples/server-generator/node/output/App/Common/node/randomizer.js b/samples/server-generator/node/output/App/Common/node/randomizer.js new file mode 100644 index 000000000000..eea17b8223bc --- /dev/null +++ b/samples/server-generator/node/output/App/Common/node/randomizer.js @@ -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=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; diff --git a/samples/server-generator/node/output/App/apis/PetApi.js b/samples/server-generator/node/output/App/apis/PetApi.js new file mode 100644 index 000000000000..d36f982e09d9 --- /dev/null +++ b/samples/server-generator/node/output/App/apis/PetApi.js @@ -0,0 +1,115 @@ +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"); + +exports.getPetById = { + 'spec': { + "description" : "Operations about pets", + "path" : "/pet.{format}/{petId}", + "notes" : "Returns a pet based on ID", + "summary" : "Find pet by ID", + "method": "GET", + "params" : [].concat([param.path("petId", "ID of pet that needs to be fetched")]).concat([]).concat([]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "getPetById" + }, + 'action': function (req,res) { + if (!req.params.petId) { + throw swe.invalid('petId'); + } + writeResponse(res, {message: "how about implementing getPetById as a GET method?"}); + } +}; +exports.addPet = { + 'spec': { + "description" : "Operations about pets", + "path" : "/pet.{format}", + "notes" : "", + "summary" : "Add a new pet to the store", + "method": "POST", + "params" : [].concat([]).concat([]).concat([param.post("Pet", "Pet object that needs to be added to the store", true) + ]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "addPet" + }, + 'action': function (req,res) { + if (!req.params.body) { + throw swe.invalid('body'); + } + writeResponse(res, {message: "how about implementing addPet as a POST method?"}); + } +}; +exports.updatePet = { + 'spec': { + "description" : "Operations about pets", + "path" : "/pet.{format}", + "notes" : "", + "summary" : "Update an existing pet", + "method": "PUT", + "params" : [].concat([]).concat([]).concat([param.post("Pet", "Pet object that needs to be updated in the store", true) + ]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "updatePet" + }, + 'action': function (req,res) { + if (!req.params.body) { + throw swe.invalid('body'); + } + writeResponse(res, {message: "how about implementing updatePet as a PUT method?"}); + } +}; +exports.findPetsByStatus = { + 'spec': { + "description" : "Operations about pets", + "path" : "/pet.{format}/findByStatus", + "notes" : "Multiple status values can be provided with comma seperated strings", + "summary" : "Finds Pets by status", + "method": "GET", + "params" : [param.query("status", "Status values that need to be considered for filter", "String", true, true, "LIST[available,pending,sold]", "available")].concat([]).concat([]).concat([]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "findPetsByStatus" + }, + 'action': function (req,res) { + if (!req.params.status) { + throw swe.invalid('status'); + } + writeResponse(res, {message: "how about implementing findPetsByStatus as a GET method?"}); + } +}; +exports.findPetsByTags = { + 'spec': { + "description" : "Operations about pets", + "path" : "/pet.{format}/findByTags", + "notes" : "Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.", + "summary" : "Finds Pets by tags", + "method": "GET", + "params" : [param.query("tags", "Tags to filter by", "String", true, true, "")].concat([]).concat([]).concat([]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "findPetsByTags" + }, + 'action': function (req,res) { + if (!req.params.tags) { + throw swe.invalid('tags'); + } + writeResponse(res, {message: "how about implementing findPetsByTags as a GET method?"}); + } +}; + diff --git a/samples/server-generator/node/output/App/apis/StoreApi.js b/samples/server-generator/node/output/App/apis/StoreApi.js new file mode 100644 index 000000000000..54720f9dbe60 --- /dev/null +++ b/samples/server-generator/node/output/App/apis/StoreApi.js @@ -0,0 +1,76 @@ +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"); + +exports.getOrderById = { + 'spec': { + "description" : "Operations about pets", + "path" : "/store.{format}/order/{orderId}", + "notes" : "For valid response try integer IDs with value <= 5. Anything above 5 or nonintegers will generate API errors", + "summary" : "Find purchase order by ID", + "method": "GET", + "params" : [].concat([param.path("orderId", "ID of pet that needs to be fetched")]).concat([]).concat([]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "getOrderById" + }, + 'action': function (req,res) { + if (!req.params.orderId) { + throw swe.invalid('orderId'); + } + writeResponse(res, {message: "how about implementing getOrderById as a GET method?"}); + } +}; +exports.deleteOrder = { + 'spec': { + "description" : "Operations about pets", + "path" : "/store.{format}/order/{orderId}", + "notes" : "For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors", + "summary" : "Delete purchase order by ID", + "method": "DELETE", + "params" : [].concat([param.path("orderId", "ID of the order that needs to be deleted")]).concat([]).concat([]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "deleteOrder" + }, + 'action': function (req,res) { + if (!req.params.orderId) { + throw swe.invalid('orderId'); + } + writeResponse(res, {message: "how about implementing deleteOrder as a DELETE method?"}); + } +}; +exports.placeOrder = { + 'spec': { + "description" : "Operations about pets", + "path" : "/store.{format}/order", + "notes" : "", + "summary" : "Place an order for a pet", + "method": "POST", + "params" : [].concat([]).concat([]).concat([param.post("Order", "order placed for purchasing the pet", true) + ]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "placeOrder" + }, + 'action': function (req,res) { + if (!req.params.body) { + throw swe.invalid('body'); + } + writeResponse(res, {message: "how about implementing placeOrder as a POST method?"}); + } +}; + diff --git a/samples/server-generator/node/output/App/apis/UserApi.js b/samples/server-generator/node/output/App/apis/UserApi.js new file mode 100644 index 000000000000..27cdf0072e6b --- /dev/null +++ b/samples/server-generator/node/output/App/apis/UserApi.js @@ -0,0 +1,177 @@ +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"); + +exports.createUsersWithArrayInput = { + 'spec': { + "description" : "Operations about pets", + "path" : "/user.{format}/createWithArray", + "notes" : "", + "summary" : "Creates list of users with given input array", + "method": "POST", + "params" : [].concat([]).concat([]).concat([param.post("Array[User]", "List of user object", true) + ]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "createUsersWithArrayInput" + }, + 'action': function (req,res) { + if (!req.params.body) { + throw swe.invalid('body'); + } + writeResponse(res, {message: "how about implementing createUsersWithArrayInput as a POST method?"}); + } +}; +exports.createUser = { + 'spec': { + "description" : "Operations about pets", + "path" : "/user.{format}", + "notes" : "This can only be done by the logged in user.", + "summary" : "Create user", + "method": "POST", + "params" : [].concat([]).concat([]).concat([param.post("User", "Created user object", true) + ]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "createUser" + }, + 'action': function (req,res) { + if (!req.params.body) { + throw swe.invalid('body'); + } + writeResponse(res, {message: "how about implementing createUser as a POST method?"}); + } +}; +exports.createUsersWithListInput = { + 'spec': { + "description" : "Operations about pets", + "path" : "/user.{format}/createWithList", + "notes" : "", + "summary" : "Creates list of users with given list input", + "method": "POST", + "params" : [].concat([]).concat([]).concat([param.post("List[User]", "List of user object", true) + ]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "createUsersWithListInput" + }, + 'action': function (req,res) { + if (!req.params.body) { + throw swe.invalid('body'); + } + writeResponse(res, {message: "how about implementing createUsersWithListInput as a POST method?"}); + } +}; +exports.updateUser = { + 'spec': { + "description" : "Operations about pets", + "path" : "/user.{format}/{username}", + "notes" : "This can only be done by the logged in user.", + "summary" : "Updated user", + "method": "PUT", + "params" : [].concat([param.path("username", "name that need to be deleted")]).concat([]).concat([param.post("User", "Updated user object", true) + ]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "updateUser" + }, + 'action': function (req,res) { + if (!req.params.username) { + throw swe.invalid('username'); + } + if (!req.params.body) { + throw swe.invalid('body'); + } + writeResponse(res, {message: "how about implementing updateUser as a PUT method?"}); + } +}; +exports.deleteUser = { + 'spec': { + "description" : "Operations about pets", + "path" : "/user.{format}/{username}", + "notes" : "This can only be done by the logged in user.", + "summary" : "Delete user", + "method": "DELETE", + "params" : [].concat([param.path("username", "The name that needs to be deleted")]).concat([]).concat([]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "deleteUser" + }, + 'action': function (req,res) { + if (!req.params.username) { + throw swe.invalid('username'); + } + writeResponse(res, {message: "how about implementing deleteUser as a DELETE method?"}); + } +}; +exports.getUserByName = { + 'spec': { + "description" : "Operations about pets", + "path" : "/user.{format}/{username}", + "notes" : "", + "summary" : "Get user by user name", + "method": "GET", + "params" : [].concat([param.path("username", "The name that needs to be fetched. Use user1 for testing.")]).concat([]).concat([]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "getUserByName" + }, + 'action': function (req,res) { + if (!req.params.username) { + throw swe.invalid('username'); + } + writeResponse(res, {message: "how about implementing getUserByName as a GET method?"}); + } +}; +exports.loginUser = { + 'spec': { + "description" : "Operations about pets", + "path" : "/user.{format}/login", + "notes" : "", + "summary" : "Logs user into the system", + "method": "GET", + "params" : [param.query("username", "The user name for login", "String", true, false, ""),param.query("password", "The password for login in clear text", "String", true, false, "")].concat([]).concat([]).concat([]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "loginUser" + }, + 'action': function (req,res) { + if (!req.params.username) { + throw swe.invalid('username'); + } + if (!req.params.password) { + throw swe.invalid('password'); + } + writeResponse(res, {message: "how about implementing loginUser as a GET method?"}); + } +}; +exports.logoutUser = { + 'spec': { + "description" : "Operations about pets", + "path" : "/user.{format}/logout", + "notes" : "", + "summary" : "Logs out current logged in user session", + "method": "GET", + "params" : [].concat([]).concat([]).concat([]), + "responseClass" : "", + "errorResponses" : [swe.invalid('id'), swe.notFound('pet')], + "nickname" : "logoutUser" + }, + 'action': function (req,res) { + writeResponse(res, {message: "how about implementing logoutUser as a GET method?"}); + } +}; + diff --git a/samples/server-generator/node/output/App/main.js b/samples/server-generator/node/output/App/main.js new file mode 100644 index 000000000000..f979c95a85f3 --- /dev/null +++ b/samples/server-generator/node/output/App/main.js @@ -0,0 +1,39 @@ +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 +var UserApi = require("./apis/UserApi.js"); +var StoreApi = require("./apis/StoreApi.js"); +var PetApi = require("./apis/PetApi.js"); +swagger.addModels(models) + .addPOST(UserApi.createUsersWithArrayInput) + .addPOST(UserApi.createUser) + .addPOST(UserApi.createUsersWithListInput) + .addPUT(UserApi.updateUser) + .addDELETE(UserApi.deleteUser) + .addGET(UserApi.getUserByName) + .addGET(UserApi.loginUser) + .addGET(UserApi.logoutUser) + .addGET(StoreApi.getOrderById) + .addDELETE(StoreApi.deleteOrder) + .addPOST(StoreApi.placeOrder) + .addGET(PetApi.getPetById) + .addPOST(PetApi.addPet) + .addPUT(PetApi.updatePet) + .addGET(PetApi.findPetsByStatus) + .addGET(PetApi.findPetsByTags) + ; + +// configures the app +swagger.configure("http://localhost:8002", "0.1"); + +// start the server +app.listen(8002); + diff --git a/samples/server-generator/node/output/App/models.js b/samples/server-generator/node/output/App/models.js new file mode 100644 index 000000000000..e0eb13dddaef --- /dev/null +++ b/samples/server-generator/node/output/App/models.js @@ -0,0 +1,2 @@ +exports.models = { + "Pet": {"id":"Pet","properties":{"id":{"type":"long"},"tags":{"items":{"$ref":"Tag"},"type":"Array"},"category":{"type":"Category"},"status":{"allowableValues":{"valueType":"LIST","values":["available","pending","sold"],"valueType":"LIST"},"description":"pet status in the store","type":"string"},"name":{"type":"string"},"photoUrls":{"items":{"type":"string"},"type":"Array"}}},"Category": {"id":"Category","properties":{"id":{"type":"long"},"name":{"type":"string"}}},"Tag": {"id":"Tag","properties":{"id":{"type":"long"},"name":{"type":"string"}}},"User": {"id":"User","properties":{"id":{"type":"long"},"lastName":{"type":"string"},"username":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"userStatus":{"allowableValues":{"valueType":"LIST","values":["1-registered","2-active","3-closed"],"valueType":"LIST"},"description":"User Status","type":"int"},"firstName":{"type":"string"},"password":{"type":"string"}}},"Order": {"id":"Order","properties":{"id":{"type":"long"},"petId":{"type":"long"},"status":{"allowableValues":{"valueType":"LIST","values":["placed"," approved"," delivered"],"valueType":"LIST"},"description":"Order Status","type":"string"},"quantity":{"type":"int"},"shipDate":{"type":"Date"}}}} diff --git a/samples/server-generator/node/output/README.md b/samples/server-generator/node/output/README.md new file mode 100644 index 000000000000..85565835ffd9 --- /dev/null +++ b/samples/server-generator/node/output/README.md @@ -0,0 +1,102 @@ +# Swagger generated server + +## Overview +This server was generated by the [swagger-codegen](https://github.com/wordnik/swagger-codegen) project. By using the +[swagger-spec](https://github.com/wordnik/swagger-core/wiki) from a remote server, you can easily generate a server stub. This +is an example of building a node.js server. + +This example uses the [expressjs](http://expressjs.com/) framework. + +### Prerequisites +You need the following installed and available in your $PATH: + +
  • - node (http://nodejs.org) + +
  • - Scala 2.9.1 [available here](http://www.scala-lang.org) + +You also need to add the scala binary to your PATH. + +### Generating a server +You first need to build the `swagger-codegen` project--this is done by running this command at the root of the swagger-codegen project: + +``` +mvn package +``` + +You can now generate a server from any valid** swagger spec: + +``` +./bin/runscala.sh samples/server-generator/node/NodeServerFromSpec.scala http://petstore.swagger.wordnik.com/api/resources.json special-key +``` + +After executing this script, you will have an output directory with the server-generated files: + +``` +$ find samples/server-generator/node/output +samples/server-generator/node/output +samples/server-generator/node/output/App +samples/server-generator/node/output/App/apis +samples/server-generator/node/output/App/apis/PetApi.js +samples/server-generator/node/output/App/apis/StoreApi.js +samples/server-generator/node/output/App/apis/UserApi.js +samples/server-generator/node/output/App/Common +samples/server-generator/node/output/App/Common/node +samples/server-generator/node/output/App/Common/node/paramTypes.js +samples/server-generator/node/output/App/Common/node/randomizer.js +samples/server-generator/node/output/App/Common/node/swagger.js +samples/server-generator/node/output/App/main.js +samples/server-generator/node/output/App/models.js +samples/server-generator/node/output/package.json +``` + +To run the server, cd to the `samples/server-generator/node/output` folder and run: + +``` +# install the dependencies +npm install +node Apps/main.js +``` + +You can now load the swagger-ui against `http://localhost:8002/resources.json`. Of course this isn't a fully +runnable server! You have to add the logic in the apis/*.js files. But that's the easy part. + + +### Making it your own +Running the sample is easy, but how about making your own server? Easy! Just modify the `samples/server-generator/node/NodeServerFromSpec.scala` file. +See comments in below, in a copy of the script + +```scala +object NodeServerGenerator extends BasicScalaGenerator { + def main(args: Array[String]) = generateClient(args) + + // if you want to point to a different template directory, change this + override def templateDir = "samples/server-generator/node/templates" + + // where the files are written + val outputFolder = "samples/server-generator/node/output" + + // where to write generated code + override def destinationDir = outputFolder + "/App" + + // template used for apis (writes one file per api) + apiTemplateFiles ++= Map("api.mustache" -> ".js") + + modelTemplateFiles.clear + + // puts the api files in a folder called `apis` + override def apiPackage = Some("apis") + + // copies swagger files and processes any *.mustache files + override def supportingFiles = List( + ("package.json", outputFolder, "package.json"), + ("README.json", outputFolder, "README.md"), + ("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")) +} +``` + +Sound easy? It is! + diff --git a/samples/server-generator/node/output/package.json b/samples/server-generator/node/output/package.json new file mode 100644 index 000000000000..871f6a5c3ffc --- /dev/null +++ b/samples/server-generator/node/output/package.json @@ -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" + } +} diff --git a/samples/server-generator/node/templates/README.mustache b/samples/server-generator/node/templates/README.mustache new file mode 100644 index 000000000000..bbd8f231a4d4 --- /dev/null +++ b/samples/server-generator/node/templates/README.mustache @@ -0,0 +1,101 @@ +# Swagger generated server + +## Overview +This server was generated by the [swagger-codegen](https://github.com/wordnik/swagger-codegen) project. By using the +[swagger-spec](https://github.com/wordnik/swagger-core/wiki) from a remote server, you can easily generate a server stub. This +is an example of building a node.js server. + +This example uses the [expressjs](http://expressjs.com/) framework. + +### Prerequisites +You need the following installed and available in your $PATH: + +
  • - node (http://nodejs.org) + +
  • - Scala 2.9.1 [available here](http://www.scala-lang.org) + +You also need to add the scala binary to your PATH. + +### Generating a server +You first need to build the `swagger-codegen` project--this is done by running this command at the root of the swagger-codegen project: + +``` +mvn package +``` + +You can now generate a server from any valid** swagger spec: + +``` +./bin/runscala.sh samples/server-generator/node/NodeServerFromSpec.scala http://petstore.swagger.wordnik.com/api/resources.json special-key +``` + +After executing this script, you will have an output directory with the server-generated files: + +``` +$ find samples/server-generator/node/output +samples/server-generator/node/output +samples/server-generator/node/output/App +samples/server-generator/node/output/App/apis +samples/server-generator/node/output/App/apis/PetApi.js +samples/server-generator/node/output/App/apis/StoreApi.js +samples/server-generator/node/output/App/apis/UserApi.js +samples/server-generator/node/output/App/Common +samples/server-generator/node/output/App/Common/node +samples/server-generator/node/output/App/Common/node/paramTypes.js +samples/server-generator/node/output/App/Common/node/randomizer.js +samples/server-generator/node/output/App/Common/node/swagger.js +samples/server-generator/node/output/App/main.js +samples/server-generator/node/output/App/models.js +samples/server-generator/node/output/package.json +``` + +To run the server, cd to the `samples/server-generator/node/output` folder and run: + +``` +# install the dependencies +npm install +node Apps/main.js +``` + +You can now load the swagger-ui against `http://localhost:8002/resources.json`. Of course this isn't a fully +runnable server! You have to add the logic in the apis/*.js files. But that's the easy part. + + +### Making it your own +Running the sample is easy, but how about making your own server? Easy! Just modify the `samples/server-generator/node/NodeServerFromSpec.scala` file. +See comments in below, in a copy of the script + +```scala +object NodeServerGenerator extends BasicScalaGenerator { + def main(args: Array[String]) = generateClient(args) + + // if you want to point to a different template directory, change this + override def templateDir = "samples/server-generator/node/templates" + + // where the files are written + val outputFolder = "samples/server-generator/node/output" + + // where to write generated code + override def destinationDir = outputFolder + "/App" + + // template used for apis (writes one file per api) + apiTemplateFiles ++= Map("api.mustache" -> ".js") + + modelTemplateFiles.clear + + // puts the api files in a folder called `apis` + override def apiPackage = Some("apis") + + // copies swagger files and processes any *.mustache files + override def supportingFiles = List( + ("package.json", outputFolder, "package.json"), + ("README.json", outputFolder, "README.md"), + ("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")) +} +``` + +Sound easy? It is!