Skip to content

Commit c6ac11f

Browse files
authored
support full callback spec (#838)
1 parent b07f5cb commit c6ac11f

File tree

2 files changed

+219
-26
lines changed

2 files changed

+219
-26
lines changed

lib/spec/openapi/utils.js

+29-22
Original file line numberDiff line numberDiff line change
@@ -391,41 +391,48 @@ function resolveResponse (fastifyResponseJson, produces, ref) {
391391
function resolveCallbacks (schema, ref) {
392392
const callbacksContainer = {}
393393

394+
// Iterate over each callback event
394395
for (const eventName in schema) {
395396
if (!schema[eventName]) {
396397
continue
397398
}
398399

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

402-
if (!callbackUrl || !eventSchema[callbackUrl]) {
403-
continue
404-
}
404+
// Iterate over each callbackUrl for the event
405+
for (const callbackUrl in eventSchema) {
406+
if (!callbackUrl || !eventSchema[callbackUrl]) {
407+
continue
408+
}
405409

406-
const callbackSchema = schema[eventName][callbackUrl]
407-
const [httpMethodName] = Object.keys(callbackSchema)
410+
// Create an empty object to house the future iterations
411+
callbacksContainer[eventName][callbackUrl] = {}
412+
const callbackSchema = eventSchema[callbackUrl]
408413

409-
if (!httpMethodName || !callbackSchema[httpMethodName]) {
410-
continue
411-
}
414+
// Iterate over each httpMethod for the callbackUrl
415+
for (const httpMethodName in callbackSchema) {
416+
if (!httpMethodName || !callbackSchema[httpMethodName]) {
417+
continue
418+
}
412419

413-
const httpMethodSchema = callbackSchema[httpMethodName]
414-
const httpMethodContainer = {}
420+
const httpMethodSchema = callbackSchema[httpMethodName]
421+
const httpMethodContainer = {}
415422

416-
if (httpMethodSchema.requestBody) {
417-
httpMethodContainer.requestBody = convertJsonSchemaToOpenapi3(
418-
ref.resolve(httpMethodSchema.requestBody)
419-
)
420-
}
423+
if (httpMethodSchema.requestBody) {
424+
httpMethodContainer.requestBody = convertJsonSchemaToOpenapi3(
425+
ref.resolve(httpMethodSchema.requestBody)
426+
)
427+
}
421428

422-
httpMethodContainer.responses = httpMethodSchema.responses
423-
? convertJsonSchemaToOpenapi3(ref.resolve(httpMethodSchema.responses))
424-
: { '2XX': { description: 'Default Response' } }
429+
// If a response is not provided, set a 2XX default response
430+
httpMethodContainer.responses = httpMethodSchema.responses
431+
? convertJsonSchemaToOpenapi3(ref.resolve(httpMethodSchema.responses))
432+
: { '2XX': { description: 'Default Response' } }
425433

426-
callbacksContainer[eventName] = {
427-
[callbackUrl]: {
428-
[httpMethodName]: httpMethodContainer
434+
// Set the schema at the appropriate location in the response object
435+
callbacksContainer[eventName][callbackUrl][httpMethodName] = httpMethodContainer
429436
}
430437
}
431438
}

test/spec/openapi/schema.js

+190-4
Original file line numberDiff line numberDiff line change
@@ -1382,7 +1382,7 @@ test('support callbacks', async () => {
13821382
await Swagger.validate(openapiObject)
13831383
})
13841384

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

13881388
await fastify.register(fastifySwagger, openapiOption)
@@ -1432,7 +1432,7 @@ test('support callbacks', async () => {
14321432
await Swagger.validate(openapiObject)
14331433
})
14341434

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

14381438
await fastify.register(fastifySwagger, openapiOption)
@@ -1492,7 +1492,7 @@ test('support callbacks', async () => {
14921492
}
14931493
},
14941494
myOtherEvent: {
1495-
'{$request.body#/callbackUrl}': {}
1495+
'{$request.body#/callbackUrl}': null
14961496
}
14971497
}
14981498
}
@@ -1586,7 +1586,11 @@ test('support callbacks', async () => {
15861586
}
15871587
}
15881588
},
1589-
myOtherEvent: {}
1589+
myOtherEvent: {
1590+
'{$request.body#/callbackUrl}': {
1591+
post: null
1592+
}
1593+
}
15901594
}
15911595
}
15921596
},
@@ -1619,4 +1623,186 @@ test('support callbacks', async () => {
16191623

16201624
await Swagger.validate(openapiObject)
16211625
})
1626+
1627+
test('supports multiple callbackUrls and httpMethods in openapiObject', async t => {
1628+
const fastify = Fastify()
1629+
1630+
await fastify.register(fastifySwagger, openapiOption)
1631+
fastify.register(async (instance) => {
1632+
instance.post(
1633+
'/subscribe',
1634+
{
1635+
schema: {
1636+
body: {
1637+
$id: 'Subscription',
1638+
type: 'object',
1639+
properties: {
1640+
callbackUrl: {
1641+
type: 'string',
1642+
examples: ['https://example.com']
1643+
}
1644+
}
1645+
},
1646+
response: {
1647+
200: {
1648+
$id: 'Subscription',
1649+
type: 'object',
1650+
properties: {
1651+
callbackUrl: {
1652+
type: 'string',
1653+
examples: ['https://example.com']
1654+
}
1655+
}
1656+
}
1657+
},
1658+
callbacks: {
1659+
myEvent: {
1660+
'{$request.body#/callbackUrl}': {
1661+
post: {
1662+
requestBody: {
1663+
content: {
1664+
'application/json': {
1665+
schema: {
1666+
type: 'object',
1667+
properties: {
1668+
message: {
1669+
type: 'string',
1670+
example: 'Some event happened'
1671+
}
1672+
},
1673+
required: ['message']
1674+
}
1675+
}
1676+
}
1677+
},
1678+
responses: {
1679+
200: {
1680+
description: 'Success'
1681+
}
1682+
}
1683+
}
1684+
},
1685+
'{$request.body#/anotherUrl}': {
1686+
post: {
1687+
requestBody: {
1688+
content: {
1689+
'application/json': {
1690+
schema: {
1691+
type: 'object',
1692+
properties: {
1693+
message: {
1694+
type: 'string',
1695+
example: 'Another event happened'
1696+
}
1697+
},
1698+
required: ['message']
1699+
}
1700+
}
1701+
}
1702+
},
1703+
responses: {
1704+
200: {
1705+
description: 'Success'
1706+
}
1707+
}
1708+
},
1709+
put: {
1710+
requestBody: {
1711+
content: {
1712+
'application/json': {
1713+
schema: {
1714+
type: 'object',
1715+
properties: {
1716+
message: {
1717+
type: 'string',
1718+
example: 'PUT event happened'
1719+
}
1720+
},
1721+
required: ['message']
1722+
}
1723+
}
1724+
}
1725+
},
1726+
responses: {
1727+
200: {
1728+
description: 'Success'
1729+
}
1730+
}
1731+
}
1732+
}
1733+
},
1734+
myOtherEvent: {
1735+
'{$request.body#/callbackUrl}': {
1736+
post: {
1737+
responses: {
1738+
200: {
1739+
description: 'Success'
1740+
},
1741+
500: {
1742+
description: 'Error'
1743+
}
1744+
}
1745+
}
1746+
}
1747+
}
1748+
}
1749+
}
1750+
},
1751+
() => {}
1752+
)
1753+
})
1754+
1755+
await fastify.ready()
1756+
1757+
const openapiObject = fastify.swagger()
1758+
1759+
t.equal(typeof openapiObject, 'object')
1760+
t.equal(typeof openapiObject.paths['/subscribe'].post.callbacks, 'object')
1761+
1762+
const definedPath = openapiObject.paths['/subscribe'].post.callbacks
1763+
1764+
// First Event->First URL->First Method
1765+
t.strictSame(
1766+
definedPath.myEvent['{$request.body#/callbackUrl}'].post.requestBody
1767+
.content['application/json'].schema.properties,
1768+
{
1769+
message: {
1770+
type: 'string',
1771+
example: 'Some event happened'
1772+
}
1773+
}
1774+
)
1775+
1776+
// First Event->Second URL->First Method
1777+
t.strictSame(
1778+
definedPath.myEvent['{$request.body#/anotherUrl}'].post.requestBody
1779+
.content['application/json'].schema.properties,
1780+
{
1781+
message: {
1782+
type: 'string',
1783+
example: 'Another event happened'
1784+
}
1785+
}
1786+
)
1787+
1788+
// First Event->Second URL->Second Method
1789+
t.strictSame(
1790+
definedPath.myEvent['{$request.body#/anotherUrl}'].put.requestBody
1791+
.content['application/json'].schema.properties,
1792+
{
1793+
message: {
1794+
type: 'string',
1795+
example: 'PUT event happened'
1796+
}
1797+
}
1798+
)
1799+
1800+
// Second Event
1801+
t.same(
1802+
definedPath.myOtherEvent['{$request.body#/callbackUrl}'].post.requestBody,
1803+
null
1804+
)
1805+
1806+
await Swagger.validate(openapiObject)
1807+
})
16221808
})

0 commit comments

Comments
 (0)