swagger2openapi
Advanced tools
Comparing version 1.1.0 to 1.1.1
202
index.js
'use strict'; | ||
var crypto = require('crypto'); | ||
var jptr = require('jgexml/jpath.js'); | ||
// TODO split out into common, params, security etc | ||
function clone(obj) { | ||
@@ -11,2 +17,14 @@ return JSON.parse(JSON.stringify(obj)); | ||
function sha1(s) { | ||
var shasum = crypto.createHash('sha1'); | ||
shasum.update(s); | ||
return shasum.digest('hex'); | ||
} | ||
String.prototype.toCamelCase = function camelize() { | ||
return this.toLowerCase().replace(/[-_ \/\.](.)/g, function(match, group1) { | ||
return group1.toUpperCase(); | ||
}); | ||
} | ||
function deleteParameters(value, index, self) { | ||
@@ -30,2 +48,9 @@ return !value["x-s2o-delete"]; | ||
function processSecurityScheme(scheme) { | ||
if (scheme.type == 'oauth2') { | ||
if (scheme.flow == 'application') scheme.flow = 'clientCredentials'; | ||
if (scheme.flow == 'accessCode') scheme.flow = 'authorizationCode'; | ||
} | ||
} | ||
function processParameter(param,op,path,index,openapi) { | ||
@@ -38,34 +63,21 @@ var result = {}; | ||
var ptr = param["$ref"].replace('#/components/parameters/',''); | ||
var rbody = false; | ||
var target = openapi.components.parameters[ptr]; | ||
if ((!target) || (target["x-s2o-delete"])) { | ||
// it's gone, chances are it's a requestBody now unless spec was broken | ||
// OR external ref - not supported yet | ||
param["x-s2o-delete"] = true; | ||
rbody = true; | ||
} | ||
// shared formData parameters could be used in any combination. We could sort and | ||
// hash them into unique combinations, or alternatively lump them all into one bucket | ||
// and ensure they're not required:true if they shouldn't be TODO | ||
// shared formData parameters from swagger or path level could be used in any combination. | ||
// probably best is to make all op.requestBody's unique then hash them and pull out | ||
// any common ones afterwards // TODO | ||
if (op) { | ||
if (!op.requestBodies) op.requestBodies = {}; | ||
if (ptr) { | ||
op.requestBodies[ptr] = {}; | ||
op.requestBodies[ptr]["$ref"] = '#/components/requestBodies/'+ptr; | ||
} | ||
else { | ||
forceFailure(openapi,'Have lost a shared parameter now requestBody'); | ||
} | ||
if (rbody) { | ||
param = jptr.jptr(openapi,param["$ref"]); | ||
} | ||
else if (path) { | ||
if (!path.requestBodies) path.requestBodies = {}; | ||
if (ptr) { | ||
path.requestBodies[ptr] = {}; | ||
path.requestBodies[ptr]["$ref"] = '#/components/requestBodies/'+ptr; | ||
} | ||
else { | ||
forceFailure(openapi,'Have lost a shared parameter now requestBody'); | ||
} | ||
} | ||
} | ||
else { | ||
if (param.type || param.in) { // if it's a real parameter OR we've dereferenced it | ||
if (param.schema) { | ||
@@ -84,2 +96,6 @@ recurse(param.schema,{},function(obj,key,parent){ | ||
} | ||
if (key == 'x-not') { | ||
obj.not = obj[key]; | ||
delete obj[key]; | ||
} | ||
}); | ||
@@ -101,3 +117,3 @@ } | ||
if (param.in == 'formData') { | ||
// convert to requestBody | ||
// convert to requestBody component | ||
singularRequestBody = false; | ||
@@ -115,4 +131,22 @@ result.content = {}; | ||
target.type = param.type; | ||
if (param.format) target.format = param.format; | ||
// TODO min/max/exclusive/minItems etc etc | ||
target.required = param.required; | ||
target.default = param.default; | ||
target.format = param.format; | ||
target.minimum = param.minimum; | ||
target.maximum = param.maximum; | ||
target.exclusiveMinimum = param.exclusiveMinimum; | ||
target.exclusiveMaximum = param.exclusiveMaximum; | ||
target.minItems = param.minItems; | ||
target.maxItems = param.maxItems; | ||
target.uniqueItems = param.uniqueItems; | ||
target.pattern = param.pattern; | ||
target.enum = param.enum; | ||
target.multipleOf = param.multipleOf; | ||
target.minLength = param.minLength; | ||
target.maxLength = param.maxLength; | ||
target.properties = param.properties; | ||
target.minProperties = param.minProperties; | ||
target.maxProperties = param.maxProperties; | ||
target.additionalProperties = param.additionalProperties; | ||
target.allOf = param.allOf; // new are anyOf, oneOf, not | ||
if ((param.type == 'array') && (param.items)) { | ||
@@ -136,2 +170,6 @@ target.items = param.items; | ||
if (consumes.length == 0) { | ||
consumes.push('application/json'); // default as per section xxx | ||
} | ||
for (var mimetype of consumes) { | ||
@@ -142,7 +180,6 @@ result.content[mimetype] = {}; | ||
} | ||
//if (consumes.length>1) { | ||
// forceFailure(openapi,'Body mimetype may not be correct. '+consumes.length); | ||
//} | ||
} | ||
// TODO multipart/formData etc | ||
if (Object.keys(result).length > 0) { | ||
@@ -169,16 +206,19 @@ param["x-s2o-delete"] = true; | ||
else if (path) { | ||
if (path.requestBody && singularRequestBody) { | ||
path.requestBody["x-s2o-overloaded"] = true; | ||
forceFailure(openapi,'Path has >1 requestBodies'); | ||
var uniqueName = index ? index.toCamelCase()+'RequestBodyBase' : param.name; | ||
if (!index) { | ||
forceFailure(openapi,'Named requestBody needs name'); | ||
} | ||
else { | ||
path.requestBody = Object.assign({},op.requestBody,result); | ||
if (param.in == 'formData') { | ||
result["x-s2o-partial"] = true; | ||
} | ||
openapi.components.requestBodies[uniqueName] = result; | ||
} | ||
else { | ||
var uniqueName = index; | ||
var uniqueName = index ? index : param.name; | ||
if (!index) { | ||
forceFailure(openapi,'Named requestBody needs name'); | ||
uniqueName = param.name; | ||
} | ||
if (param.in == 'formData') { | ||
result["x-s2o-partial"] = true; | ||
} | ||
openapi.components.requestBodies[uniqueName] = result; | ||
@@ -192,2 +232,4 @@ } | ||
function convert(swagger, options) { | ||
var requestBodyCache = {}; | ||
var openapi = {}; | ||
@@ -209,3 +251,2 @@ openapi.openapi = '3.0.0-RC0'; // semver | ||
server.url = server.url.split('}}').join('}'); | ||
// TODO if we have non-standard path variables here expand them using a regex | ||
openapi.servers.push(server); | ||
@@ -229,3 +270,3 @@ } | ||
openapi.components.requestBodies = {}; | ||
openapi.components.securitySchemes = openapi.securityDefinitions; | ||
openapi.components.securitySchemes = openapi.securityDefinitions||{}; | ||
openapi.components.headers = {}; | ||
@@ -238,2 +279,6 @@ delete openapi.definitions; | ||
for (var s in openapi.components.securitySchemes) { | ||
processSecurityScheme(openapi.components.securitySchemes[s]); | ||
} | ||
for (var p in openapi.components.parameters) { | ||
@@ -243,2 +288,12 @@ var param = openapi.components.parameters[p]; | ||
} | ||
for (var r in openapi.components.requestBodies) { // converted ones | ||
var rb = openapi.components.requestBodies[r]; | ||
var rbStr = JSON.stringify(rb); | ||
var rbSha1 = sha1(rbStr); | ||
var entry = {}; | ||
entry.name = r; | ||
entry.body = rb; | ||
entry.refs = []; | ||
requestBodyCache[rbSha1] = entry; | ||
} | ||
@@ -266,6 +321,3 @@ for (var p in openapi.paths) { | ||
// remove requestBody for non-supported ops? SHALL be ignored | ||
//if (op.requestBody && method != 'put' && method != 'post') { | ||
// forceFailure(openapi,'requestBody on get style operation'); | ||
//} | ||
//don't need to remove requestBody for non-supported ops "SHALL be ignored" | ||
@@ -278,3 +330,3 @@ // responses | ||
if ((key == '$ref') && (typeof obj[key] === 'string')) { | ||
obj[key] = obj[key].replace('#/definitions/','#/components/schemas'); | ||
obj[key] = obj[key].replace('#/definitions/','#/components/schemas/'); | ||
} | ||
@@ -297,12 +349,30 @@ if (key == 'x-anyOf') { | ||
// examples | ||
if (options.debug) { | ||
op["x-s2o-consumes"] = op.consumes; | ||
op["x-s2o-produces"] = op.produces; | ||
} | ||
delete op.consumes; | ||
delete op.produces; | ||
// file types | ||
// TODO examples | ||
if (op.requestBody) { | ||
var rbStr = JSON.stringify(op.requestBody); | ||
var rbSha1 = sha1(rbStr); | ||
if (!requestBodyCache[rbSha1]) { | ||
var entry = {}; | ||
entry.name = ''; | ||
entry.body = op.requestBody; | ||
entry.refs = []; | ||
requestBodyCache[rbSha1] = entry; | ||
} | ||
requestBodyCache[rbSha1].refs.push(method+' '+p); | ||
} | ||
} | ||
} | ||
if (path.parameters) { | ||
for (var p in path.parameters) { | ||
var param = path.parameters[p]; | ||
processParameter(param,null,path,null,openapi); | ||
for (var p2 in path.parameters) { | ||
var param = path.parameters[p2]; | ||
processParameter(param,null,path,p,openapi); // index here is the path string | ||
} | ||
@@ -316,7 +386,14 @@ if (!options.debug) { | ||
// security changes (oAuth) | ||
if (!options.debug) { | ||
for (var p in openapi.components.parameters) { | ||
param = openapi.components.parameters[p]; | ||
if (param["x-s2o-delete"]) { | ||
delete openapi.components.parameters[p]; | ||
} | ||
} | ||
} | ||
recurse(openapi.components.schemas,{},function(obj,key,parent){ | ||
if ((key == '$ref') && (typeof obj[key] === 'string')) { | ||
obj[key] = obj[key].replace('#/definitions/','#/components/schemas'); | ||
obj[key] = obj[key].replace('#/definitions/','#/components/schemas/'); | ||
} | ||
@@ -333,7 +410,28 @@ if (key == 'x-anyOf') { | ||
if (options.debug) openapi["x-s2o-consumes"] = openapi.consumes; | ||
if (options.debug) { | ||
openapi["x-s2o-consumes"] = openapi.consumes; | ||
openapi["x-s2o-produces"] = openapi.produces; | ||
} | ||
delete openapi.consumes; | ||
if (options.debug) openapi["x-s2o-produces"] = openapi.produces; | ||
delete openapi.produces; | ||
openapi.components.requestBodies = {}; // for now as we've dereffed them | ||
var counter = 0; | ||
for (var e in requestBodyCache) { | ||
var entry = requestBodyCache[e]; | ||
if (entry.refs.length>1) { | ||
if (!entry.name) { | ||
entry.name = 'requestBody'+counter++; | ||
} | ||
// we can reinstate | ||
openapi.components.requestBodies[entry.name] = entry.body; | ||
for (var r in entry.refs) { | ||
var address = entry.refs[r].split(' '); | ||
var ref = {}; | ||
ref["$ref"] = '#/components/requestBodies/'+entry.name; | ||
openapi.paths[address[1]][address[0]].requestBody = ref; | ||
} | ||
} | ||
} | ||
return openapi; | ||
@@ -340,0 +438,0 @@ } |
{ | ||
"name": "swagger2openapi", | ||
"version": "1.1.0", | ||
"version": "1.1.1", | ||
"description": "Convert Swagger 2.0 specifications to OpenApi 3.0", | ||
"main": "index.js", | ||
"bin": { | ||
"swagger2openapi": "./swagger2openapi.js" | ||
}, | ||
"repository": { | ||
@@ -14,5 +17,7 @@ "url": "https://github.com/Mermade/swagger2openapi.git", | ||
"ajv": "^4.11.3", | ||
"jgexml": "^0.3.6", | ||
"js-yaml": "^3.6.1", | ||
"recursive-readdir": "^2.1.1", | ||
"yargs": "^6.2.0" | ||
} | ||
} |
@@ -23,3 +23,4 @@ # swagger2openapi | ||
var options = {}; | ||
//options.debug = true; // sets various x-s2o- properties for debugging | ||
var openapi = converter.convert(swagger, options); | ||
```` |
@@ -0,1 +1,3 @@ | ||
#!/usr/bin/env node | ||
'use strict'; | ||
@@ -2,0 +4,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
32735
527
26
0
5
+ Addedjgexml@^0.3.6
+ Addedrecursive-readdir@^2.1.1
+ Addedbalanced-match@1.0.2(transitive)
+ Addedbrace-expansion@1.1.11(transitive)
+ Addedconcat-map@0.0.1(transitive)
+ Addedjgexml@0.3.9(transitive)
+ Addedminimatch@3.1.2(transitive)
+ Addedrecursive-readdir@2.2.3(transitive)