Skip to content
This repository has been archived by the owner on Jun 20, 2022. It is now read-only.

Commit

Permalink
fix: fix schema generation for swagger
Browse files Browse the repository at this point in the history
  • Loading branch information
juliangoacher committed Feb 21, 2020
1 parent 76ddd14 commit af892c5
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 99 deletions.
84 changes: 28 additions & 56 deletions packages/trail-hapi-plugin/lib/api.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,7 @@
'use strict'

const { get } = require('lodash')
const j2s = require('joi-to-swagger')

const addReference = function (spec) {
const info = typeof spec.describe === 'function' ? spec.describe() : spec
const id = get(info, 'metas.0.id')

if (id) return { $ref: `#/components/${id}` }
else if (spec.isJoi) return JSON.stringify(info)
else return spec
}

const parseResponses = function (route) {
const parseResponses = function (route, components) {
const responses = {}
const specObject = get(route, 'config.response.status')

Expand All @@ -20,18 +10,15 @@ const parseResponses = function (route) {

// For each response
for (const [code, response] of specPairs) {
// Get its info and any reference id
const info = response.describe()
const { swagger: schema } = j2s(response, components)

if (code === '204') { // No body reply
responses[code.toString()] = { description: info.description }
responses[code.toString()] = { description: schema.description }
} else { // Assign the new response, either with a reference or by converting the object
responses[code.toString()] = {
description: info.description,
description: schema.description,
content: {
'application/json': {
schema: addReference(response)
}
'application/json': { schema }
}
}
}
Expand All @@ -40,55 +27,40 @@ const parseResponses = function (route) {
return responses
}

const parseParameters = function (route) {
// If there is a already defined format, use it
const specObject = get(route, 'config.validate.params')

const parseSpecObject = function (specObject, scope, components) {
if (!specObject) return null

return Object.entries(specObject).map(([name, spec]) => {
const info = spec.describe()

const { swagger: { properties = {}, required = [] } } = j2s(specObject, components)
return Object.entries(properties).map(([name, schema]) => {
return {
name,
in: 'path',
description: info.description,
required: get(info, 'flags.presence') === 'required',
schema: addReference(info)
in: scope,
description: schema.description,
required: required.includes(name),
schema
}
})
}

const parseQuerystring = function (route) {
// If there is a already defined format, use it
const specObject = get(route, 'config.validate.query')

if (!specObject) return null
const parseParameters = function (route, components) {
return parseSpecObject(get(route, 'config.validate.params'), 'path', components)
}

const spec = specObject.describe().keys
return Object.entries(spec).map(([name, info]) => {
return {
name,
in: 'query',
description: info.description,
required: get(info, 'flags.presence') === 'required',
schema: addReference(info)
}
})
const parseQuerystring = function (route, components) {
return parseSpecObject(get(route, 'config.validate.query'), 'query', components)
}

const parseBody = function (route) {
const parseBody = function (route, components) {
// If there is a already defined format, use it
const specObject = get(route, 'config.validate.payload')

if (!specObject) return null

const { swagger: schema } = j2s(specObject, components)

return {
description: specObject.description,
description: schema.description,
content: {
'application/json': {
schema: addReference(specObject)
}
'application/json': { schema }
}
}
}
Expand All @@ -103,7 +75,7 @@ module.exports = (function () {
routes[collection].push(routeSpec)
return server.route(routeSpec)
},
generateSpec (spec, collection) {
generateSpec (spec, components, collection) {
// Sort by path
const apiRoutes = routes[collection].sort((a, b) => a.path.localeCompare(b.path))

Expand All @@ -119,11 +91,11 @@ module.exports = (function () {
spec.paths[path][route.method.toLowerCase()] = {
summary: description,
tags: tags.filter(t => t !== 'api'),
responses: parseResponses(route),
requestBody: parseBody(route),
responses: parseResponses(route, components),
requestBody: parseBody(route, components),
parameters: [
parseParameters(route),
parseQuerystring(route)
parseParameters(route, components),
parseQuerystring(route, components)
].filter(l => l).reduce((a, b) => a.concat(b), [])
}
}
Expand Down
16 changes: 5 additions & 11 deletions packages/trail-hapi-plugin/lib/routes/trails.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const { notFound } = require('boom')
const Joi = require('@hapi/joi')

const { errorsSchemas } = require('../schemas/errors')
const { spec, trailSchema } = require('../schemas/trails')
const { spec, components, trailSchema } = require('../schemas/trails')
const { failAction, validationOptions } = require('../validation')
const { addApiRoute, generateSpec } = require('../api')

Expand Down Expand Up @@ -126,9 +126,7 @@ module.exports = {
description: 'Get a audit trail.',
tags: ['api', 'trails'],
validate: {
params: {
id: trailSchema.params.id
}
params: trailSchema.params
},
response: {
status: {
Expand Down Expand Up @@ -163,9 +161,7 @@ module.exports = {
'content-type': Joi.string().valid('application/json')
})
.unknown(true),
params: {
id: trailSchema.params.id
},
params: trailSchema.params,
payload: trailSchema.request
},
response: {
Expand Down Expand Up @@ -196,9 +192,7 @@ module.exports = {
description: 'Delete a audit trail.',
tags: ['api', 'trails'],
validate: {
params: {
id: trailSchema.params.id
}
params: trailSchema.params
},
response: {
status: {
Expand All @@ -213,7 +207,7 @@ module.exports = {

// Add tagged routes to the swagger.json
server.ext('onPostStart', server => {
generateSpec(spec, 'trails')
generateSpec(spec, components, 'trails')
})
}
}
10 changes: 5 additions & 5 deletions packages/trail-hapi-plugin/lib/schemas/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module.exports = {
errorsMessages,
errorsSchemas: {
400: Joi.object()
.meta({ id: 'errors/400' })
.meta({ className: 'errors/400' })
.description('Error returned when the input payload is not a valid JSON.')
.keys({
statusCode: Joi.number().valid(400).example(400).required(),
Expand All @@ -30,7 +30,7 @@ module.exports = {
})
.unknown(false),
404: Joi.object()
.meta({ id: 'errors/404' })
.meta({ className: 'errors/404' })
.description('Error returned when a requested resource could not be found.')
.keys({
statusCode: Joi.number().valid(404).example(404).required(),
Expand All @@ -39,7 +39,7 @@ module.exports = {
})
.unknown(false),
409: Joi.object()
.meta({ id: 'errors/404' })
.meta({ className: 'errors/409' })
.description('Error returned when a requested resource already exists.')
.keys({
statusCode: Joi.number().valid(409).example(409).required(),
Expand All @@ -48,7 +48,7 @@ module.exports = {
})
.unknown(false),
422: Joi.object()
.meta({ id: 'errors/422' })
.meta({ className: 'errors/422' })
.description('Error returned when the input payload is not a valid trail.')
.keys({
statusCode: Joi.number().valid(422).example(422).required(),
Expand All @@ -57,7 +57,7 @@ module.exports = {
})
.unknown(false),
500: Joi.object()
.meta({ id: 'errors/500' })
.meta({ className: 'errors/500' })
.description('Error returned when a unexpected error was thrown by the server.')
.keys({
statusCode: Joi.number().valid(500).example(500).required(),
Expand Down
55 changes: 28 additions & 27 deletions packages/trail-hapi-plugin/lib/schemas/trails.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
'use strict'

const config = require('config')
const Joi = require('@hapi/joi')
const j2s = require('joi-to-swagger')

const { errorsSchemas } = require('./errors')
const host = config.get('hapi.host')
const port = config.get('hapi.port')

const joiToSchema = joi => JSON.stringify(joi.describe())

const namedObject = function (name) {
return Joi.object()
.description(name)
Expand Down Expand Up @@ -44,14 +41,16 @@ const dateTime = Joi.string()
.isoDate()

const trailSchema = {
params: {
id: Joi.number()
.description('Trail id')
.meta({ id: 'models/trail.params.id' })
.required()
.min(0)
.example(12345)
},
params: Joi.object()
.meta({ className: 'trail/params' })
.keys({
id: Joi.number()
.description('Trail id')
.meta({ id: 'models/trail.params.id' })
.required()
.min(0)
.example(12345)
}),
search: Joi.object()
.description('An audit search')
.keys({
Expand Down Expand Up @@ -113,7 +112,7 @@ const trailSchema = {
.unknown(false),
request: Joi.object()
.description('A audit trail')
.meta({ id: 'models/trail.request' })
.meta({ className: 'trail/request' })
.keys({
when: dateTime,
who: stringOrObject('Trail actor'),
Expand All @@ -129,7 +128,7 @@ const trailSchema = {
.unknown(false),
response: Joi.object()
.description('A audit trail')
.meta({ id: 'models/trail.response' })
.meta({ className: 'trail/response' })
.keys({
id: Joi.number()
.description('Trail id')
Expand All @@ -152,6 +151,19 @@ const trailSchema = {
.unknown(false)
}

const components = [
trailSchema.params,
trailSchema.request,
trailSchema.response,
errorsSchemas[400],
errorsSchemas[404],
errorsSchemas[422],
errorsSchemas[500]
].reduce((components, joi, idx) => {
const result = j2s(joi, components)
return { schemas: Object.assign(components.schemas, result.components.schemas) }
}, { schemas: {} })

const spec = {
openapi: '3.0.1',
info: {
Expand Down Expand Up @@ -184,23 +196,12 @@ const spec = {
description: 'Endpoints for monitoring and uptime'
}
],
components: {
models: {
'trail.params.id': joiToSchema(trailSchema.params.id),
'trail.request': joiToSchema(trailSchema.request),
'trail.response': joiToSchema(trailSchema.response)
},
errors: {
400: joiToSchema(errorsSchemas['400']),
404: joiToSchema(errorsSchemas['404']),
422: joiToSchema(errorsSchemas['422']),
500: joiToSchema(errorsSchemas['500'])
}
},
components,
paths: {}
}

module.exports = {
trailSchema,
components,
spec
}
1 change: 1 addition & 0 deletions packages/trail-hapi-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
"@nearform/trail-core": "^3.0.0",
"boom": "^7.2.0",
"config": "^3.2.5",
"joi-to-swagger": "^4.0.0",
"lodash": "^4.17.15",
"luxon": "^1.2.0"
},
Expand Down

0 comments on commit af892c5

Please sign in to comment.