You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					128 lines
				
				3.5 KiB
			
		
		
			
		
	
	
					128 lines
				
				3.5 KiB
			| 
											3 years ago
										 | 'use strict' | ||
|  | 
 | ||
|  | const fastClone = require('rfdc')({ circles: false, proto: true }) | ||
|  | const { kSchemaVisited } = require('./symbols') | ||
|  | const kFluentSchema = Symbol.for('fluent-schema-object') | ||
|  | 
 | ||
|  | const { | ||
|  |   FST_ERR_SCH_MISSING_ID, | ||
|  |   FST_ERR_SCH_ALREADY_PRESENT, | ||
|  |   FST_ERR_SCH_DUPLICATE | ||
|  | } = require('./errors') | ||
|  | 
 | ||
|  | const SCHEMAS_SOURCE = ['params', 'body', 'querystring', 'query', 'headers'] | ||
|  | 
 | ||
|  | function Schemas (initStore) { | ||
|  |   this.store = initStore || {} | ||
|  | } | ||
|  | 
 | ||
|  | Schemas.prototype.add = function (inputSchema) { | ||
|  |   const schema = fastClone((inputSchema.isFluentSchema || inputSchema.isFluentJSONSchema || inputSchema[kFluentSchema]) | ||
|  |     ? inputSchema.valueOf() | ||
|  |     : inputSchema | ||
|  |   ) | ||
|  | 
 | ||
|  |   // devs can add schemas without $id, but with $def instead
 | ||
|  |   const id = schema.$id | ||
|  |   if (!id) { | ||
|  |     throw new FST_ERR_SCH_MISSING_ID() | ||
|  |   } | ||
|  | 
 | ||
|  |   if (this.store[id]) { | ||
|  |     throw new FST_ERR_SCH_ALREADY_PRESENT(id) | ||
|  |   } | ||
|  | 
 | ||
|  |   this.store[id] = schema | ||
|  | } | ||
|  | 
 | ||
|  | Schemas.prototype.getSchemas = function () { | ||
|  |   return Object.assign({}, this.store) | ||
|  | } | ||
|  | 
 | ||
|  | Schemas.prototype.getSchema = function (schemaId) { | ||
|  |   return this.store[schemaId] | ||
|  | } | ||
|  | 
 | ||
|  | function normalizeSchema (routeSchemas, serverOptions) { | ||
|  |   if (routeSchemas[kSchemaVisited]) { | ||
|  |     return routeSchemas | ||
|  |   } | ||
|  | 
 | ||
|  |   // alias query to querystring schema
 | ||
|  |   if (routeSchemas.query) { | ||
|  |     // check if our schema has both querystring and query
 | ||
|  |     if (routeSchemas.querystring) { | ||
|  |       throw new FST_ERR_SCH_DUPLICATE('querystring') | ||
|  |     } | ||
|  |     routeSchemas.querystring = routeSchemas.query | ||
|  |   } | ||
|  | 
 | ||
|  |   generateFluentSchema(routeSchemas) | ||
|  | 
 | ||
|  |   // let's check if our schemas have a custom prototype
 | ||
|  |   for (const key of ['headers', 'querystring', 'params', 'body']) { | ||
|  |     if (typeof routeSchemas[key] === 'object' && Object.getPrototypeOf(routeSchemas[key]) !== Object.prototype) { | ||
|  |       return routeSchemas | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   if (routeSchemas.body) { | ||
|  |     routeSchemas.body = getSchemaAnyway(routeSchemas.body, serverOptions.jsonShorthand) | ||
|  |   } | ||
|  | 
 | ||
|  |   if (routeSchemas.headers) { | ||
|  |     routeSchemas.headers = getSchemaAnyway(routeSchemas.headers, serverOptions.jsonShorthand) | ||
|  |   } | ||
|  | 
 | ||
|  |   if (routeSchemas.querystring) { | ||
|  |     routeSchemas.querystring = getSchemaAnyway(routeSchemas.querystring, serverOptions.jsonShorthand) | ||
|  |   } | ||
|  | 
 | ||
|  |   if (routeSchemas.params) { | ||
|  |     routeSchemas.params = getSchemaAnyway(routeSchemas.params, serverOptions.jsonShorthand) | ||
|  |   } | ||
|  | 
 | ||
|  |   if (routeSchemas.response) { | ||
|  |     const httpCodes = Object.keys(routeSchemas.response) | ||
|  |     for (const code of httpCodes) { | ||
|  |       routeSchemas.response[code] = getSchemaAnyway(routeSchemas.response[code], serverOptions.jsonShorthand) | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   routeSchemas[kSchemaVisited] = true | ||
|  |   return routeSchemas | ||
|  | } | ||
|  | 
 | ||
|  | function generateFluentSchema (schema) { | ||
|  |   for (const key of SCHEMAS_SOURCE) { | ||
|  |     if (schema[key] && (schema[key].isFluentSchema || schema[key][kFluentSchema])) { | ||
|  |       schema[key] = schema[key].valueOf() | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   if (schema.response) { | ||
|  |     const httpCodes = Object.keys(schema.response) | ||
|  |     for (const code of httpCodes) { | ||
|  |       if (schema.response[code].isFluentSchema || schema.response[code][kFluentSchema]) { | ||
|  |         schema.response[code] = schema.response[code].valueOf() | ||
|  |       } | ||
|  |     } | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | function getSchemaAnyway (schema, jsonShorthand) { | ||
|  |   if (!jsonShorthand || schema.$ref || schema.oneOf || schema.allOf || schema.anyOf || schema.$merge || schema.$patch) return schema | ||
|  |   if (!schema.type && !schema.properties) { | ||
|  |     return { | ||
|  |       type: 'object', | ||
|  |       properties: schema | ||
|  |     } | ||
|  |   } | ||
|  |   return schema | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = { | ||
|  |   buildSchemas (initStore) { return new Schemas(initStore) }, | ||
|  |   normalizeSchema | ||
|  | } |