Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
eabc7bf
The $http legacy promise methods success and error have been deprecat…
Apr 27, 2016
330832b
getTypescriptCode() can generate for Angular using $http
Apr 27, 2016
8e97453
export all interfaces for the module
Apr 27, 2016
9e231cc
dropped the `default` export
Apr 27, 2016
c6d0979
reverted un-related change to README
nalbion Apr 27, 2016
8f57b51
reverted un-related change
nalbion Apr 27, 2016
774a495
fixed typo - hasBody->hasForm
May 4, 2016
de40d02
Merge branch 'feature/typescript-angular' of https://github.com/nalbi…
May 4, 2016
e62f7bb
TypeScript should assert required parameters at compile-time
May 4, 2016
f8ebdce
updated README to include opts.language and opts.framework
May 4, 2016
c316ada
added new templates
May 4, 2016
6bfe66c
added tests for `selectTemplates` and `locateTemplate`
May 4, 2016
8e42271
more flexible support for languages and frameworks
May 4, 2016
f863201
restored updated basePath treatment
May 4, 2016
df773a6
restored updated basePath treatment
May 4, 2016
28b5e9f
hasForm
nalbion May 4, 2016
06ce474
New line at EOF
nalbion May 4, 2016
bbf9fcd
updated version
May 5, 2016
6829f3d
Merge branch 'feature/typescript-angular' of https://github.com/nalbi…
May 5, 2016
536c75b
merge recent changes on master
Jul 27, 2016
57d5c9b
these files were removed from master
Jul 27, 2016
82a3720
removed references to `template.request`
Jul 27, 2016
daa9239
Moved code from "typescript-angular-request.mustache" into "typescrip…
adrianwong81 Aug 1, 2016
c98a3ad
Merge pull request #2 from adrianwong81/feature/typescript-angular
nalbion Aug 1, 2016
ce0497b
sync with master
Aug 22, 2016
c5619c9
restored tests for `selectTemplates`, `locateTemplate`
Aug 22, 2016
4be38ae
added support for optional fields
Aug 22, 2016
d936942
default response is not always 200/201 - fallback to "any
Aug 22, 2016
9521a68
reject responses with status codes >= 400
Dec 1, 2016
6c878a5
don't generate code for OPTIONS requests
Dec 1, 2016
5e3728a
don't generate code for OPTIONS requests
Dec 1, 2016
3292350
Merge branch 'master' into feature/typescript-angular
nalbion Feb 3, 2017
5f135b9
fix up after update from master
Feb 3, 2017
3b98e57
Array is not implicity Array|Element
Feb 3, 2017
a41d233
treat `type:array` definitions as `type name = ...`
Feb 3, 2017
a4751ed
renamed `types` as `definitions`
Feb 3, 2017
6ca3f7f
getTypescriptCode() can generate for Angular using $http
Apr 27, 2016
c47beee
reverted un-related change to README
nalbion Apr 27, 2016
9974ba1
reverted un-related change
nalbion Apr 27, 2016
8259154
fixed typo - hasBody->hasForm
May 4, 2016
ba8b3a0
updated README to include opts.language and opts.framework
May 4, 2016
582b84c
added new templates
May 4, 2016
7e2b476
more flexible support for languages and frameworks
May 4, 2016
88d319c
hasForm
nalbion May 4, 2016
49551ca
New line at EOF
nalbion May 4, 2016
b7d3374
these files were removed from master
Jul 27, 2016
a7a6d33
removed references to `template.request`
Jul 27, 2016
c1851a3
Moved code from "typescript-angular-request.mustache" into "typescrip…
adrianwong81 Aug 1, 2016
ba10979
restored tests for `selectTemplates`, `locateTemplate`
Aug 22, 2016
b999db0
added support for optional fields
Aug 22, 2016
329ee2c
default response is not always 200/201 - fallback to "any
Aug 22, 2016
2e33e39
reject responses with status codes >= 400
Dec 1, 2016
61c5d08
don't generate code for OPTIONS requests
Dec 1, 2016
a3d9d5c
don't generate code for OPTIONS requests
Dec 1, 2016
b18e754
Array is not implicity Array|Element
Feb 3, 2017
387f80b
treat `type:array` definitions as `type name = ...`
Feb 3, 2017
dc2b3e6
updated as per master
Feb 3, 2017
311e2dd
cleaning up
Feb 3, 2017
6f69a36
switched from `/// reference` to `import`
Mar 22, 2017
4292e42
v1.7.13
Mar 22, 2017
429ff00
single quotes
Mar 28, 2017
eb63436
v1.7.14
Mar 28, 2017
5c20749
v1.7.15
Mar 28, 2017
ad66a98
fixed handling of arrays in query, optionally with enum
May 10, 2017
7fbaa16
v1.7.15-a
May 11, 2017
534fd1c
support `basePath`
May 17, 2017
ed6d3fa
v1.7.15-b
May 17, 2017
7c92ac0
support allOf
May 18, 2017
cd00549
v1.7.15-c
May 18, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ In addition to the common options listed below, `getCustomCode()` *requires* a `
lint:
type: boolean
description: whether or not to run jslint on the generated code
language:
type: string
description: currently only 'typescript' is supported, but could potentially be 'coffeescript', 'es2015'...
framework:
type: string
description: currently only 'angular' is supported, but could potentially be 'react', 'polymer'...
esnext:
type: boolean
description: passed through to jslint
Expand Down
112 changes: 91 additions & 21 deletions lib/codegen.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ var getViewForSwagger2 = function(opts, type){
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,'') : '',
basePath: swagger.basePath,
methods: [],
definitions: []
};
Expand All @@ -57,7 +58,7 @@ var getViewForSwagger2 = function(opts, type){
}
});
_.forEach(api, function(op, m){
if(authorizedMethods.indexOf(m.toUpperCase()) === -1) {
if(authorizedMethods.indexOf(m.toUpperCase()) === -1 || m.toUpperCase() == 'OPTIONS') {
return;
}
var method = {
Expand All @@ -74,7 +75,7 @@ var getViewForSwagger2 = function(opts, type){
headers: []
};

if(op.produces) {
if (op.produces) {
var headers = [];
headers.value = [];

Expand All @@ -85,17 +86,40 @@ var getViewForSwagger2 = function(opts, type){
}

var consumes = op.consumes || swagger.consumes;
if(consumes) {
if (consumes) {
method.headers.push({name: 'Content-Type', value: '\'' + consumes + '\'' });
}

var params = [];
if(_.isArray(op.parameters)) {
if (_.isArray(op.parameters)) {
params = op.parameters;
}

method.hasBody = false;
method.hasForm = false;
method.tsType = 'void';
method.hasVoidReturn = true;

if (op.responses) {
_.some(['200', '201'], function(code) {
if (op.responses[code]) {
method.tsType = ts.convertType(op.responses[code]);
if (method.tsType.isRef) {
method.tsType = method.tsType.target.charAt(0).toUpperCase() + method.tsType.target.substring(1);
} else {
method.tsType = method.tsType.tsType;
}
if (method.tsType != 'any') {
method.hasVoidReturn = false;
}
return true;
}
});
}

params = params.concat(globalParams);
_.forEach(params, function(parameter) {
//Ignore parameters which contain the x-exclude-from-bindings extension
// Ignore parameters which contain the x-exclude-from-bindings extension
if(parameter['x-exclude-from-bindings'] === true) {
return;
}
Expand All @@ -114,8 +138,10 @@ var getViewForSwagger2 = function(opts, type){
parameter.isSingleton = true;
parameter.singleton = parameter.enum[0];
}
parameter.paramType = parameter.in;
if(parameter.in === 'body'){
parameter.isBodyParameter = true;
method.hasBody = true;
} else if(parameter.in === 'path'){
parameter.isPathParameter = true;
} else if(parameter.in === 'query'){
Expand All @@ -128,6 +154,7 @@ var getViewForSwagger2 = function(opts, type){
parameter.isHeaderParameter = true;
} else if(parameter.in === 'formData'){
parameter.isFormParameter = true;
method.hasForm = true;
}
parameter.tsType = ts.convertType(parameter);
parameter.cardinality = parameter.required ? '' : '?';
Expand All @@ -137,11 +164,10 @@ var getViewForSwagger2 = function(opts, type){
});
});

_.forEach(swagger.definitions, function(definition, name){
data.definitions.push({
name: name,
tsType: ts.convertType(definition)
});
_.forEach(swagger.definitions, function(definition, name) {
var type = ts.convertType(definition);
type.name = name.charAt(0).toUpperCase() + name.substring(1);
data.definitions.push(type);
});

return data;
Expand All @@ -159,6 +185,8 @@ var getViewForSwagger1 = function(opts, type){
};
swagger.apis.forEach(function(api){
api.operations.forEach(function(op){
if (op.method == 'OPTIONS') return;

var method = {
path: api.path,
className: opts.className,
Expand Down Expand Up @@ -210,23 +238,65 @@ var getViewForSwagger1 = function(opts, type){
return data;
};

var fileExists = function(filePath) {
try {
return fs.statSync(filePath).isFile();
} catch (err) {
return false;
}
};

/**
* @param path eg: __dirname + '/../templates/'
* @param language eg: 'typescript', 'coffeescript'
* @param framework eg: 'angular', 'angular2', 'react', 'polymer'
* @param suffix eg: 'class.mustache'
*/
var locateTemplate = function(path, language, framework, suffix) {
if (language && framework && fileExists(path + language + '-' + framework + '-' + suffix)) {
return path + language + '-' + framework + '-' + suffix;
}
if (language && fileExists(path + language + '-' + suffix)) {
return path + language + '-' + suffix;
}
if (framework && fileExists(path + framework + '-' + suffix)) {
return path + framework + '-' + suffix;
}
return path + suffix;
};

var readTemplate = function(path, language, framework, suffix) {
return fs.readFileSync(locateTemplate(path, language, framework, suffix), 'utf-8');
};

/**
* @param {{ template?: {}, framework?: string, language?: string }} opts
* @param type - 'typescript', 'angular', 'node'
*/
var selectTemplates = function(opts, type) {
if (!_.isObject(opts.template)) {
opts.template = {};
}
var templates = __dirname + '/../templates/';
var language = opts.language || (type === 'typescript' ? type : undefined);
var framework = opts.framework || (type !== 'typescript' ? type : undefined);

opts.template.class = opts.template.class || readTemplate(templates, language, framework, 'class.mustache');
opts.template.method = opts.template.method || readTemplate(templates, language, framework, 'method.mustache');
if(type === 'typescript') {
opts.template.type = readTemplate(templates, language, framework, 'type.mustache');
}
};

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: "..." }');
throw new Error('Unprovided custom template. Please use the following template: template: { class: "...", method: "..." }');
}
} 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 + (type === 'typescript' ? 'typescript-' : '') + 'method.mustache', 'utf-8');
if(type === 'typescript') {
opts.template.type = opts.template.type || fs.readFileSync(templates + 'type.mustache', 'utf-8');
}
selectTemplates(opts, type);
}

if (opts.mustache) {
Expand All @@ -247,7 +317,7 @@ var getCode = function(opts, type) {
lintOptions.esnext = true;
}

if(type === 'typescript') {
if (type === 'typescript') {
opts.lint = false;
}

Expand Down
38 changes: 31 additions & 7 deletions lib/typescript.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,58 @@ var _ = require('lodash');
function convertType(swaggerType) {

var typespec = {};
if (swaggerType.hasOwnProperty('allOf')) {
typespec.isIntersection = true;
typespec.items = swaggerType.allOf.map(function(item) {
// join with '&' and avoid 'RangeError: Maximum call stack size exceeded'
return Object.assign(convertType(item), {hasPrefix: true, isIntersection: false});
});
typespec.items[0].hasPrefix = false;
typespec.tsType = 'intersection';

if (swaggerType.hasOwnProperty('schema')) {
} else if (swaggerType.hasOwnProperty('schema')) {
return convertType(swaggerType.schema);
} else if (_.isString(swaggerType.$ref)) {
typespec.tsType = 'ref';
typespec.target = swaggerType.$ref.substring(swaggerType.$ref.lastIndexOf('/') + 1);
} else if (swaggerType.type === 'array') {
if (swaggerType.in === 'query' && swaggerType.collectionFormat !== 'multi') {
// arrays in query parameters are merged by csv, ssv, tsv or pipes
typespec.tsType = 'string';
if (swaggerType.hasOwnProperty('enum')) {
// doesn't affect the compiler, but useful for documentation
typespec.tsType += ' | ' + swaggerType.enum.map(function (str) {
return typeof str == 'string' ? '\'' + str + '\'' : JSON.stringify(str);
}).join(' | ');
typespec.isAtomic = true;
}
} else {
typespec.tsType = 'array';
}
typespec.elementType = convertType(swaggerType.items);
} else if (swaggerType.hasOwnProperty('enum')) {
typespec.tsType = swaggerType.enum.map(function(str) { return JSON.stringify(str); }).join(' | ');
typespec.tsType = swaggerType.enum.map(function(str) {
return typeof str == 'string' ? '\'' + str + '\'' : JSON.stringify(str);
}).join(' | ');
typespec.isAtomic = true;
} else if (swaggerType.type === 'string') {
typespec.tsType = 'string';
} else if (swaggerType.type === 'number' || swaggerType.type === 'integer') {
typespec.tsType = 'number';
} else if (swaggerType.type === 'boolean') {
typespec.tsType = 'boolean';
} else if (swaggerType.type === 'array') {
typespec.tsType = 'array';
typespec.elementType = convertType(swaggerType.items);
} else if (swaggerType.type === 'object') {
typespec.tsType = 'object';
typespec.properties = [];

_.forEach(swaggerType.properties, function (propertyType, propertyName) {
var property = convertType(propertyType);
property.name = propertyName;
property.isOptional = !(swaggerType.required && _.includes(swaggerType.required, propertyName));
typespec.properties.push(property);
});
} else {
// type unknown or unsupported... just map to 'any'...
// TODO: probably void
typespec.tsType = 'any';
}

Expand All @@ -51,9 +74,10 @@ function convertType(swaggerType) {
typespec.isObject = typespec.tsType === 'object';
typespec.isArray = typespec.tsType === 'array';
typespec.isAtomic = typespec.isAtomic || _.includes(['string', 'number', 'boolean', 'any'], typespec.tsType);
typespec.isOptional = !swaggerType.required;
typespec.description = swaggerType.description;

return typespec;

}

module.exports.convertType = convertType;
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "swagger-js-codegen",
"main": "./lib/codegen.js",
"version": "1.7.12",
"version": "1.7.15-c",
"description": "A Swagger codegen for JavaScript",
"scripts": {
"test": "grunt",
Expand Down
13 changes: 7 additions & 6 deletions templates/type.mustache
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{{#tsType}}
{{! must use different delimiters to avoid ambiguities when delimiters directly follow a literal brace {. }}
{{=<% %>=}}
<%#isRef%><%target%><%/isRef%><%!
<%#isIntersection%><%#items%><%#hasPrefix%>&<%/hasPrefix%><%>type%><%/items%><%/isIntersection%><%!
%><%#isRef%><%target%><%/isRef%><%!
%><%#isAtomic%><%&tsType%><%/isAtomic%><%!
%><%#isObject%>{<%#properties%>
'<%name%>': <%>type%><%/properties%>
%><%#isObject%>{<%#properties%><%#description%>/** <%description%> */<%/description%>
<%name%><%#isOptional%>?<%/isOptional%>: <%>type%>;<%/properties%>
}<%/isObject%><%!
%><%#isArray%>Array<<%#elementType%><%>type%><%/elementType%>>|<%#elementType%><%>type%><%/elementType%><%/isArray%>
<%={{ }}=%>
{{/tsType}}
%><%#isArray%>Array<<%#elementType%><%>type%><%/elementType%>><%/isArray%><%!
%><%#isBoolean%>boolean<%/isBoolean%><%!
%><%={{ }}=%>{{/tsType}}
45 changes: 45 additions & 0 deletions templates/typescript-angular-class.mustache
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* tslint:disable */
import * as angular from 'angular';

export module {{moduleName}} {

{{#definitions}}
{{#description}}/** {{description}} */{{/description}}
export type {{&name}} = {{#tsType}}{{> type}}{{/tsType}};

{{/definitions}}

/**
* {{&description}}
* @class {{&className}}
* @param {ng.IHttpService} $http
* @param {ng.IQService} $q
* @param {string} domain - The project domain.
* provide using <code>.constant('domain', '//example.com')</code> or <code>.factory('domain', function(){return '//example.com'})</code>
*/
export class {{&className}} {
static $inject = ['$http', '$q', 'domain'];

constructor(private $http: ng.IHttpService, private $q: ng.IQService, private domain: string) {}

{{#methods}}
{{> method}}

{{/methods}}

private static transformRequest(obj: any): string {
var str = [];
for(var p in obj) {
var val = obj[p];
if(angular.isArray(val)) {
val.forEach(function(val){
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(val));
});
} else {
str.push(encodeURIComponent(p) + '=' + encodeURIComponent(val));
}
}
return str.join('&');
}
}
}
Loading