shelving
Version:
Toolkit for using data in JavaScript.
100 lines (99 loc) • 3.88 kB
JavaScript
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 });
}
}