import {InvalidArgumentError} from '@e22m4u/js-format'; /** * Validate projection schema. * * @param {object|Function|string} schema * @param {boolean} [shallowMode] * @param {Set} [validatedSchemas] */ export function validateProjectionSchema( schema, shallowMode = false, validatedSchemas = new Set(), ) { // если схема не является объектом, функцией // и не пустой строкой, то выбрасывается ошибка if ( !schema || (typeof schema !== 'object' && typeof schema !== 'function' && typeof schema !== 'string') || Array.isArray(schema) ) { throw new InvalidArgumentError( 'Projection schema must be an Object, a Function ' + 'or a non-empty String, but %v was given.', schema, ); } // если флаг поверхностного режима не является // логическим значением, то выбрасывается ошибка if (typeof shallowMode !== 'boolean') { throw new InvalidArgumentError( 'Argument "shallowMode" must be a Boolean, but %v was given.', shallowMode, ); } // если набор проверенных схем не является // экземпляром Set, то выбрасывается ошибка if (!(validatedSchemas instanceof Set)) { throw new InvalidArgumentError( 'Argument "validatedSchemas" must be ' + 'an instance of Set, but %v was given.', validatedSchemas, ); } // если схема уже была проверена, // то проверка пропускается if (validatedSchemas.has(schema)) { return; } // если схема не является объектом, // то проверка пропускается if (typeof schema !== 'object') { return; } // для исключения бесконечного цикла, // текущая схема добавляется в историю validatedSchemas.add(schema); // schema[k] Object.keys(schema).forEach(propName => { const propOptions = schema[propName]; if (propOptions === undefined) { return; } if ( propOptions === null || (typeof propOptions !== 'object' && typeof propOptions !== 'boolean') || Array.isArray(propOptions) ) { throw new InvalidArgumentError( 'Property options must be an Object or a Boolean, but %v was given.', propOptions, ); } if (typeof propOptions === 'boolean') { return; } // schema[k].select if ( propOptions.select !== undefined && typeof propOptions.select !== 'boolean' ) { throw new InvalidArgumentError( 'Property option "select" must be a Boolean, but %v was given.', propOptions.select, ); } // schema[k].scopes if (propOptions.scopes !== undefined) { if ( !propOptions.scopes || typeof propOptions.scopes !== 'object' || Array.isArray(propOptions.scopes) ) { throw new InvalidArgumentError( 'Property option "scopes" must be an Object, but %v was given.', propOptions.scopes, ); } Object.values(propOptions.scopes).forEach(scopeOptions => { if (scopeOptions === undefined) { return; } // schema[k].scopes[k] if ( scopeOptions === null || (typeof scopeOptions !== 'object' && typeof scopeOptions !== 'boolean') || Array.isArray(scopeOptions) ) { throw new InvalidArgumentError( 'Scope options must be an Object or a Boolean, but %v was given.', scopeOptions, ); } // schema[k].scopes[k].select if ( scopeOptions.select !== undefined && typeof scopeOptions.select !== 'boolean' ) { throw new InvalidArgumentError( 'Scope option "select" must be a Boolean, but %v was given.', scopeOptions.select, ); } }); } // schema[k].schema if (propOptions.schema !== undefined) { if ( !propOptions.schema || (typeof propOptions.schema !== 'object' && typeof propOptions.schema !== 'function' && typeof propOptions.schema !== 'string') || Array.isArray(propOptions.schema) ) { throw new InvalidArgumentError( 'Property option "schema" must be an Object, a Function' + ' or a non-empty String, but %v was given.', propOptions.schema, ); } if (!shallowMode && typeof propOptions.schema === 'object') { validateProjectionSchema( propOptions.schema, shallowMode, validatedSchemas, ); } } }); }