swagger-js-codegen
Version:
A Swagger codegen for JavaScript
356 lines (332 loc) • 14 kB
JavaScript
;
var fs = require('fs');
var Mustache = require('mustache');
var beautify = require('js-beautify').js_beautify;
var lint = require('jshint').JSHINT;
var _ = require('lodash');
var ts = require('./typescript');
var flow = require('./flow');
var normalizeName = function(id) {
return id.replace(/\.|\-|\{|\}|\s/g, '_');
};
var getPathToMethodName = function(opts, m, path){
if(path === '/' || path === '') {
return m;
}
// clean url path for requests ending with '/'
var cleanPath = path.replace(/\/$/, '');
var segments = cleanPath.split('/').slice(1);
segments = _.transform(segments, function (result, segment) {
if (segment[0] === '{' && segment[segment.length - 1] === '}') {
segment = 'by' + segment[1].toUpperCase() + segment.substring(2, segment.length - 1);
}
result.push(segment);
});
var result = _.camelCase(segments.join('-'));
return m.toLowerCase() + result[0].toUpperCase() + result.substring(1);
};
var getViewForSwagger2 = function(opts, type){
var swagger = opts.swagger;
var methods = [];
var authorizedMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'COPY', 'HEAD', 'OPTIONS', 'LINK', 'UNLIK', 'PURGE', 'LOCK', 'UNLOCK', 'PROPFIND'];
var data = {
isNode: type === 'node' || type === 'react',
isES6: opts.isES6 || type === 'react',
description: swagger.info.description,
isSecure: swagger.securityDefinitions !== undefined,
moduleName: opts.moduleName,
className: opts.className,
imports: opts.imports,
domain: (swagger.schemes && swagger.schemes.length > 0 && swagger.host && swagger.basePath) ? swagger.schemes[0] + '://' + swagger.host + swagger.basePath.replace(/\/+$/g,'') : '',
methods: [],
definitions: []
};
_.forEach(swagger.paths, function(api, path){
var globalParams = [];
/**
* @param {Object} op - meta data for the request
* @param {string} m - HTTP method name - eg: 'get', 'post', 'put', 'delete'
*/
_.forEach(api, function(op, m){
if(m.toLowerCase() === 'parameters') {
globalParams = op;
}
});
_.forEach(api, function(op, m){
var M = m.toUpperCase();
if(M === '' || authorizedMethods.indexOf(M) === -1) {
return;
}
var secureTypes = [];
if(swagger.securityDefinitions !== undefined || op.security !== undefined) {
var mergedSecurity = _.merge([], swagger.security, op.security).map(function(security){
return Object.keys(security);
});
if(swagger.securityDefinitions) {
for(var sk in swagger.securityDefinitions) {
if(mergedSecurity.join(',').indexOf(sk) !== -1){
secureTypes.push(swagger.securityDefinitions[sk].type);
}
}
}
}
var methodName = (op.operationId ? normalizeName(op.operationId) : getPathToMethodName(opts, m, path));
// Make sure the method name is unique
if(methods.indexOf(methodName) !== -1) {
var i = 1;
while(true) {
if(methods.indexOf(methodName + '_' + i) !== -1) {
i++;
} else {
methodName = methodName + '_' + i;
break;
}
}
}
methods.push(methodName);
var method = {
path: path,
className: opts.className,
methodName: methodName,
method: M,
isGET: M === 'GET',
isPOST: M === 'POST',
summary: op.description || op.summary,
externalDocs: op.externalDocs,
isSecure: swagger.security !== undefined || op.security !== undefined,
isSecureToken: secureTypes.indexOf('oauth2') !== -1,
isSecureApiKey: secureTypes.indexOf('apiKey') !== -1,
isSecureBasic: secureTypes.indexOf('basic') !== -1,
parameters: [],
hasParameters: false,
headers: []
};
if(method.isSecure && method.isSecureToken) {
data.isSecureToken = method.isSecureToken;
}
if(method.isSecure && method.isSecureApiKey) {
data.isSecureApiKey = method.isSecureApiKey;
}
if(method.isSecure && method.isSecureBasic) {
data.isSecureBasic = method.isSecureBasic;
}
var produces = op.produces || swagger.produces;
if(produces) {
method.headers.push({
name: 'Accept',
value: `'${produces.map(function(value) { return value; }).join(', ')}'`,
});
}
var consumes = op.consumes || swagger.consumes;
if(consumes) {
method.headers.push({name: 'Content-Type', value: '\'' + consumes + '\'' });
}
var params = [];
if(_.isArray(op.parameters)) {
params = op.parameters;
}
params = params.concat(globalParams);
_.forEach(params, function(parameter) {
//Ignore parameters which contain the x-exclude-from-bindings extension
if(parameter['x-exclude-from-bindings'] === true) {
return;
}
// Ignore headers which are injected by proxies & app servers
// eg: https://cloud.google.com/appengine/docs/go/requests#Go_Request_headers
if (parameter['x-proxy-header'] && !data.isNode) {
return;
}
if (_.isString(parameter.$ref)) {
var segments = parameter.$ref.split('/');
parameter = swagger.parameters[segments.length === 1 ? segments[0] : segments[2] ];
}
parameter.camelCaseName = _.camelCase(parameter.name);
if(parameter.enum && parameter.enum.length === 1) {
parameter.isSingleton = true;
parameter.singleton = parameter.enum[0];
}
if(parameter.in === 'body'){
parameter.isBodyParameter = true;
} else if(parameter.in === 'path'){
parameter.isPathParameter = true;
} else if(parameter.in === 'query'){
if(parameter['x-name-pattern']){
parameter.isPatternType = true;
parameter.pattern = parameter['x-name-pattern'];
}
parameter.isQueryParameter = true;
} else if(parameter.in === 'header'){
parameter.isHeaderParameter = true;
} else if(parameter.in === 'formData'){
parameter.isFormParameter = true;
}
parameter.tsType = ts.convertType(parameter);
parameter.flowType = flow.convertType(parameter);
parameter.cardinality = parameter.required ? '' : '?';
method.parameters.push(parameter);
});
var success = 0;
method.isInlineType = false;
_.forEach(op.responses, function(val, key) {
if (key.startsWith('2')) {
if (val.schema) {
method.methodTsType = ts.convertType(val.schema);
method.methodFlowType = flow.convertType(val.schema);
method.isInlineType = true;
} else {
method.methodResponse = val.description;
}
}
});
method.hasParameters = method.parameters.length > 0;
data.methods.push(method);
});
});
_.forEach(swagger.definitions, function(definition, name){
data.definitions.push({
name: type === 'flow' ? flow.sanitizeReservedWords(name) : name,
description: definition.description,
flowType: flow.convertType(definition, swagger),
tsType: ts.convertType(definition, swagger)
});
});
return data;
};
var getViewForSwagger1 = function(opts, type){
var swagger = opts.swagger;
var data = {
isNode: type === 'node' || type === 'react',
isES6: opts.isES6 || type === 'react',
description: swagger.description,
moduleName: opts.moduleName,
className: opts.className,
domain: swagger.basePath ? swagger.basePath : '',
methods: []
};
swagger.apis.forEach(function(api){
api.operations.forEach(function(op){
if (op.method === 'OPTIONS') {
return;
}
var method = {
path: api.path,
className: opts.className,
methodName: op.nickname,
method: op.method,
isGET: op.method === 'GET',
isPOST: op.method.toUpperCase() === 'POST',
summary: op.summary,
parameters: op.parameters,
headers: []
};
if(op.produces) {
var headers = [];
headers.value = [];
headers.name = 'Accept';
headers.value.push(op.produces.map(function(value) { return '\'' + value + '\''; }).join(', '));
method.headers.push(headers);
}
op.parameters = op.parameters ? op.parameters : [];
op.parameters.forEach(function(parameter) {
parameter.camelCaseName = _.camelCase(parameter.name);
if(parameter.enum && parameter.enum.length === 1) {
parameter.isSingleton = true;
parameter.singleton = parameter.enum[0];
}
if(parameter.paramType === 'body'){
parameter.isBodyParameter = true;
} else if(parameter.paramType === 'path'){
parameter.isPathParameter = true;
} else if(parameter.paramType === 'query'){
if(parameter['x-name-pattern']){
parameter.isPatternType = true;
parameter.pattern = parameter['x-name-pattern'];
}
parameter.isQueryParameter = true;
} else if(parameter.paramType === 'header'){
parameter.isHeaderParameter = true;
} else if(parameter.paramType === 'form'){
parameter.isFormParameter = true;
}
});
data.methods.push(method);
});
});
return data;
};
var getCode = function(opts, type) {
// For Swagger Specification version 2.0 value of field 'swagger' must be a string '2.0'
var data = opts.swagger.swagger === '2.0' ? getViewForSwagger2(opts, type) : getViewForSwagger1(opts, type);
if (type === 'custom') {
if (!_.isObject(opts.template) || !_.isString(opts.template.class) || !_.isString(opts.template.method)) {
throw new Error('Unprovided custom template. Please use the following template: template: { class: "...", method: "...", request: "..." }');
}
} else {
if (!_.isObject(opts.template)) {
opts.template = {};
}
var templates = __dirname + '/../templates/';
opts.template.class = opts.template.class || fs.readFileSync(templates + type + '-class.mustache', 'utf-8');
opts.template.method = opts.template.method || fs.readFileSync(templates + (_.includes(['flow', 'typescript'], type) ? type + '-' : '') + 'method.mustache', 'utf-8');
if(type === 'typescript') {
opts.template.type = opts.template.type || fs.readFileSync(templates + 'type.mustache', 'utf-8');
} else if (type === 'flow') {
opts.template.type = opts.template.type || fs.readFileSync(templates + 'flow-type.mustache', 'utf-8');
}
}
if (opts.mustache) {
_.assign(data, opts.mustache);
}
var source = Mustache.render(opts.template.class, data, opts.template);
var lintOptions = {
node: type === 'node' || type === 'custom',
browser: type === 'angular' || type === 'custom' || type === 'react',
undef: true,
strict: true,
trailing: true,
smarttabs: true,
maxerr: 999
};
if (opts.esnext) {
lintOptions.esnext = true;
}
if(type === 'typescript' || type === 'flow') {
opts.lint = false;
}
if (opts.lint === undefined || opts.lint === true) {
lint(source, lintOptions);
lint.errors.forEach(function(error) {
if (error.code[0] === 'E') {
throw new Error(error.reason + ' in ' + error.evidence + ' (' + error.code + ')');
}
});
}
if (opts.beautify === undefined || opts.beautify === true) {
return beautify(source, { indent_size: 4, max_preserve_newlines: 2 });
} else {
return source;
}
};
exports.CodeGen = {
getTypescriptCode: function(opts){
if (opts.swagger.swagger !== '2.0') {
throw 'Typescript is only supported for Swagger 2.0 specs.';
}
return getCode(opts, 'typescript');
},
getAngularCode: function(opts){
return getCode(opts, 'angular');
},
getNodeCode: function(opts){
return getCode(opts, 'node');
},
getReactCode: function(opts){
return getCode(opts, 'react');
},
getFlowCode: function(opts) {
return getCode(opts, 'flow');
},
getCustomCode: function(opts){
return getCode(opts, 'custom');
}
};