UNPKG

4.02 kBJavaScriptView Raw
1'use strict'
2
3const fastClone = require('rfdc')({ circles: false, proto: true })
4
5const {
6 codes: {
7 FST_ERR_SCH_MISSING_ID,
8 FST_ERR_SCH_ALREADY_PRESENT,
9 FST_ERR_SCH_NOT_PRESENT
10 }
11} = require('./errors')
12
13const URI_NAME_FRAGMENT = /^#[A-Za-z]{1}[\w-:.]{0,}$/
14
15function Schemas () {
16 this.store = {}
17}
18
19Schemas.prototype.add = function (inputSchema) {
20 const schema = fastClone(inputSchema)
21 const id = schema['$id']
22 if (id === undefined) {
23 throw new FST_ERR_SCH_MISSING_ID()
24 }
25
26 if (this.store[id] !== undefined) {
27 throw new FST_ERR_SCH_ALREADY_PRESENT(id)
28 }
29
30 this.store[id] = this.resolveRefs(schema, true)
31}
32
33Schemas.prototype.resolve = function (id) {
34 if (this.store[id] === undefined) {
35 throw new FST_ERR_SCH_NOT_PRESENT(id)
36 }
37 return this.store[id]
38}
39
40Schemas.prototype.resolveRefs = function (routeSchemas, dontClearId) {
41 // let's check if our schemas have a custom prototype
42 for (const key of ['headers', 'querystring', 'params', 'body']) {
43 if (typeof routeSchemas[key] === 'object' && Object.getPrototypeOf(routeSchemas[key]) !== Object.prototype) {
44 return routeSchemas
45 }
46 }
47
48 try {
49 // this will work only for standard json schemas
50 // other compilers such as Joi will fail
51 this.traverse(routeSchemas)
52
53 // when a plugin uses the 'skip-override' and call addSchema
54 // the same JSON will be pass throug all the avvio tree. In this case
55 // it is not possible clean the id. The id will be cleared
56 // in the startup phase by the call of validation.js. Details PR #1496
57 if (dontClearId !== true) {
58 this.cleanId(routeSchemas)
59 }
60 } catch (err) {
61 // if we have failed because `resolve has thrown
62 // let's rethrow the error and let avvio handle it
63 if (/FST_ERR_SCH_*/.test(err.code)) throw err
64 }
65
66 if (routeSchemas.headers) {
67 routeSchemas.headers = this.getSchemaAnyway(routeSchemas.headers)
68 }
69
70 if (routeSchemas.querystring) {
71 routeSchemas.querystring = this.getSchemaAnyway(routeSchemas.querystring)
72 }
73
74 if (routeSchemas.params) {
75 routeSchemas.params = this.getSchemaAnyway(routeSchemas.params)
76 }
77
78 return routeSchemas
79}
80
81Schemas.prototype.traverse = function (schema) {
82 for (var key in schema) {
83 // resolve the `sharedSchemaId#' only if is not a standard $ref JSON Pointer
84 if (typeof schema[key] === 'string' && key !== '$schema' && key !== '$ref' && schema[key].slice(-1) === '#') {
85 schema[key] = this.resolve(schema[key].slice(0, -1))
86 }
87
88 if (schema[key] !== null && typeof schema[key] === 'object') {
89 this.traverse(schema[key])
90 }
91 }
92}
93
94Schemas.prototype.cleanId = function (schema) {
95 for (var key in schema) {
96 if (key === '$id' && !URI_NAME_FRAGMENT.test(schema[key])) {
97 delete schema[key]
98 }
99 if (schema[key] !== null && typeof schema[key] === 'object') {
100 this.cleanId(schema[key])
101 }
102 }
103}
104
105Schemas.prototype.getSchemaAnyway = function (schema) {
106 if (schema.oneOf || schema.allOf || schema.anyOf) return schema
107 if (!schema.type || !schema.properties) {
108 return {
109 type: 'object',
110 properties: schema
111 }
112 }
113 return schema
114}
115
116Schemas.prototype.getSchemas = function () {
117 return Object.assign({}, this.store)
118}
119
120Schemas.prototype.getJsonSchemas = function (options) {
121 const store = this.getSchemas()
122 const schemasArray = Object.keys(store).map(schemaKey => {
123 // if the shared-schema "replace-way" has been used, the $id field has been removed
124 if (store[schemaKey]['$id'] === undefined) {
125 store[schemaKey]['$id'] = schemaKey
126 }
127 return store[schemaKey]
128 })
129
130 if (options && options.onlyAbsoluteUri === true) {
131 // the caller wants only the absolute URI (without the shared schema - "replace-way" usage)
132 return schemasArray.filter(_ => !/^\w*$/g.test(_.$id))
133 }
134 return schemasArray
135}
136
137function buildSchemas (s) {
138 const schema = new Schemas()
139 s.getJsonSchemas().forEach(_ => schema.add(_))
140 return schema
141}
142
143module.exports = { Schemas, buildSchemas }