fastify-swagger
Advanced tools
Comparing version 4.12.6 to 4.13.0
@@ -83,2 +83,8 @@ 'use strict' | ||
if (typeof jsonSchema === 'object' && jsonSchema !== null) { | ||
// Handle patternProperties, that is not part of OpenAPI definitions | ||
if (jsonSchema.patternProperties) { | ||
jsonSchema.additionalProperties = { type: 'string' } | ||
delete jsonSchema.patternProperties | ||
} | ||
Object.keys(jsonSchema).forEach(function (key) { | ||
@@ -85,0 +91,0 @@ if (key === '$ref') { |
@@ -158,2 +158,21 @@ 'use strict' | ||
/* | ||
* Map unsupported JSON schema definitions to Swagger definitions | ||
*/ | ||
function replaceUnsupported (jsonSchema) { | ||
if (typeof jsonSchema === 'object' && jsonSchema !== null) { | ||
// Handle patternProperties, that is not part of OpenAPI definitions | ||
if (jsonSchema.patternProperties) { | ||
jsonSchema.additionalProperties = { type: 'string' } | ||
delete jsonSchema.patternProperties | ||
} | ||
Object.keys(jsonSchema).forEach(function (key) { | ||
jsonSchema[key] = replaceUnsupported(jsonSchema[key]) | ||
}) | ||
} | ||
return jsonSchema | ||
} | ||
function isConsumesFormOnly (schema) { | ||
@@ -172,2 +191,4 @@ const consumes = schema.consumes | ||
replaceUnsupported(resolved) | ||
parameters.push({ | ||
@@ -229,2 +250,3 @@ name: 'body', | ||
const schema = { ...resolved } | ||
replaceUnsupported(schema) | ||
delete schema[xResponseDescription] | ||
@@ -231,0 +253,0 @@ response.schema = schema |
@@ -72,12 +72,76 @@ 'use strict' | ||
// custom verbs at the end of a url are okay => /user::watch but should be rendered as /user:watch in swagger | ||
function formatParamUrl (url) { | ||
const regex = /(?<!:):([a-zA-Z0-9_]+)/g | ||
let found = regex.exec(url) | ||
while (found !== null) { | ||
const [full, param] = found | ||
url = url.replace(full, '{' + param + '}') | ||
found = regex.exec(url) | ||
const COLON = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_' | ||
function formatParamUrl (str) { | ||
let i, char | ||
let state = 'skip' | ||
let path = '' | ||
let param = '' | ||
let level = 0 | ||
// count for regex if no param exist | ||
let regexp = 0 | ||
for (i = 0; i < str.length; i++) { | ||
char = str[i] | ||
switch (state) { | ||
case 'colon': { | ||
// we only accept a-zA-Z0-9_ in param | ||
if (COLON.indexOf(char) !== -1) { | ||
param += char | ||
} else if (char === '(') { | ||
state = 'regexp' | ||
level++ | ||
} else { | ||
// end | ||
state = 'skip' | ||
path += '{' + param + '}' | ||
path += char | ||
param = '' | ||
} | ||
break | ||
} | ||
case 'regexp': { | ||
if (char === '(') { | ||
level++ | ||
} else if (char === ')') { | ||
level-- | ||
} | ||
// we end if the level reach zero | ||
if (level === 0) { | ||
state = 'skip' | ||
if (param === '') { | ||
regexp++ | ||
param = 'regexp' + String(regexp) | ||
} | ||
path += '{' + param + '}' | ||
param = '' | ||
} | ||
break | ||
} | ||
default: { | ||
// we check if we need to change state | ||
if (char === ':' && str[i + 1] === ':') { | ||
// double colon -> single colon | ||
path += char | ||
// skip one more | ||
i++ | ||
} else if (char === ':') { | ||
// single colon -> state colon | ||
state = 'colon' | ||
} else if (char === '(') { | ||
state = 'regexp' | ||
level++ | ||
} else if (char === '*') { | ||
// * -> wildcard | ||
// should be exist once only | ||
path += '{wildcard}' | ||
} else { | ||
path += char | ||
} | ||
} | ||
} | ||
} | ||
return url.replace(/::/g, ':') | ||
// clean up | ||
if (state === 'colon' && param !== '') { | ||
path += '{' + param + '}' | ||
} | ||
return path | ||
} | ||
@@ -84,0 +148,0 @@ |
{ | ||
"name": "fastify-swagger", | ||
"version": "4.12.6", | ||
"version": "4.13.0", | ||
"description": "Serve Swagger/OpenAPI documentation for Fastify, supporting dynamic generation", | ||
@@ -38,3 +38,3 @@ "main": "index.js", | ||
"devDependencies": { | ||
"@types/node": "^16.0.0", | ||
"@types/node": "^17.0.0", | ||
"fastify": "^3.7.0", | ||
@@ -51,5 +51,5 @@ "fastify-basic-auth": "^2.1.0", | ||
"swagger-parser": "^10.0.2", | ||
"swagger-ui-dist": "3.52.5", | ||
"swagger-ui-dist": "4.1.3", | ||
"tap": "^15.0.1", | ||
"tsd": "^0.18.0" | ||
"tsd": "^0.19.0" | ||
}, | ||
@@ -61,3 +61,3 @@ "dependencies": { | ||
"json-schema-resolver": "^1.3.0", | ||
"openapi-types": "^9.1.0" | ||
"openapi-types": "^10.0.0" | ||
}, | ||
@@ -64,0 +64,0 @@ "standard": { |
@@ -419,1 +419,58 @@ 'use strict' | ||
}) | ||
test('support "patternProperties" parameter', async t => { | ||
const opt = { | ||
schema: { | ||
response: { | ||
200: { | ||
description: 'Expected Response', | ||
type: 'object', | ||
properties: { | ||
foo: { | ||
type: 'object', | ||
patternProperties: { | ||
'^[a-z]{2,3}-[a-zA-Z]{2}$': { | ||
type: 'string' | ||
} | ||
}, | ||
additionalProperties: false | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
const fastify = Fastify() | ||
fastify.register(fastifySwagger, { | ||
openapi: true, | ||
routePrefix: '/docs', | ||
exposeRoute: true | ||
}) | ||
fastify.get('/', opt, () => {}) | ||
await fastify.ready() | ||
const swaggerObject = fastify.swagger() | ||
const api = await Swagger.validate(swaggerObject) | ||
const definedPath = api.paths['/'].get | ||
t.same(definedPath.responses[200], { | ||
description: 'Expected Response', | ||
content: { | ||
'application/json': { | ||
schema: { | ||
description: 'Expected Response', | ||
type: 'object', | ||
properties: { | ||
foo: { | ||
type: 'object', | ||
additionalProperties: { type: 'string' } | ||
} | ||
} | ||
} | ||
} | ||
} | ||
}) | ||
}) |
@@ -418,1 +418,67 @@ 'use strict' | ||
}) | ||
test('support "patternProperties" in json schema', async t => { | ||
const opt = { | ||
schema: { | ||
body: { | ||
type: 'object', | ||
patternProperties: { | ||
'^[a-z]{2,3}-[a-zA-Z]{2}$': { | ||
type: 'string' | ||
} | ||
} | ||
}, | ||
response: { | ||
200: { | ||
description: 'Expected Response', | ||
type: 'object', | ||
properties: { | ||
foo: { | ||
type: 'object', | ||
patternProperties: { | ||
'^[a-z]{2,3}-[a-zA-Z]{2}$': { | ||
type: 'string' | ||
} | ||
}, | ||
additionalProperties: false | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} | ||
const fastify = Fastify() | ||
fastify.register(fastifySwagger, { | ||
swagger: true, | ||
routePrefix: '/docs', | ||
exposeRoute: true | ||
}) | ||
fastify.get('/', opt, () => {}) | ||
await fastify.ready() | ||
const swaggerObject = fastify.swagger() | ||
const api = await Swagger.validate(swaggerObject) | ||
const definedPath = api.paths['/'].get | ||
t.same(definedPath.parameters[0].schema, { | ||
type: 'object', | ||
additionalProperties: { type: 'string' } | ||
}) | ||
t.same(definedPath.responses[200], { | ||
description: 'Expected Response', | ||
schema: { | ||
description: 'Expected Response', | ||
type: 'object', | ||
properties: { | ||
foo: { | ||
type: 'object', | ||
additionalProperties: { type: 'string' } | ||
} | ||
} | ||
} | ||
}) | ||
}) |
@@ -6,28 +6,23 @@ 'use strict' | ||
const cases = [ | ||
['/example/:userId', '/example/{userId}'], | ||
['/example/:userId/:secretToken', '/example/{userId}/{secretToken}'], | ||
['/example/near/:lat-:lng/radius/:r', '/example/near/{lat}-{lng}/radius/{r}'], | ||
['/example/near/:lat_1-:lng_1/radius/:r_1', '/example/near/{lat_1}-{lng_1}/radius/{r_1}'], | ||
['/example/*', '/example/{wildcard}'], | ||
['/example/:file(^\\d+).png', '/example/{file}.png'], | ||
['/example/at/:hour(^\\d{2})h:minute(^\\d{2})m', '/example/at/{hour}h{minute}m'], | ||
['/example/at/(^\\d{2})h(^\\d{2})m', '/example/at/{regexp1}h{regexp2}m'], | ||
['/example/at/(^([0-9]{2})h$)-(^([0-9]{2})m$)', '/example/at/{regexp1}-{regexp2}'], | ||
['/name::verb', '/name:verb'], | ||
['/api/v1/postalcode-jp/:code(^[0-9]{7}$)', '/api/v1/postalcode-jp/{code}'], | ||
['/api/v1/postalcode-jp/(^[0-9]{7}$)', '/api/v1/postalcode-jp/{regexp1}'] | ||
] | ||
test('formatParamUrl', t => { | ||
t.plan(4) | ||
t.plan(cases.length) | ||
t.test('support /example/:userId', t => { | ||
t.plan(1) | ||
const url = formatParamUrl('/example/:userId') | ||
t.equal(url, '/example/{userId}') | ||
}) | ||
t.test('support /example/:userId/:secretToken', t => { | ||
t.plan(1) | ||
const url = formatParamUrl('/example/:userId/:secretToken') | ||
t.equal(url, '/example/{userId}/{secretToken}') | ||
}) | ||
t.test('support /example/near/:lat-:lng/radius/:r', t => { | ||
t.plan(1) | ||
const url = formatParamUrl('/example/near/:lat-:lng/radius/:r') | ||
t.equal(url, '/example/near/{lat}-{lng}/radius/{r}') | ||
}) | ||
t.test('support /example/near/:lat_1-:lng_1/radius/:r_1', t => { | ||
t.plan(1) | ||
const url = formatParamUrl('/example/near/:lat_1-:lng_1/radius/:r_1') | ||
t.equal(url, '/example/near/{lat_1}-{lng_1}/radius/{r_1}') | ||
}) | ||
for (const kase of cases) { | ||
t.equal(formatParamUrl(kase[0]), kase[1]) | ||
} | ||
}) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
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
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
15445
6
10267345
+ Addedopenapi-types@10.0.0(transitive)
- Removedopenapi-types@9.3.1(transitive)
Updatedopenapi-types@^10.0.0