Skip to content

Commit

Permalink
fix: Pass JSON Schema keywords into OpenAPI Header Spec (#704)
Browse files Browse the repository at this point in the history
* fix: Render JSON Schema keywords

* chores: Cleanup and refactor

* feat: allowReserved in queryParams
  • Loading branch information
WoH authored Jan 16, 2023
1 parent 49f1642 commit 05d9d14
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 37 deletions.
28 changes: 4 additions & 24 deletions lib/spec/openapi/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ function plainJsonObjectToOpenapi3 (container, jsonSchema, externalSchemas, secu
let toOpenapiProp
switch (container) {
case 'cookie':
case 'header':
case 'query':
toOpenapiProp = function (propertyName, jsonSchemaElement) {
let result = {
Expand Down Expand Up @@ -184,6 +185,9 @@ function plainJsonObjectToOpenapi3 (container, jsonSchema, externalSchemas, secu
// optionally add serialization format style
if (jsonSchema.style) result.style = jsonSchema.style
if (jsonSchema.explode != null) result.explode = jsonSchema.explode
if (jsonSchema.allowReserved === true && container === 'query') {
result.allowReserved = jsonSchema.allowReserved
}
return result
}
break
Expand All @@ -203,30 +207,6 @@ function plainJsonObjectToOpenapi3 (container, jsonSchema, externalSchemas, secu
return result
}
break
case 'header':
toOpenapiProp = function (propertyName, jsonSchemaElement) {
const result = {
in: 'header',
name: propertyName,
required: jsonSchemaElement.required,
description: jsonSchemaElement.description,
schema: {
type: jsonSchemaElement.type
}
}

const media = schemaToMedia(jsonSchemaElement)
if (media.example) {
result.example = media.example
}

if (media.examples) {
result.examples = media.examples
}

return result
}
break
}

return Object.keys(obj)
Expand Down
14 changes: 2 additions & 12 deletions lib/spec/swagger/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,13 @@ function plainJsonObjectToSwagger2 (container, jsonSchema, externalSchemas, secu
const obj = resolveLocalRef(jsonSchema, externalSchemas)
let toSwaggerProp
switch (container) {
case 'header':
case 'query':
toSwaggerProp = function (propertyName, jsonSchemaElement) {
// complex serialization is not supported by swagger
if (jsonSchemaElement[xConsume]) {
throw new Error('Complex serialization is not supported by Swagger. ' +
'Remove "' + xConsume + '" for "' + propertyName + '" querystring schema or ' +
'Remove "' + xConsume + '" for "' + propertyName + '" querystring/header schema or ' +
'change specification to OpenAPI')
}
jsonSchemaElement.in = container
Expand Down Expand Up @@ -135,17 +136,6 @@ function plainJsonObjectToSwagger2 (container, jsonSchema, externalSchemas, secu
return jsonSchemaElement
}
break
case 'header':
toSwaggerProp = function (propertyName, jsonSchemaElement) {
return {
in: 'header',
name: propertyName,
required: jsonSchemaElement.required,
description: jsonSchemaElement.description,
type: jsonSchemaElement.type
}
}
break
}

return Object.keys(obj)
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
"homepage": "https://github.com/fastify/fastify-swagger#readme",
"devDependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"@fastify/cookie": "^8.3.0",
"@fastify/pre-commit": "^2.0.2",
"@types/node": "^18.0.0",
"fastify": "^4.0.0",
Expand Down
70 changes: 70 additions & 0 deletions test/spec/openapi/refs.js
Original file line number Diff line number Diff line change
Expand Up @@ -282,3 +282,73 @@ test('uses examples if has property required in body', async (t) => {
t.ok(schema.parameters)
t.same(schema.parameters[0].in, 'query')
})

test('renders $ref schema with enum in headers', async (t) => {
const fastify = Fastify()
await fastify.register(fastifySwagger, { openapi: {} })
fastify.register(async (instance) => {
instance.addSchema({ $id: 'headerA', type: 'object', properties: { 'x-enum-header': { type: 'string', enum: ['OK', 'NOT_OK'] } } })
instance.get('/url1', { schema: { headers: { $ref: 'headerA#' }, response: { 200: { type: 'object' } } } }, async () => ({ result: 'OK' }))
})

await fastify.ready()

const openapiObject = fastify.swagger()

await Swagger.validate(openapiObject)

// the OpenAPI spec should show the enum
t.match(openapiObject.paths['/url1'].get.parameters[0].schema, { type: 'string', enum: ['OK', 'NOT_OK'] })
})

test('renders $ref schema with additional keywords', async (t) => {
const fastify = Fastify()
await fastify.register(fastifySwagger, { openapi: {} })
await fastify.register(require('@fastify/cookie'))

const cookie = {
type: 'object',
properties: {
a: { type: 'string' },
b: { type: 'string' },
c: { type: 'string' }
},
minProperties: 2
}

fastify.register(async (instance) => {
instance.addSchema({
$id: 'headerA',
type: 'object',
properties: {
cookie
}
})

instance.get('/url1', {
preValidation: async (request) => {
request.headers.cookie = request.cookies
},
schema: {
headers: {
$ref: 'headerA#'
}
}
}, async (req) => (req.headers))
})

await fastify.ready()
const openapiObject = fastify.swagger()
await Swagger.validate(openapiObject)

t.match(openapiObject.paths['/url1'].get.parameters[0].schema, cookie)

let res = await fastify.inject({ method: 'GET', url: 'url1', cookies: { a: 'hi', b: 'asd' } })

t.match(res.statusCode, 200)

res = await fastify.inject({ method: 'GET', url: 'url1', cookies: { a: 'hi' } })

t.match(res.statusCode, 400)
t.match(openapiObject.paths['/url1'].get.parameters[0].schema, cookie)
})
4 changes: 3 additions & 1 deletion test/spec/openapi/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ test('support - oneOf, anyOf, allOf in headers', async (t) => {
required: false,
in: 'header',
name: 'foo',
description: undefined,
schema: {
type: 'string'
}
Expand Down Expand Up @@ -819,6 +818,7 @@ test('support query serialization params', async t => {
style: 'deepObject',
explode: false,
type: 'object',
allowReserved: true,
properties: {
obj: {
type: 'string'
Expand All @@ -834,6 +834,7 @@ test('support query serialization params', async t => {
function (ajv) {
ajv.addKeyword({ keyword: 'style' })
ajv.addKeyword({ keyword: 'explode' })
ajv.addKeyword({ keyword: 'allowReserved' })
}
]
}
Expand All @@ -848,4 +849,5 @@ test('support query serialization params', async t => {
const api = await Swagger.validate(swaggerObject)
t.equal(api.paths['/'].get.parameters[0].style, 'deepObject')
t.equal(api.paths['/'].get.parameters[0].explode, false)
t.equal(api.paths['/'].get.parameters[0].allowReserved, true)
})
17 changes: 17 additions & 0 deletions test/spec/swagger/refs.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,20 @@ test('trying to overwriting a schema results in a FST_ERR_SCH_ALREADY_PRESENT',

await fastify.ready()
})

test('renders $ref schema with enum in headers', async (t) => {
const fastify = Fastify()
await fastify.register(fastifySwagger)
fastify.register(async (instance) => {
instance.addSchema({ $id: 'headerA', type: 'object', properties: { 'x-enum-header': { type: 'string', enum: ['OK', 'NOT_OK'] } } })
instance.get('/url1', { schema: { headers: { $ref: 'headerA#' }, response: { 200: { type: 'object' } } } }, async () => ({ result: 'OK' }))
})

await fastify.ready()

const swagger = fastify.swagger()

await Swagger.validate(swagger)

t.match(swagger.paths['/url1'].get.parameters[0], { type: 'string', enum: ['OK', 'NOT_OK'] })
})

0 comments on commit 05d9d14

Please sign in to comment.