UNPKG

arrow-admin

Version:
263 lines (246 loc) 5.76 kB
var _ = require('lodash'); var verbMap = { POST: 'create', GET: 'find', PUT: 'update', DELETE: 'delete' }; exports.route = route; exports.definition = definition; function route(objectModel) { return function (req, res, next) { res.send(definition(objectModel)); }; } function definition(objectModel) { return { swagger: '2.0', info: compact({ version: objectModel.metadata.version, title: objectModel.metadata.name, description: objectModel.metadata.description, termsOfService: objectModel.metadata.termsOfService, contact: { name: objectModel.metadata.author }, license: { name: objectModel.metadata.license } }), // TODO: Specify the host (from ENV variables?) host: (objectModel.server.host || '127.0.0.1') + (objectModel.server.port ? ':' + objectModel.server.port : ''), basePath: objectModel.config.apiPrefix, schemes: [ 'http', 'https' ], consumes: [ 'application/json', 'application/x-www-form-urlencoded', 'multipart/form-data' ], produces: [ 'application/json', 'application/xml', 'text/yaml', 'text/csv', 'text/plain' ], definitions: createDefinitions(objectModel), paths: createPaths(objectModel) }; } function createDefinitions(objectModel) { var retVal = {}, models = objectModel.models; for (var modelName in models) { if (models.hasOwnProperty(modelName)) { var model = models[modelName], safeName = modelName.replace(/\//g, '_'), required = [], properties = {}; for (var fieldName in model.fields) { if (model.fields.hasOwnProperty(fieldName)) { var field = model.fields[fieldName]; if (field.required) { required.push(fieldName); } properties[fieldName] = {}; switch (field.type) { case 'number': properties[fieldName].type = 'integer'; properties[fieldName].format = 'int32'; break; case 'array': // TODO: We need more information about the array sub-type here. properties[fieldName].items = {type: 'string'}; break; default: properties[fieldName].type = field.type; break; } } } retVal[safeName] = { type: 'object', required: required, properties: properties }; } } retVal.ResponseModel = { type: 'object', required: [ 'code', 'success', 'request-id' ], properties: { code: { type: 'integer', format: 'int32' }, success: { type: 'boolean', 'default': false }, 'request-id': { type: 'string' }, message: { type: 'string' }, url: { type: 'string' } } }; retVal.ErrorModel = { type: 'object', allOf: [ { "$ref": "#/definitions/ResponseModel" }, { "required": [ 'message' ] } ] }; return retVal; } function createPaths(objectModel) { var retVal = {}, apis = objectModel.apis; for (var groupName in apis) { if (apis.hasOwnProperty(groupName)) { var api = apis[groupName]; for (var i = 0; i < api.endpoints.length; i++) { var endpoint = api.endpoints[i], relativePath = translatePath(endpoint.path.split(objectModel.config.apiPrefix).pop()), def = retVal[relativePath]; if (endpoint.enabled === false) { continue; } if (!def) { retVal[relativePath] = def = {}; } def[endpoint.method.toLowerCase()] = compact({ description: endpoint.description, operationId: getOperationId(endpoint), deprecated: endpoint.deprecated, parameters: translateParameters(endpoint), responses: endpoint.responses || api.responses, tags: [groupName] }); } } } return retVal; } function translatePath(path) { return path.replace(/:([^/]+)/g, '{$1}'); } function translateParameters(endpoint) { var retVal = [], bodyParams; for (var name in endpoint.parameters) { if (endpoint.parameters.hasOwnProperty(name)) { var param = endpoint.parameters[name]; switch (param.type) { case 'body': if (!bodyParams) { bodyParams = { name: endpoint.nickname, 'in': 'body', description: endpoint.nickname + ' body', schema: { type: 'object', required: [], properties: {} } }; } if (param.required) { bodyParams.schema.required.push(name); } bodyParams.schema.properties[name] = { type: param.dataType || 'string', description: param.description }; break; default: var translated = { name: name, 'in': param.type, description: param.description, required: !!param.required, type: param.dataType || 'string' }; // TODO: We need more information about the sub-types of objects and arrays. if (param.dataType === 'object') { translated.type = 'string'; } if (param.dataType === 'array') { translated.items = {type: 'object'}; } retVal.push(compact(translated)); break; } } } if (bodyParams) { retVal.push(bodyParams); } return retVal; } function getOperationId(endpoint) { var retVal = verbMap[endpoint.method] || endpoint.method.toLowerCase(), splits = endpoint.path.replace(/appc\./, '') .replace(/_[a-z]/ig, function (val) { return val[1].toUpperCase(); }) .slice(1).split('/'); for (var i = 1; i < splits.length; i++) { var split = splits[i]; if (split[0] === ':') { retVal += 'By' + split.slice(1).toUpperCase(); } else { retVal += split[0].toUpperCase() + split.slice(1); } } return retVal; } function compact(obj) { return _.omit(obj, function objectFilter(val, key) { if (_.isObject(val)) { obj[key] = compact(val); if (_.keys(obj[key]).length === 0) { return false; } } return !val; }); }