UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

100 lines (99 loc) 3.88 kB
import { ValueError } from "../../error/ValueError.js"; import { PARTIAL } from "../../schema/DataSchema.js"; import { getNamedMessage } from "../../util/error.js"; import { ThroughDBProvider } from "./ThroughDBProvider.js"; /** Validate an asynchronous source provider (source can have any type because validation guarantees the type). */ export class ValidationDBProvider extends ThroughDBProvider { async getItem(collection, id) { return _validateItem(collection, await super.getItem(collection, id), this.getItem); } async *getItemSequence(collection, id) { for await (const item of super.getItemSequence(collection, id)) yield _validateItem(collection, item, this.getItemSequence); } async addItem(collection, data) { return _validateIdentifier(collection, await super.addItem(collection, collection.validate(data)), this.addItem); } setItem(collection, id, data) { return super.setItem(collection, id, collection.validate(data)); } updateItem(collection, id, updates) { return super.updateItem(collection, id, _validateUpdates(collection, updates, this.updateItem)); } countQuery(collection, query) { return super.countQuery(collection, query); } async getQuery(collection, query) { return _validateItems(collection, await super.getQuery(collection, query), this.getQuery); } async *getQuerySequence(collection, query) { for await (const items of super.getQuerySequence(collection, query)) yield _validateItems(collection, items, this.getQuerySequence); } setQuery(collection, query, data) { return super.setQuery(collection, query, collection.validate(data)); } updateQuery(collection, query, updates) { return super.updateQuery(collection, query, _validateUpdates(collection, updates, this.updateQuery)); } deleteQuery(collection, query) { return super.deleteQuery(collection, query); } } /** * Validate a returned `id` for a collection item. */ function _validateIdentifier(collection, id, caller) { try { return collection.id.validate(id); } catch (thrown) { if (typeof thrown !== "string") throw thrown; throw new ValueError(`Invalid identifier for "${collection}"\n${thrown}`, { received: id, caller }); } } function _validateItem(collection, item, caller) { if (!item) return undefined; try { return collection.item.validate(item); } catch (thrown) { if (typeof thrown !== "string") throw thrown; throw new ValueError(`Invalid data for "${collection}"\n${thrown}`, { received: item, caller }); } } /** * Validate a set of entities for this query reference. * @throws `ValueError` if one or more items did not validate (conflict because the program is not in an expected state). */ function _validateItems(collection, items, caller) { return Array.from(_yieldValidItems(collection, items, caller)); } function* _yieldValidItems(collection, items, caller) { const messages = []; for (const item of items) { try { yield collection.item.validate(item); } catch (thrown) { if (typeof thrown !== "string") throw thrown; messages.push(getNamedMessage(item.id, thrown)); } } if (messages.length) throw new ValueError(`Invalid data for "${collection}"\n${messages.join("\n")}`, { received: items, caller }); } function _validateUpdates(collection, updates, caller) { try { return PARTIAL(collection).validate(updates); } catch (thrown) { if (typeof thrown !== "string") throw thrown; throw new ValueError(`Invalid updates for "${collection}"\n${thrown}`, { received: updates, caller }); } }