Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add route, openapi and swagger objects to transform #739

Merged
merged 3 commits into from
Jul 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,11 +226,14 @@ An example of using `@fastify/swagger` with `static` mode enabled can be found [

By passing a synchronous `transform` function you can modify the route's url and schema.

You may also access the `openapiObject` and `swaggerObject`

Some possible uses of this are:

- add the `hide` flag on schema according to your own logic based on url & schema
- altering the route url into something that's more suitable for the api spec
- using different schemas such as [Joi](https://github.com/hapijs/joi) and transforming them to standard JSON schemas expected by this plugin
- hiding routes based on version constraints

This option is available in `dynamic` mode only.

Expand All @@ -241,7 +244,7 @@ const convert = require('joi-to-json')

await fastify.register(require('@fastify/swagger'), {
swagger: { ... },
transform: ({ schema, url }) => {
transform: ({ schema, url, route, swaggerObject }) => {
const {
params,
body,
Expand All @@ -266,6 +269,9 @@ await fastify.register(require('@fastify/swagger'), {
// can transform the url
if (url.startsWith('/latest_version/endpoint')) transformedUrl = url.replace('latest_version', 'v3')

// can add the hide tag for routes that do not match the swaggerObject version
if (route?.constraints?.version !== swaggerObject.swagger) transformedSchema.hide = true

return { schema: transformedSchema, url: transformedUrl }
}
})
Expand Down
10 changes: 8 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FastifyPluginCallback, FastifySchema, onRequestHookHandler, preHandlerHookHandler } from 'fastify';
import { FastifyPluginCallback, FastifySchema, FastifyRequest, onRequestHookHandler, preHandlerHookHandler } from 'fastify';
import { OpenAPI, OpenAPIV2, OpenAPIV3, OpenAPIV3_1 } from 'openapi-types';

/**
Expand Down Expand Up @@ -124,7 +124,13 @@ declare namespace fastifySwagger {
/**
* custom function to transform the route's schema and url
*/
transform?: <S extends FastifySchema = FastifySchema>({ schema, url }: { schema: S, url: string }) => { schema: FastifySchema, url: string };
transform?: <S extends FastifySchema = FastifySchema>({ schema, url, route, swaggerObject, openapiObject }: {
schema: S,
url: string,
route: FastifyRequest,
swaggerObject: Partial<OpenAPIV2.Document>
openapiObject: Partial<OpenAPIV3.Document | OpenAPIV3_1.Document>
}) => { schema: FastifySchema, url: string };

refResolver?: {
/** Clone the input schema without changing it. Default to `false`. */
Expand Down
2 changes: 1 addition & 1 deletion lib/spec/openapi/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ module.exports = function (opts, cache, routes, Ref, done) {

for (const route of routes) {
const transformResult = defOpts.transform
? defOpts.transform({ schema: route.schema, url: route.url })
? defOpts.transform({ schema: route.schema, url: route.url, route, openapiObject })
: {}

const schema = transformResult.schema || route.schema
Expand Down
2 changes: 1 addition & 1 deletion lib/spec/swagger/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module.exports = function (opts, cache, routes, Ref, done) {
swaggerObject.paths = {}
for (const route of routes) {
const transformResult = defOpts.transform
? defOpts.transform({ schema: route.schema, url: route.url })
? defOpts.transform({ schema: route.schema, url: route.url, route, swaggerObject })
: {}

const schema = transformResult.schema || route.schema
Expand Down
83 changes: 83 additions & 0 deletions test/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,86 @@ test('transform should work with a Function', async (t) => {
await fastify.ready()
t.doesNotThrow(fastify.swagger)
})

test('transform can access route', async (t) => {
t.plan(5)
const fastify = Fastify()

await fastify.register(fastifySwagger, {
openapi: { info: { version: '1.0.0' } },
transform: ({ route }) => {
t.ok(route)
t.equal(route.method, 'GET')
t.equal(route.url, '/example')
t.equal(route.constraints.version, '1.0.0')
return { schema: route.schema, url: route.url }
}
})
fastify.get('/example', { constraints: { version: '1.0.0' } }, () => {})

await fastify.ready()
t.doesNotThrow(fastify.swagger)
})

test('transform can access openapi object', async (t) => {
t.plan(4)
const fastify = Fastify()

await fastify.register(fastifySwagger, {
openapi: { info: { version: '1.0.0' } },
transform: ({ route, openapiObject }) => {
t.ok(openapiObject)
t.equal(openapiObject.openapi, '3.0.3')
t.equal(openapiObject.info.version, '1.0.0')
return {
schema: route.schema,
url: route.url
}
}
})
fastify.get('/example', () => {})

await fastify.ready()
t.doesNotThrow(fastify.swagger)
})

test('transform can access swagger object', async (t) => {
t.plan(4)
const fastify = Fastify()

await fastify.register(fastifySwagger, {
swagger: { info: { version: '1.0.0' } },
transform: ({ route, swaggerObject }) => {
t.ok(swaggerObject)
t.equal(swaggerObject.swagger, '2.0')
t.equal(swaggerObject.info.version, '1.0.0')
return {
schema: route.schema,
url: route.url
}
}
})
fastify.get('/example', () => {})

await fastify.ready()
t.doesNotThrow(fastify.swagger)
})

test('transform can hide routes based on openapi version', async (t) => {
t.plan(1)
const fastify = Fastify()

await fastify.register(fastifySwagger, {
openapi: { info: { version: '2.0.0' } },
transform: ({ schema, route, openapiObject }) => {
const transformedSchema = Object.assign({}, schema)
if (route?.constraints?.version !== openapiObject.info.version) transformedSchema.hide = true
return { schema: transformedSchema, url: route.url }
}
})
fastify.get('/example', { constraints: { version: '1.0.0' } }, () => {})

await fastify.ready()
const openapiObject = fastify.swagger()
t.notOk(openapiObject.paths['/example'])
})