swagger2openapi
Advanced tools
Comparing version 3.2.14 to 4.0.0
348
index.js
@@ -9,3 +9,3 @@ // @ts-check | ||
const maybe = require('call-me-maybe'); | ||
const fetch = require('node-fetch'); | ||
const fetch = require('node-fetch-h2'); | ||
const yaml = require('js-yaml'); | ||
@@ -32,4 +32,11 @@ | ||
class S2OError extends Error { | ||
constructor(message) { | ||
super(message); | ||
this.name = 'S2OError'; | ||
} | ||
} | ||
function throwError(message, options) { | ||
let err = new Error(message); | ||
let err = new S2OError(message); | ||
err.options = options; | ||
@@ -197,2 +204,12 @@ if (options.promise) { | ||
} | ||
else if (obj[key] === '#/consumes') { | ||
// people are *so* creative | ||
delete obj[key]; | ||
state.parent[state.pkey] = clone(options.openapi.consumes); | ||
} | ||
else if (obj[key] === '#/produces') { | ||
// and by creative, I mean devious | ||
delete obj[key]; | ||
state.parent[state.pkey] = clone(options.openapi.produces); | ||
} | ||
else if (obj[key].startsWith('#/definitions/')) { | ||
@@ -276,4 +293,12 @@ //only the first part of a schema component name must be sanitised | ||
} | ||
delete obj['x-miro']; | ||
// do this last | ||
if (Object.keys(obj).length > 1) { | ||
let tmp = obj[key]; | ||
delete obj[key]; | ||
state.parent[state.pkey] = { allOf: [ { $ref: tmp }, obj ]}; | ||
} | ||
} | ||
delete obj['x-miro']; | ||
if ((key === 'x-ms-odata') && (typeof obj[key] === 'string') && (obj[key].startsWith('#/'))) { | ||
@@ -325,3 +350,3 @@ let keys = obj[key].replace('#/definitions/', '').replace('#/components/schemas/','').split('/'); | ||
if (typeof scheme.authorizationUrl !== 'undefined') flow.authorizationUrl = scheme.authorizationUrl.split('?')[0].trim() || '/'; | ||
if (typeof scheme.tokenUrl !== 'undefined') flow.tokenUrl = scheme.tokenUrl.split('?')[0].trim() || '/'; | ||
if (typeof scheme.tokenUrl === 'string') flow.tokenUrl = scheme.tokenUrl.split('?')[0].trim() || '/'; | ||
flow.scopes = scheme.scopes || {}; | ||
@@ -345,4 +370,4 @@ scheme.flows = {}; | ||
function deleteParameters(value) { | ||
return !value["x-s2o-delete"]; | ||
function keepParameters(value) { | ||
return (value && !value["x-s2o-delete"]); | ||
} | ||
@@ -426,6 +451,16 @@ | ||
let singularRequestBody = true; | ||
let originalType; | ||
let consumes = ((op && op.consumes) || (openapi.consumes || [])).filter(common.uniqueOnly); | ||
if (op && op.consumes && (typeof op.consumes === 'string')) { | ||
if (options.patch) { | ||
op.consumes = [op.consumes]; | ||
} | ||
else { | ||
return throwError('(Patchable) operation.consumes must be an array', options); | ||
} | ||
} | ||
if (!Array.isArray(openapi.consumes)) delete openapi.consumes; | ||
let consumes = ((op ? op.consumes : null) || (openapi.consumes || [])).filter(common.uniqueOnly); | ||
if (param.$ref && (typeof param.$ref === 'string')) { | ||
if (param && param.$ref && (typeof param.$ref === 'string')) { | ||
// if we still have a ref here, it must be an internal one | ||
@@ -458,4 +493,5 @@ fixParamRef(param, options); | ||
if (param.name || param.in) { // if it's a real parameter OR we've dereferenced it | ||
if (param && (param.name || param.in)) { // if it's a real parameter OR we've dereferenced it | ||
if (typeof param['x-deprecated'] === 'boolean') { | ||
@@ -483,2 +519,3 @@ param.deprecated = param['x-deprecated']; | ||
} | ||
originalType = param.type; | ||
if (param.description && typeof param.description === 'object' && param.description.$ref) { | ||
@@ -573,3 +610,3 @@ // $ref anywhere sensibility | ||
if (param.in === 'formData') { | ||
if (param && param.in === 'formData') { | ||
// convert to requestBody component | ||
@@ -610,3 +647,3 @@ singularRequestBody = false; | ||
if (target.properties) target.properties = param.properties; | ||
if (param.allOf) target.allOf = param.allOf; // new are anyOf, oneOf, not, x- vendor extensions? | ||
if (param.allOf) target.allOf = param.allOf; // new are anyOf, oneOf, not | ||
if ((param.type === 'array') && (param.items)) { | ||
@@ -616,9 +653,12 @@ target.items = param.items; | ||
} | ||
if (param.type === 'file') { | ||
if (originalType === 'file') { | ||
target.type = 'string'; | ||
target.format = 'binary'; | ||
} | ||
// Copy any extensions on the form param to the target schema property. | ||
copyExtensions(param, target); | ||
} | ||
} | ||
else if (param.type === 'file') { | ||
else if (param && param.type === 'file') { | ||
// convert to requestBody | ||
@@ -631,4 +671,5 @@ if (param.required) result.required = param.required; | ||
result.content["application/octet-stream"].schema.format = 'binary'; | ||
copyExtensions(param, result); | ||
} | ||
if (param.in === 'body') { | ||
if (param && param.in === 'body') { | ||
result.content = {}; | ||
@@ -639,2 +680,6 @@ if (param.name) result['x-s2o-name'] = (op && op.operationId ? common.sanitiseAll(op.operationId) : '') + ('_' + param.name).toCamelCase(); | ||
// Set the "request body name" extension on the operation if requested. | ||
if (op && options.rbname && param.name) { | ||
op[options.rbname] = param.name; | ||
} | ||
if (param.schema && param.schema.$ref) { | ||
@@ -653,5 +698,8 @@ result['x-s2o-name'] = decodeURIComponent(param.schema.$ref.replace('#/components/schemas/', '')); | ||
result.content[mimetype] = {}; | ||
result.content[mimetype].schema = clone(param.schema) || {}; | ||
result.content[mimetype].schema = clone(param.schema || {}); | ||
fixUpSchema(result.content[mimetype].schema,options); | ||
} | ||
// Copy any extensions from the original parameter to the new requestBody | ||
copyExtensions(param, result); | ||
} | ||
@@ -672,3 +720,3 @@ | ||
if ((op.requestBody.content && op.requestBody.content["multipart/form-data"]) | ||
&& (result.content["multipart/form-data"])) { | ||
&& (op.requestBody.content["multipart/form-data"].schema) && (op.requestBody.content["multipart/form-data"].schema.properties) && (result.content["multipart/form-data"]) && (result.content["multipart/form-data"].schema) && (result.content["multipart/form-data"].schema.properties)) { | ||
op.requestBody.content["multipart/form-data"].schema.properties = | ||
@@ -681,4 +729,4 @@ Object.assign(op.requestBody.content["multipart/form-data"].schema.properties, result.content["multipart/form-data"].schema.properties); | ||
} | ||
else if ((op.requestBody.content && op.requestBody.content["application/x-www-form-urlencoded"]) | ||
&& (result.content["application/x-www-form-urlencoded"])) { | ||
else if ((op.requestBody.content && op.requestBody.content["application/x-www-form-urlencoded"] && op.requestBody.content["application/x-www-form-urlencoded"].schema && op.requestBody.content["application/x-www-form-urlencoded"].schema.properties) | ||
&& result.content["application/x-www-form-urlencoded"] && result.content["application/x-www-form-urlencoded"].schema && result.content["application/x-www-form-urlencoded"].schema.properties) { | ||
op.requestBody.content["application/x-www-form-urlencoded"].schema.properties = | ||
@@ -707,14 +755,16 @@ Object.assign(op.requestBody.content["application/x-www-form-urlencoded"].schema.properties, result.content["application/x-www-form-urlencoded"].schema.properties); | ||
// tidy up | ||
delete param.type; | ||
for (let prop of common.parameterTypeProperties) { | ||
delete param[prop]; | ||
} | ||
if (param) { | ||
delete param.type; | ||
for (let prop of common.parameterTypeProperties) { | ||
delete param[prop]; | ||
} | ||
if ((param.in === 'path') && ((typeof param.required === 'undefined') || (param.required !== true))) { | ||
if (options.patch) { | ||
param.required = true; | ||
if ((param.in === 'path') && ((typeof param.required === 'undefined') || (param.required !== true))) { | ||
if (options.patch) { | ||
param.required = true; | ||
} | ||
else { | ||
throwError('(Patchable) path parameters must be required:true', options); | ||
} | ||
} | ||
else { | ||
throwError('(Patchable) path parameters must be required:true', options); | ||
} | ||
} | ||
@@ -725,3 +775,12 @@ | ||
function copyExtensions(src, tgt) { | ||
for (let prop in src) { | ||
if (prop.startsWith('x-') && !prop.startsWith('x-s2o')) { | ||
tgt[prop] = src[prop]; | ||
} | ||
} | ||
} | ||
function processResponse(response, name, op, openapi, options) { | ||
if (!response) return false; | ||
if (response.$ref && (typeof response.$ref === 'string')) { | ||
@@ -742,7 +801,4 @@ if (response.$ref.indexOf('#/definitions/') >= 0) { | ||
if (options.patch) { | ||
let sc = statusCodes.find(function (e) { | ||
return e.code === name; | ||
}); | ||
if ((typeof response === 'object') && (!Array.isArray(response))) { | ||
response.description = (sc ? sc.phrase : ''); | ||
response.description = (statusCodes[response] || ''); | ||
} | ||
@@ -762,3 +818,13 @@ } | ||
let produces = ((op && op.produces) || (openapi.produces || [])).filter(common.uniqueOnly); | ||
if (op && op.produces && (typeof op.produces === 'string')) { | ||
if (options.patch) { | ||
op.produces = [op.produces]; | ||
} | ||
else { | ||
return throwError('(Patchable) operation.produces must be an array', options); | ||
} | ||
} | ||
if (openapi.produces && !Array.isArray(openapi.produces)) delete openapi.produces; | ||
let produces = ((op ? op.produces : null) || (openapi.produces || [])).filter(common.uniqueOnly); | ||
if (!produces.length) produces.push('*/*'); // TODO verify default | ||
@@ -815,15 +881,15 @@ | ||
// path.$ref is external only | ||
if ((path['x-trace']) && (typeof path['x-trace'] === 'object')) { | ||
if (path && (path['x-trace']) && (typeof path['x-trace'] === 'object')) { | ||
path.trace = path['x-trace']; | ||
delete path['x-trace']; | ||
} | ||
if ((path['x-summary']) && (typeof path['x-summary'] === 'string')) { | ||
if (path && (path['x-summary']) && (typeof path['x-summary'] === 'string')) { | ||
path.summary = path['x-summary']; | ||
delete path['x-summary']; | ||
} | ||
if ((path['x-description']) && (typeof path['x-description'] === 'string')) { | ||
if (path && (path['x-description']) && (typeof path['x-description'] === 'string')) { | ||
path.description = path['x-description']; | ||
delete path['x-description']; | ||
} | ||
if ((path['x-servers']) && (Array.isArray(path['x-servers']))) { | ||
if (path && (path['x-servers']) && (Array.isArray(path['x-servers']))) { | ||
path.servers = path['x-servers']; | ||
@@ -836,3 +902,3 @@ delete path['x-servers']; | ||
if (op.parameters && Array.isArray(op.parameters)) { | ||
if (op && op.parameters && Array.isArray(op.parameters)) { | ||
if (path.parameters) { | ||
@@ -857,8 +923,7 @@ for (let param of path.parameters) { | ||
if (!options.debug) { | ||
op.parameters = op.parameters.filter(deleteParameters); | ||
op.parameters = op.parameters.filter(keepParameters); | ||
} | ||
} | ||
if (op.parameters === null) delete op.parameters; | ||
if (op.security) processSecurity(op.security); | ||
if (op && op.security) processSecurity(op.security); | ||
@@ -868,16 +933,18 @@ //don't need to remove requestBody for non-supported ops as they "SHALL be ignored" | ||
// responses | ||
if (!op.responses) { | ||
let defaultResp = {}; | ||
defaultResp.description = 'Default response'; | ||
op.responses = { default: defaultResp }; | ||
if (typeof op === 'object') { | ||
if (!op.responses) { | ||
let defaultResp = {}; | ||
defaultResp.description = 'Default response'; | ||
op.responses = { default: defaultResp }; | ||
} | ||
for (let r in op.responses) { | ||
let response = op.responses[r]; | ||
processResponse(response, r, op, openapi, options); | ||
} | ||
} | ||
for (let r in op.responses) { | ||
let response = op.responses[r]; | ||
processResponse(response, r, op, openapi, options); | ||
} | ||
if ((op['x-servers']) && (Array.isArray(op['x-servers']))) { | ||
if (op && (op['x-servers']) && (Array.isArray(op['x-servers']))) { | ||
op.servers = op['x-servers']; | ||
delete op['x-servers']; | ||
} else if (op.schemes && op.schemes.length) { | ||
} else if (op && op.schemes && op.schemes.length) { | ||
for (let scheme of op.schemes) { | ||
@@ -888,8 +955,10 @@ if ((!openapi.schemes) || (openapi.schemes.indexOf(scheme) < 0)) { | ||
} | ||
for (let server of openapi.servers) { | ||
let newServer = clone(server); | ||
let serverUrl = url.parse(newServer.url); | ||
serverUrl.protocol = scheme; | ||
newServer.url = serverUrl.format(); | ||
op.servers.push(newServer); | ||
if (Array.isArray(openapi.servers)) { | ||
for (let server of openapi.servers) { | ||
let newServer = clone(server); | ||
let serverUrl = url.parse(newServer.url); | ||
serverUrl.protocol = scheme; | ||
newServer.url = serverUrl.format(); | ||
op.servers.push(newServer); | ||
} | ||
} | ||
@@ -904,75 +973,76 @@ } | ||
} | ||
delete op.consumes; | ||
delete op.produces; | ||
delete op.schemes; | ||
if (op) { | ||
delete op.consumes; | ||
delete op.produces; | ||
delete op.schemes; | ||
if (op["x-ms-examples"]) { | ||
for (let e in op["x-ms-examples"]) { | ||
let example = op["x-ms-examples"][e]; | ||
let se = common.sanitiseAll(e); | ||
if (example.parameters) { | ||
for (let p in example.parameters) { | ||
let value = example.parameters[p]; | ||
for (let param of (op.parameters||[]).concat(path.parameters||[])) { | ||
if (param.$ref) { | ||
param = jptr.jptr(openapi,param.$ref); | ||
} | ||
if ((param.name === p) && (!param.example)) { | ||
if (!param.examples) { | ||
param.examples = {}; | ||
if (op["x-ms-examples"]) { | ||
for (let e in op["x-ms-examples"]) { | ||
let example = op["x-ms-examples"][e]; | ||
let se = common.sanitiseAll(e); | ||
if (example.parameters) { | ||
for (let p in example.parameters) { | ||
let value = example.parameters[p]; | ||
for (let param of (op.parameters||[]).concat(path.parameters||[])) { | ||
if (param.$ref) { | ||
param = jptr.jptr(openapi,param.$ref); | ||
} | ||
param.examples[e] = {value: value}; | ||
if ((param.name === p) && (!param.example)) { | ||
if (!param.examples) { | ||
param.examples = {}; | ||
} | ||
param.examples[e] = {value: value}; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
if (example.responses) { | ||
for (let r in example.responses) { | ||
if (example.responses[r].headers) { | ||
for (let h in example.responses[r].headers) { | ||
let value = example.responses[r].headers[h]; | ||
for (let rh in op.responses[r].headers) { | ||
if (rh === h) { | ||
let header = op.responses[r].headers[rh]; | ||
header.example = value; | ||
if (example.responses) { | ||
for (let r in example.responses) { | ||
if (example.responses[r].headers) { | ||
for (let h in example.responses[r].headers) { | ||
let value = example.responses[r].headers[h]; | ||
for (let rh in op.responses[r].headers) { | ||
if (rh === h) { | ||
let header = op.responses[r].headers[rh]; | ||
header.example = value; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
if (example.responses[r].body) { | ||
openapi.components.examples[se] = { value: clone(example.responses[r].body) }; | ||
if (op.responses[r] && op.responses[r].content) { | ||
for (let ct in op.responses[r].content) { | ||
let contentType = op.responses[r].content[ct]; | ||
if (!contentType.examples) { | ||
contentType.examples = {}; | ||
if (example.responses[r].body) { | ||
openapi.components.examples[se] = { value: clone(example.responses[r].body) }; | ||
if (op.responses[r] && op.responses[r].content) { | ||
for (let ct in op.responses[r].content) { | ||
let contentType = op.responses[r].content[ct]; | ||
if (!contentType.examples) { | ||
contentType.examples = {}; | ||
} | ||
contentType.examples[e] = { $ref: '#/components/examples/'+se }; | ||
} | ||
contentType.examples[e] = { $ref: '#/components/examples/'+se }; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
delete op["x-ms-examples"]; | ||
} | ||
delete op["x-ms-examples"]; | ||
} | ||
if (op.parameters && op.parameters.length === 0) delete op.parameters; | ||
if (op.requestBody) { | ||
let effectiveOperationId = op.operationId ? common.sanitiseAll(op.operationId) : common.sanitiseAll(method + p).toCamelCase(); | ||
let rbName = common.sanitise(op.requestBody['x-s2o-name'] || effectiveOperationId || ''); | ||
delete op.requestBody['x-s2o-name']; | ||
let rbStr = JSON.stringify(op.requestBody); | ||
let rbHash = common.hash(rbStr); | ||
if (!requestBodyCache[rbHash]) { | ||
let entry = {}; | ||
entry.name = rbName; | ||
entry.body = op.requestBody; | ||
entry.refs = []; | ||
requestBodyCache[rbHash] = entry; | ||
if (op.parameters && op.parameters.length === 0) delete op.parameters; | ||
if (op.requestBody) { | ||
let effectiveOperationId = op.operationId ? common.sanitiseAll(op.operationId) : common.sanitiseAll(method + p).toCamelCase(); | ||
let rbName = common.sanitise(op.requestBody['x-s2o-name'] || effectiveOperationId || ''); | ||
delete op.requestBody['x-s2o-name']; | ||
let rbStr = JSON.stringify(op.requestBody); | ||
let rbHash = common.hash(rbStr); | ||
if (!requestBodyCache[rbHash]) { | ||
let entry = {}; | ||
entry.name = rbName; | ||
entry.body = op.requestBody; | ||
entry.refs = []; | ||
requestBodyCache[rbHash] = entry; | ||
} | ||
let ptr = '#/'+containerName+'/'+encodeURIComponent(jptr.jpescape(p))+'/'+method+'/requestBody'; | ||
requestBodyCache[rbHash].refs.push(ptr); | ||
} | ||
let ptr = '#/'+containerName+'/'+encodeURIComponent(jptr.jpescape(p))+'/'+method+'/requestBody'; | ||
requestBodyCache[rbHash].refs.push(ptr); | ||
} | ||
@@ -982,4 +1052,3 @@ | ||
} | ||
if (path.parameters === null) delete path.parameters; | ||
if (path.parameters) { | ||
if (path && path.parameters) { | ||
for (let p2 in path.parameters) { | ||
@@ -989,4 +1058,4 @@ let param = path.parameters[p2]; | ||
} | ||
if (!options.debug) { | ||
path.parameters = path.parameters.filter(deleteParameters); | ||
if (!options.debug && Array.isArray(path.parameters)) { | ||
path.parameters = path.parameters.filter(keepParameters); | ||
} | ||
@@ -1169,2 +1238,3 @@ } | ||
function extractServerParameters(server) { | ||
if (!server || !server.url || (typeof server.url !== 'string')) return server; | ||
server.url = server.url.split('{{').join('{'); | ||
@@ -1178,2 +1248,3 @@ server.url = server.url.split('}}').join('}'); | ||
}); | ||
return server; | ||
} | ||
@@ -1187,7 +1258,7 @@ | ||
else { | ||
return reject(new Error('(Patchable) info object is mandatory')); | ||
return reject(new S2OError('(Patchable) info object is mandatory')); | ||
} | ||
} | ||
if ((typeof openapi.info !== 'object') || (Array.isArray(openapi.info))) { | ||
return reject(new Error('info must be an object')); | ||
return reject(new S2OError('info must be an object')); | ||
} | ||
@@ -1199,3 +1270,3 @@ if ((typeof openapi.info.title === 'undefined') || (openapi.info.title === null)) { | ||
else { | ||
return reject(new Error('(Patchable) info.title cannot be null')); | ||
return reject(new S2OError('(Patchable) info.title cannot be null')); | ||
} | ||
@@ -1208,3 +1279,3 @@ } | ||
else { | ||
return reject(new Error('(Patchable) info.version cannot be null')); | ||
return reject(new S2OError('(Patchable) info.version cannot be null')); | ||
} | ||
@@ -1217,3 +1288,3 @@ } | ||
else { | ||
return reject(new Error('(Patchable) info.version must be a string')); | ||
return reject(new S2OError('(Patchable) info.version must be a string')); | ||
} | ||
@@ -1226,3 +1297,3 @@ } | ||
} | ||
else return reject(new Error('(Patchable) info should not have logo property')); | ||
else return reject(new S2OError('(Patchable) info should not have logo property')); | ||
} | ||
@@ -1235,3 +1306,3 @@ if (typeof openapi.info.termsOfService !== 'undefined') { | ||
else { | ||
return reject(new Error('(Patchable) info.termsOfService cannot be null')); | ||
return reject(new S2OError('(Patchable) info.termsOfService cannot be null')); | ||
} | ||
@@ -1247,3 +1318,3 @@ } | ||
} | ||
else return reject(new Error('(Patchable) info.termsOfService must be a URL')); | ||
else return reject(new S2OError('(Patchable) info.termsOfService must be a URL')); | ||
} | ||
@@ -1260,3 +1331,3 @@ } | ||
else { | ||
return reject(new Error('(Patchable) paths object is mandatory')); | ||
return reject(new S2OError('(Patchable) paths object is mandatory')); | ||
} | ||
@@ -1268,2 +1339,4 @@ } | ||
return maybe(callback, new Promise(function (resolve, reject) { | ||
if (!swagger) swagger = {}; | ||
options.original = swagger; | ||
options.externals = []; | ||
@@ -1277,2 +1350,3 @@ options.externalRefs = {}; | ||
if (!options.cache) options.cache = {}; | ||
if (options.source) options.cache[options.source] = options.original; | ||
if (swagger.openapi && (typeof swagger.openapi === 'string') && swagger.openapi.startsWith('3.')) { | ||
@@ -1300,7 +1374,7 @@ options.openapi = cclone(swagger); | ||
if ((!swagger.swagger) || (swagger.swagger != "2.0")) { | ||
return reject(new Error('Unsupported swagger/OpenAPI version: ' + (swagger.openapi ? swagger.openapi : swagger.swagger))); | ||
return reject(new S2OError('Unsupported swagger/OpenAPI version: ' + (swagger.openapi ? swagger.openapi : swagger.swagger))); | ||
} | ||
let openapi = options.openapi = {}; | ||
openapi.openapi = targetVersion; // semver | ||
openapi.openapi = (typeof options.targetVersion === 'string' && options.targetVersion.startsWith('3.')) ? options.targetVersion : targetVersion; // semver | ||
@@ -1324,5 +1398,8 @@ if (options.origin) { | ||
delete openapi.swagger; | ||
recurse(openapi, {}, function(obj, key, state){ | ||
if ((obj[key] === null) && (!key.startsWith('x-'))) delete obj[key]; // this saves *so* much grief later | ||
}); | ||
if (swagger.host) { | ||
for (let s of swagger.schemes || ['']) { | ||
for (let s of (Array.isArray(swagger.schemes) ? swagger.schemes : [''])) { | ||
let server = {}; | ||
@@ -1386,2 +1463,9 @@ server.url = (s ? s+':' : '') + '//' + swagger.host + (swagger.basePath ? swagger.basePath : ''); | ||
if (typeof openapi.consumes === 'string') { | ||
openapi.consumes = [openapi.consumes]; | ||
} | ||
if (typeof openapi.produces === 'string') { | ||
openapi.produces = [openapi.produces]; | ||
} | ||
openapi.components = {}; | ||
@@ -1440,3 +1524,2 @@ if (openapi['x-callbacks']) { | ||
if (obj) { | ||
options.original = obj; | ||
convertObj(obj, options) | ||
@@ -1447,3 +1530,3 @@ .then(options => resolve(options)) | ||
else { | ||
reject(new Error('Could not parse string')); | ||
reject(new S2OError('Could not parse string')); | ||
} | ||
@@ -1463,3 +1546,3 @@ })); | ||
fetch(url, {agent:options.agent}).then(function (res) { | ||
if (res.status !== 200) throw new Error(`Received status code ${res.status}`); | ||
if (res.status !== 200) throw new S2OError(`Received status code ${res.status}`); | ||
return res.text(); | ||
@@ -1507,2 +1590,3 @@ }).then(function (body) { | ||
module.exports = { | ||
S2OError: S2OError, | ||
targetVersion: targetVersion, | ||
@@ -1509,0 +1593,0 @@ convert: convertObj, |
'use strict'; | ||
const statusCodes = [ | ||
{ | ||
"code": "default", | ||
"phrase": "Default response" | ||
}, | ||
{ | ||
"code": "1XX", | ||
"phrase": "Informational" | ||
}, | ||
{ | ||
"code": "100", | ||
"phrase": "Continue" | ||
}, | ||
{ | ||
"code": "101", | ||
"phrase": "Switching Protocols" | ||
}, | ||
{ | ||
"code": "2XX", | ||
"phrase": "Successful" | ||
}, | ||
{ | ||
"code": "200", | ||
"phrase": "OK" | ||
}, | ||
{ | ||
"code": "201", | ||
"phrase": "Created" | ||
}, | ||
{ | ||
"code": "202", | ||
"phrase": "Accepted" | ||
}, | ||
{ | ||
"code": "203", | ||
"phrase": "Non-Authoritative Information" | ||
}, | ||
{ | ||
"code": "204", | ||
"phrase": "No Content" | ||
}, | ||
{ | ||
"code": "205", | ||
"phrase": "Reset Content" | ||
}, | ||
{ | ||
"code": "206", | ||
"phrase": "Partial Content" | ||
}, | ||
{ | ||
"code": "3XX", | ||
"phrase": "Redirection" | ||
}, | ||
{ | ||
"code": "300", | ||
"phrase": "Multiple Choices" | ||
}, | ||
{ | ||
"code": "301", | ||
"phrase": "Moved Permanently" | ||
}, | ||
{ | ||
"code": "302", | ||
"phrase": "Found" | ||
}, | ||
{ | ||
"code": "303", | ||
"phrase": "See Other" | ||
}, | ||
{ | ||
"code": "304", | ||
"phrase": "Not Modified" | ||
}, | ||
{ | ||
"code": "305", | ||
"phrase": "Use Proxy" | ||
}, | ||
{ | ||
"code": "307", | ||
"phrase": "Temporary Redirect" | ||
}, | ||
{ | ||
"code": "4XX", | ||
"phrase": "Client Error" | ||
}, | ||
{ | ||
"code": "400", | ||
"phrase": "Bad Request" | ||
}, | ||
{ | ||
"code": "401", | ||
"phrase": "Unauthorized" | ||
}, | ||
{ | ||
"code": "402", | ||
"phrase": "Payment Required" | ||
}, | ||
{ | ||
"code": "403", | ||
"phrase": "Forbidden" | ||
}, | ||
{ | ||
"code": "404", | ||
"phrase": "Not Found" | ||
}, | ||
{ | ||
"code": "405", | ||
"phrase": "Method Not Allowed" | ||
}, | ||
{ | ||
"code": "406", | ||
"phrase": "Not Acceptable" | ||
}, | ||
{ | ||
"code": "407", | ||
"phrase": "Proxy Authentication Required" | ||
}, | ||
{ | ||
"code": "408", | ||
"phrase": "Request Timeout" | ||
}, | ||
{ | ||
"code": "409", | ||
"phrase": "Conflict" | ||
}, | ||
{ | ||
"code": "410", | ||
"phrase": "Gone" | ||
}, | ||
{ | ||
"code": "411", | ||
"phrase": "Length Required" | ||
}, | ||
{ | ||
"code": "412", | ||
"phrase": "Precondition Failed" | ||
}, | ||
{ | ||
"code": "413", | ||
"phrase": "Payload Too Large" | ||
}, | ||
{ | ||
"code": "414", | ||
"phrase": "URI Too Long" | ||
}, | ||
{ | ||
"code": "415", | ||
"phrase": "Unsupported Media Type" | ||
}, | ||
{ | ||
"code": "416", | ||
"phrase": "Range Not Satisfiable" | ||
}, | ||
{ | ||
"code": "417", | ||
"phrase": "Expectation Failed" | ||
}, | ||
{ | ||
"code": "418", | ||
"phrase": "I'm a teapot" | ||
}, | ||
{ | ||
"code": "421", | ||
"phrase": "Misdirected request" | ||
}, | ||
{ | ||
"code": "426", | ||
"phrase": "Upgrade Required" | ||
}, | ||
{ | ||
"code": "5XX", | ||
"phrase": "Server Error" | ||
}, | ||
{ | ||
"code": "500", | ||
"phrase": "Internal Server Error" | ||
}, | ||
{ | ||
"code": "501", | ||
"phrase": "Not Implemented" | ||
}, | ||
{ | ||
"code": "502", | ||
"phrase": "Bad Gateway" | ||
}, | ||
{ | ||
"code": "503", | ||
"phrase": "Service Unavailable" | ||
}, | ||
{ | ||
"code": "504", | ||
"phrase": "Gateway Time-out" | ||
}, | ||
{ | ||
"code": "505", | ||
"phrase": "HTTP Version Not Supported" | ||
}, | ||
{ | ||
"code": "102", | ||
"phrase": "Processing" | ||
}, | ||
{ | ||
"code": "103", | ||
"phrase": "Early Hints" | ||
}, | ||
{ | ||
"code": "207", | ||
"phrase": "Multi-Status" | ||
}, | ||
{ | ||
"code": "226", | ||
"phrase": "IM Used" | ||
}, | ||
{ | ||
"code": "308", | ||
"phrase": "Permanent Redirect" | ||
}, | ||
{ | ||
"code": "422", | ||
"phrase": "Unprocessable Entity" | ||
}, | ||
{ | ||
"code": "423", | ||
"phrase": "Locked" | ||
}, | ||
{ | ||
"code": "424", | ||
"phrase": "Failed Dependency" | ||
}, | ||
{ | ||
"code": "428", | ||
"phrase": "Precondition Required" | ||
}, | ||
{ | ||
"code": "429", | ||
"phrase": "Too Many Requests" | ||
}, | ||
{ | ||
"code": "431", | ||
"phrase": "Request Header Fields Too Large" | ||
}, | ||
{ | ||
"code": "451", | ||
"phrase": "Unavailable For Legal Reasons" | ||
}, | ||
{ | ||
"code": "506", | ||
"phrase": "Variant Also Negotiates" | ||
}, | ||
{ | ||
"code": "507", | ||
"phrase": "Insufficient Storage" | ||
}, | ||
{ | ||
"code": "511", | ||
"phrase": "Network Authentication Required" | ||
}, | ||
{ | ||
"code": "7XX", | ||
"phrase": "Developer Error" | ||
} | ||
]; | ||
const http = require('http'); | ||
const ours = { | ||
"default": "Default response", | ||
"1XX": "Informational", | ||
"103": "Early hints", // not in Node < 10 | ||
"2XX": "Successful", | ||
"3XX": "Redirection", | ||
"4XX": "Client Error", | ||
"5XX": "Server Error", | ||
"7XX": "Developer Error" // April fools RFC | ||
}; | ||
module.exports = { | ||
statusCodes: statusCodes | ||
statusCodes: Object.assign({},ours,http.STATUS_CODES) | ||
}; | ||
@@ -9,3 +9,3 @@ #!/usr/bin/env node | ||
const yaml = require('js-yaml'); | ||
const fetch = require('node-fetch'); | ||
const fetch = require('node-fetch-h2'); | ||
@@ -12,0 +12,0 @@ const resolver = require('oas-resolver'); |
@@ -16,2 +16,3 @@ #!/usr/bin/env node | ||
const clone = require('reftools/lib/clone.js').circularClone; | ||
const reref = require('reftools/lib/reref.js').reref; | ||
@@ -26,3 +27,3 @@ const swagger2openapi = require('./index.js'); | ||
let argv = yargs | ||
.usage(baseName+' [options] {path-to-specs}...') | ||
.usage(baseName+' [options] {path-to-docs}...') | ||
.string('encoding') | ||
@@ -33,3 +34,3 @@ .alias('e', 'encoding') | ||
.string('fail') | ||
.describe('fail', 'path to specs expected to fail') | ||
.describe('fail', 'path to docs expected to fail') | ||
.alias('f', 'fail') | ||
@@ -176,2 +177,8 @@ .string('jsonschema') | ||
catch (ex) { | ||
if (options.verbose>1) { | ||
fs.writeFileSync('./debug.yaml',resultStr,'utf8'); | ||
console.warn('Result dumped to debug.yaml fixed.yaml'); | ||
let fix = reref(result); | ||
fs.writeFileSync('./fixed.yaml',yaml.safeDump(fix, { lineWidth: -1 }),'utf8'); | ||
} | ||
should.fail(false,true,'Result cannot be represented safely in YAML'); | ||
@@ -181,3 +188,9 @@ } | ||
validator.validate(result, options, finalise); | ||
validator.validate(result, options) | ||
.then(function(options){ | ||
finalise(null,options); | ||
}) | ||
.catch(function(ex){ | ||
finalise(ex,options); | ||
}); | ||
} | ||
@@ -330,3 +343,4 @@ catch (ex) { | ||
.catch(err => { | ||
console.log(util.inspect(err)); | ||
//console.log(util.inspect(err)); | ||
handleResult(err,options); | ||
}); | ||
@@ -348,3 +362,3 @@ } | ||
process.on('unhandledRejection', r => console.warn(r)); | ||
process.on('unhandledRejection', r => console.warn('UPR',r)); | ||
@@ -368,3 +382,3 @@ process.on('exit', function () { | ||
console.log('Tests: %s passing, %s failing, %s warnings', pass, fail, warnings.length); | ||
process.exitCode = ((fail === 0) && (pass > 0)) ? 0 : 1; | ||
process.exitCode = ((fail === 0 || options.fail) && (pass > 0)) ? 0 : 1; | ||
}); |
{ | ||
"name": "swagger2openapi", | ||
"version": "3.2.14", | ||
"version": "4.0.0", | ||
"description": "Convert Swagger 2.0 definitions to OpenApi 3.0 and validate", | ||
@@ -30,2 +30,5 @@ "main": "index.js", | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/mermade/oas-kit/issues" | ||
}, | ||
"author": "Mike Ralphson <mike.ralphson@gmail.com>", | ||
@@ -36,9 +39,9 @@ "license": "BSD-3-Clause", | ||
"js-yaml": "^3.12.0", | ||
"node-fetch": "^2.3.0", | ||
"node-fetch-h2": "^2.3.0", | ||
"node-readfiles": "^0.2.0", | ||
"oas-kit-common": "^1.0.4", | ||
"oas-resolver": "^1.0.12", | ||
"oas-schema-walker": "^1.1.0", | ||
"oas-validator": "^1.1.13", | ||
"reftools": "^1.0.3", | ||
"oas-kit-common": "^1.0.5", | ||
"oas-resolver": "^1.1.0", | ||
"oas-schema-walker": "^1.1.1", | ||
"oas-validator": "^2.0.0", | ||
"reftools": "^1.0.4", | ||
"yargs": "^12.0.2" | ||
@@ -59,3 +62,3 @@ }, | ||
], | ||
"gitHead": "4862e159745d6ca06044bc3ebabe57a2ad8b7152" | ||
"gitHead": "81067168e0cc0c151de4fd25bf24cfc23bc7bf8b" | ||
} |
@@ -8,3 +8,3 @@ # swagger2openapi | ||
[![Tested on APIs.guru](https://api.apis.guru/badges/tested_on.svg)](https://APIs.guru) | ||
[![Tested on Mermade OpenAPIs](https://img.shields.io/badge/Additional%20Specs-34882-brightgreen.svg)](https://github.com/mermade/openapi-definitions) | ||
[![Tested on Mermade OpenAPIs](https://img.shields.io/badge/Additional%20Docs-74426-brightgreen.svg)](https://github.com/mermade/openapi-definitions) | ||
[![Coverage Status](https://coveralls.io/repos/github/Mermade/swagger2openapi/badge.svg?branch=master)](https://coveralls.io/github/Mermade/swagger2openapi?branch=master) | ||
@@ -33,18 +33,19 @@ [![Known Vulnerabilities](https://snyk.io/test/npm/swagger2openapi/badge.svg)](https://snyk.io/test/npm/swagger2openapi) | ||
Options: | ||
--warnProperty Property name to use for warning extensions | ||
[string] [default: "x-s2o-warning"] | ||
--version Show version number [boolean] | ||
-c, --components output information to unresolve a definition [boolean] | ||
-d, --debug enable debug mode, adds specification-extensions [boolean] | ||
-e, --encoding encoding for input/output files [string] [default: "utf8"] | ||
-h, --help Show help [boolean] | ||
-i, --indent JSON indent to use, defaults to 4 spaces [string] | ||
-o, --outfile the output file to write to [string] | ||
-p, --patch fix up small errors in the source definition [boolean] | ||
-r, --resolve resolve external references [boolean] | ||
-u, --url url of original spec, creates x-origin entry [string] | ||
-v, --verbose increase verbosity [count] | ||
-w, --warnOnly Do not throw on non-patchable errors, add warning extensions | ||
[boolean] | ||
-y, --yaml write YAML, default JSON (overridden by --outfile filepath extension) [boolean] | ||
--warnProperty Property name to use for warning extensions | ||
[string] [default: "x-s2o-warning"] | ||
--version Show version number [boolean] | ||
-c, --components output information to unresolve a definition [boolean] | ||
-d, --debug enable debug mode, adds specification-extensions [boolean] | ||
-e, --encoding encoding for input/output files [string] [default: "utf8"] | ||
-h, --help Show help [boolean] | ||
-i, --indent JSON indent to use, defaults to 4 spaces [string] | ||
-o, --outfile the output file to write to [string] | ||
-p, --patch fix up small errors in the source definition [boolean] | ||
-r, --resolve resolve external references [boolean] | ||
-t, --targetVersion override default target version of 3.0.0 [string] | ||
-u, --url url of original spec, creates x-origin entry [string] | ||
-v, --verbose increase verbosity [count] | ||
-w, --warnOnly Do not throw on non-patchable errors, add warning extensions | ||
[boolean] | ||
-y, --yaml write YAML, default JSON (overridden by --outfile filepath extension) [boolean] | ||
``` | ||
@@ -83,3 +84,3 @@ | ||
```text | ||
oas-validate.js [options] {path-to-specs}... | ||
oas-validate.js [options] {path-to-docs}... | ||
@@ -93,3 +94,3 @@ Options: | ||
-e, --encoding encoding for input/output files [string] [default: "utf8"] | ||
-f, --fail path to specs expected to fail [string] | ||
-f, --fail path to docs expected to fail [string] | ||
-j, --jsonschema path to alternative JSON schema [string] | ||
@@ -131,3 +132,3 @@ -l, --laxurls lax checking of empty urls [boolean] | ||
The test harness currently expects files with a `.json` or `.yaml` extension, or a single named file, and has been tested on Node.js versions 4.x, 6.x and 8.x LTS (it is not recommended to run the test suite under Node.js versions >=7.0.0 and \<8.7.0 because of [this bug](https://github.com/nodejs/node/issues/13048)) against | ||
The test harness currently expects files with a `.json` or `.yaml` extension, or a single named file, and has been tested on Node.js versions 8.x and 10.x LTS (it is not recommended to run the test suite under Node.js versions >=7.0.0 and \<8.7.0 because of [this bug](https://github.com/nodejs/node/issues/13048)) against | ||
@@ -139,3 +140,3 @@ * [APIs.guru](https://github.com/APIs-guru/openapi-directory) | ||
Additionally swagger2openapi has been tested on a corpus of 34,679 real-world valid Swagger 2.0 definitions from GitHub and [SwaggerHub](https://swaggerhub.com/). However, if you have a definition which causes errors in the converter or does not pass validation, please do not hesitate to [raise an issue](https://github.com/Mermade/swagger2openapi/issues). | ||
Additionally swagger2openapi has been tested on a corpus of 74,426 real-world valid Swagger 2.0 definitions from GitHub and [SwaggerHub](https://swaggerhub.com/). However, if you have a definition which causes errors in the converter or does not pass validation, please do not hesitate to [raise an issue](https://github.com/Mermade/swagger2openapi/issues). | ||
@@ -142,0 +143,0 @@ ### Regression tests |
@@ -42,2 +42,5 @@ #!/usr/bin/env node | ||
.describe('resolve', 'resolve external references') | ||
.string('targetVersion') | ||
.alias('t','targetVersion') | ||
.describe('targetVersion','override default target version of 3.0.0') | ||
.string('url') | ||
@@ -58,2 +61,6 @@ .describe('url', 'url of original spec, creates x-origin entry') | ||
.describe('yaml', 'write YAML, default JSON (overridden by --outfile filepath extension)') | ||
.string('rbname') | ||
.alias('b', 'rbname') | ||
.default('rbname', '') | ||
.describe('rbname', 'Extension to use to preserve body parameter names in converted operations ("" == disabled)') | ||
.require(1) | ||
@@ -60,0 +67,0 @@ .strict() |
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
Network access
Supply chain riskThis module accesses the network.
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
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
96450
9
0
144
2020
5
3
+ Addednode-fetch-h2@^2.3.0
+ Addedoas-linter@2.0.1(transitive)
+ Addedoas-validator@2.0.2(transitive)
- Removednode-fetch@^2.3.0
- Removednode-fetch@2.7.0(transitive)
- Removedoas-linter@1.0.8(transitive)
- Removedoas-validator@1.1.13(transitive)
- Removedtr46@0.0.3(transitive)
- Removedwebidl-conversions@3.0.1(transitive)
- Removedwhatwg-url@5.0.0(transitive)
Updatedoas-kit-common@^1.0.5
Updatedoas-resolver@^1.1.0
Updatedoas-schema-walker@^1.1.1
Updatedoas-validator@^2.0.0
Updatedreftools@^1.0.4