Skip to content

Commit

Permalink
support full callback spec (#838)
Browse files Browse the repository at this point in the history
  • Loading branch information
R-Campbell authored Nov 15, 2024
1 parent b07f5cb commit c6ac11f
Show file tree
Hide file tree
Showing 2 changed files with 219 additions and 26 deletions.
51 changes: 29 additions & 22 deletions lib/spec/openapi/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,41 +391,48 @@ function resolveResponse (fastifyResponseJson, produces, ref) {
function resolveCallbacks (schema, ref) {
const callbacksContainer = {}

// Iterate over each callback event
for (const eventName in schema) {
if (!schema[eventName]) {
continue
}

// Create an empty object to house the future iterations
callbacksContainer[eventName] = {}
const eventSchema = schema[eventName]
const [callbackUrl] = Object.keys(eventSchema)

if (!callbackUrl || !eventSchema[callbackUrl]) {
continue
}
// Iterate over each callbackUrl for the event
for (const callbackUrl in eventSchema) {
if (!callbackUrl || !eventSchema[callbackUrl]) {
continue
}

const callbackSchema = schema[eventName][callbackUrl]
const [httpMethodName] = Object.keys(callbackSchema)
// Create an empty object to house the future iterations
callbacksContainer[eventName][callbackUrl] = {}
const callbackSchema = eventSchema[callbackUrl]

if (!httpMethodName || !callbackSchema[httpMethodName]) {
continue
}
// Iterate over each httpMethod for the callbackUrl
for (const httpMethodName in callbackSchema) {
if (!httpMethodName || !callbackSchema[httpMethodName]) {
continue
}

const httpMethodSchema = callbackSchema[httpMethodName]
const httpMethodContainer = {}
const httpMethodSchema = callbackSchema[httpMethodName]
const httpMethodContainer = {}

if (httpMethodSchema.requestBody) {
httpMethodContainer.requestBody = convertJsonSchemaToOpenapi3(
ref.resolve(httpMethodSchema.requestBody)
)
}
if (httpMethodSchema.requestBody) {
httpMethodContainer.requestBody = convertJsonSchemaToOpenapi3(
ref.resolve(httpMethodSchema.requestBody)
)
}

httpMethodContainer.responses = httpMethodSchema.responses
? convertJsonSchemaToOpenapi3(ref.resolve(httpMethodSchema.responses))
: { '2XX': { description: 'Default Response' } }
// If a response is not provided, set a 2XX default response
httpMethodContainer.responses = httpMethodSchema.responses
? convertJsonSchemaToOpenapi3(ref.resolve(httpMethodSchema.responses))
: { '2XX': { description: 'Default Response' } }

callbacksContainer[eventName] = {
[callbackUrl]: {
[httpMethodName]: httpMethodContainer
// Set the schema at the appropriate location in the response object
callbacksContainer[eventName][callbackUrl][httpMethodName] = httpMethodContainer
}
}
}
Expand Down
194 changes: 190 additions & 4 deletions test/spec/openapi/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -1382,7 +1382,7 @@ test('support callbacks', async () => {
await Swagger.validate(openapiObject)
})

test('skips callbacks if badly formatted', async t => {
test('skips callbacks if event is badly formatted', async t => {
const fastify = Fastify()

await fastify.register(fastifySwagger, openapiOption)
Expand Down Expand Up @@ -1432,7 +1432,7 @@ test('support callbacks', async () => {
await Swagger.validate(openapiObject)
})

test('skips callback if event is badly formatted', async t => {
test('skips callback if callbackUrl is badly formatted', async t => {
const fastify = Fastify()

await fastify.register(fastifySwagger, openapiOption)
Expand Down Expand Up @@ -1492,7 +1492,7 @@ test('support callbacks', async () => {
}
},
myOtherEvent: {
'{$request.body#/callbackUrl}': {}
'{$request.body#/callbackUrl}': null
}
}
}
Expand Down Expand Up @@ -1586,7 +1586,11 @@ test('support callbacks', async () => {
}
}
},
myOtherEvent: {}
myOtherEvent: {
'{$request.body#/callbackUrl}': {
post: null
}
}
}
}
},
Expand Down Expand Up @@ -1619,4 +1623,186 @@ test('support callbacks', async () => {

await Swagger.validate(openapiObject)
})

test('supports multiple callbackUrls and httpMethods in openapiObject', async t => {
const fastify = Fastify()

await fastify.register(fastifySwagger, openapiOption)
fastify.register(async (instance) => {
instance.post(
'/subscribe',
{
schema: {
body: {
$id: 'Subscription',
type: 'object',
properties: {
callbackUrl: {
type: 'string',
examples: ['https://example.com']
}
}
},
response: {
200: {
$id: 'Subscription',
type: 'object',
properties: {
callbackUrl: {
type: 'string',
examples: ['https://example.com']
}
}
}
},
callbacks: {
myEvent: {
'{$request.body#/callbackUrl}': {
post: {
requestBody: {
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: {
type: 'string',
example: 'Some event happened'
}
},
required: ['message']
}
}
}
},
responses: {
200: {
description: 'Success'
}
}
}
},
'{$request.body#/anotherUrl}': {
post: {
requestBody: {
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: {
type: 'string',
example: 'Another event happened'
}
},
required: ['message']
}
}
}
},
responses: {
200: {
description: 'Success'
}
}
},
put: {
requestBody: {
content: {
'application/json': {
schema: {
type: 'object',
properties: {
message: {
type: 'string',
example: 'PUT event happened'
}
},
required: ['message']
}
}
}
},
responses: {
200: {
description: 'Success'
}
}
}
}
},
myOtherEvent: {
'{$request.body#/callbackUrl}': {
post: {
responses: {
200: {
description: 'Success'
},
500: {
description: 'Error'
}
}
}
}
}
}
}
},
() => {}
)
})

await fastify.ready()

const openapiObject = fastify.swagger()

t.equal(typeof openapiObject, 'object')
t.equal(typeof openapiObject.paths['/subscribe'].post.callbacks, 'object')

const definedPath = openapiObject.paths['/subscribe'].post.callbacks

// First Event->First URL->First Method
t.strictSame(
definedPath.myEvent['{$request.body#/callbackUrl}'].post.requestBody
.content['application/json'].schema.properties,
{
message: {
type: 'string',
example: 'Some event happened'
}
}
)

// First Event->Second URL->First Method
t.strictSame(
definedPath.myEvent['{$request.body#/anotherUrl}'].post.requestBody
.content['application/json'].schema.properties,
{
message: {
type: 'string',
example: 'Another event happened'
}
}
)

// First Event->Second URL->Second Method
t.strictSame(
definedPath.myEvent['{$request.body#/anotherUrl}'].put.requestBody
.content['application/json'].schema.properties,
{
message: {
type: 'string',
example: 'PUT event happened'
}
}
)

// Second Event
t.same(
definedPath.myOtherEvent['{$request.body#/callbackUrl}'].post.requestBody,
null
)

await Swagger.validate(openapiObject)
})
})

0 comments on commit c6ac11f

Please sign in to comment.