UNPKG

3.46 kBJavaScriptView Raw
1'use strict'
2
3const fastClone = require('rfdc')({ circles: false, proto: true })
4const { kSchemaVisited } = require('./symbols')
5const kFluentSchema = Symbol.for('fluent-schema-object')
6
7const {
8 FST_ERR_SCH_MISSING_ID,
9 FST_ERR_SCH_ALREADY_PRESENT,
10 FST_ERR_SCH_DUPLICATE
11} = require('./errors')
12
13function Schemas () {
14 this.store = {}
15 this.newSchemasAdded = false
16}
17
18Schemas.prototype.add = function (inputSchema) {
19 var schema = fastClone((inputSchema.isFluentSchema || inputSchema[kFluentSchema])
20 ? inputSchema.valueOf()
21 : inputSchema
22 )
23
24 // devs can add schemas without $id, but with $def instead
25 const id = schema.$id
26 if (!id) {
27 throw new FST_ERR_SCH_MISSING_ID()
28 }
29
30 if (this.store[id]) {
31 throw new FST_ERR_SCH_ALREADY_PRESENT(id)
32 }
33
34 this.store[id] = schema
35 this.newSchemasAdded = true
36}
37
38Schemas.prototype.getSchemas = function () {
39 return Object.assign({}, this.store)
40}
41
42Schemas.prototype.getSchema = function (schemaId) {
43 return this.store[schemaId]
44}
45
46Schemas.prototype.hasNewSchemas = function () {
47 return this.newSchemasAdded
48}
49
50function normalizeSchema (routeSchemas) {
51 if (routeSchemas[kSchemaVisited]) {
52 return routeSchemas
53 }
54
55 // alias query to querystring schema
56 if (routeSchemas.query) {
57 // check if our schema has both querystring and query
58 if (routeSchemas.querystring) {
59 throw new FST_ERR_SCH_DUPLICATE('querystring')
60 }
61 routeSchemas.querystring = routeSchemas.query
62 }
63
64 generateFluentSchema(routeSchemas)
65
66 // let's check if our schemas have a custom prototype
67 for (const key of ['headers', 'querystring', 'params', 'body']) {
68 if (typeof routeSchemas[key] === 'object' && Object.getPrototypeOf(routeSchemas[key]) !== Object.prototype) {
69 return routeSchemas
70 }
71 }
72
73 if (routeSchemas.body) {
74 routeSchemas.body = getSchemaAnyway(routeSchemas.body)
75 }
76
77 if (routeSchemas.headers) {
78 routeSchemas.headers = getSchemaAnyway(routeSchemas.headers)
79 }
80
81 if (routeSchemas.querystring) {
82 routeSchemas.querystring = getSchemaAnyway(routeSchemas.querystring)
83 }
84
85 if (routeSchemas.params) {
86 routeSchemas.params = getSchemaAnyway(routeSchemas.params)
87 }
88
89 if (routeSchemas.response) {
90 Object.keys(routeSchemas.response).forEach(code => {
91 routeSchemas.response[code] = getSchemaAnyway(routeSchemas.response[code])
92 })
93 }
94
95 routeSchemas[kSchemaVisited] = true
96 return routeSchemas
97}
98
99function generateFluentSchema (schema) {
100 ;['params', 'body', 'querystring', 'query', 'headers'].forEach(key => {
101 if (schema[key] && (schema[key].isFluentSchema || schema[key][kFluentSchema])) {
102 schema[key] = schema[key].valueOf()
103 }
104 })
105
106 if (schema.response) {
107 Object.keys(schema.response).forEach(code => {
108 if (schema.response[code].isFluentSchema || schema.response[code][kFluentSchema]) {
109 schema.response[code] = schema.response[code].valueOf()
110 }
111 })
112 }
113}
114
115function getSchemaAnyway (schema) {
116 if (schema.$ref || schema.oneOf || schema.allOf || schema.anyOf || schema.$merge || schema.$patch) return schema
117 if (!schema.type && !schema.properties) {
118 return {
119 type: 'object',
120 properties: schema
121 }
122 }
123 return schema
124}
125
126function buildSchemas (s) {
127 const schema = new Schemas()
128 Object.values(s.getSchemas()).forEach(_ => schema.add(_))
129 schema.newSchemasAdded = false
130 return schema
131}
132
133module.exports = {
134 Schemas,
135 buildSchemas,
136 normalizeSchema
137}