UNPKG

mongoose

Version:
106 lines (92 loc) 3.68 kB
'use strict'; /*! * ignore */ const SchemaUnionOptions = require('../options/schemaUnionOptions'); const SchemaType = require('../schemaType'); const firstValueSymbol = Symbol('firstValue'); /*! * ignore */ class Union extends SchemaType { constructor(key, options, schemaOptions = {}) { super(key, options, 'Union'); if (!options || !Array.isArray(options.of) || options.of.length === 0) { throw new Error('Union schema type requires an array of types'); } this.schemaTypes = options.of.map(obj => options.parentSchema.interpretAsType(key, obj, schemaOptions)); } cast(val, doc, init, prev, options) { let firstValue = firstValueSymbol; let lastError; // Loop through each schema type in the union. If one of the schematypes returns a value that is `=== val`, then // use `val`. Otherwise, if one of the schematypes casted successfully, use the first successfully casted value. // Finally, if none of the schematypes casted successfully, throw the error from the last schema type in the union. // The `=== val` check is a workaround to ensure that the original value is returned if it matches one of the schema types, // avoiding cases like where numbers are casted to strings or dates even if the schema type is a number. for (let i = 0; i < this.schemaTypes.length; ++i) { try { const casted = this.schemaTypes[i].cast(val, doc, init, prev, options); if (casted === val) { return casted; } if (firstValue === firstValueSymbol) { firstValue = casted; } } catch (error) { lastError = error; } } if (firstValue !== firstValueSymbol) { return firstValue; } throw lastError; } // Setters also need to be aware of casting - we need to apply the setters of the entry in the union we choose. applySetters(val, doc, init, prev, options) { let firstValue = firstValueSymbol; let lastError; // Loop through each schema type in the union. If one of the schematypes returns a value that is `=== val`, then // use `val`. Otherwise, if one of the schematypes casted successfully, use the first successfully casted value. // Finally, if none of the schematypes casted successfully, throw the error from the last schema type in the union. // The `=== val` check is a workaround to ensure that the original value is returned if it matches one of the schema types, // avoiding cases like where numbers are casted to strings or dates even if the schema type is a number. for (let i = 0; i < this.schemaTypes.length; ++i) { try { let castedVal = this.schemaTypes[i]._applySetters(val, doc, init, prev, options); if (castedVal == null) { castedVal = this.schemaTypes[i]._castNullish(castedVal); } else { castedVal = this.schemaTypes[i].cast(castedVal, doc, init, prev, options); } if (castedVal === val) { return castedVal; } if (firstValue === firstValueSymbol) { firstValue = castedVal; } } catch (error) { lastError = error; } } if (firstValue !== firstValueSymbol) { return firstValue; } throw lastError; } clone() { const schematype = super.clone(); schematype.schemaTypes = this.schemaTypes.map(schemaType => schemaType.clone()); return schematype; } } /** * This schema type's name, to defend against minifiers that mangle * function names. * * @api public */ Union.schemaName = 'Union'; Union.defaultOptions = {}; Union.prototype.OptionsConstructor = SchemaUnionOptions; module.exports = Union;