UNPKG

rxdb

Version:

A local-first realtime NoSQL Database for JavaScript applications - https://rxdb.info/

222 lines (216 loc) 8.05 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.wrapRxStorageInstance = wrapRxStorageInstance; exports.wrappedValidateStorageFactory = wrappedValidateStorageFactory; var _operators = require("rxjs/operators"); var _rxSchemaHelper = require("./rx-schema-helper.js"); var _index = require("./plugins/utils/index.js"); var _rxjs = require("rxjs"); /** * Returns the validation errors. * If document is fully valid, returns an empty array. */ /** * cache the validators by the schema string * so we can reuse them when multiple collections have the same schema * * Notice: to make it easier and not dependent on a hash function, * we use the plain json string. */ var VALIDATOR_CACHE_BY_VALIDATOR_KEY = new Map(); /** * This factory is used in the validation plugins * so that we can reuse the basic storage wrapping code. */ function wrappedValidateStorageFactory( /** * Returns a method that can be used to validate * documents and throws when the document is not valid. */ getValidator, /** * A string to identify the validation library. */ validatorKey) { var VALIDATOR_CACHE = (0, _index.getFromMapOrCreate)(VALIDATOR_CACHE_BY_VALIDATOR_KEY, validatorKey, () => new Map()); function initValidator(schema) { return (0, _index.getFromMapOrCreate)(VALIDATOR_CACHE, JSON.stringify(schema), () => getValidator(schema)); } return args => { return Object.assign({}, args.storage, { name: 'validate-' + validatorKey + '-' + args.storage.name, async createStorageInstance(params) { var instance = await args.storage.createStorageInstance(params); var primaryPath = (0, _rxSchemaHelper.getPrimaryFieldOfPrimaryKey)(params.schema.primaryKey); /** * Lazy initialize the validator * to save initial page load performance. * Some libraries take really long to initialize the validator * from the schema. */ var validatorCached; (0, _index.requestIdleCallbackIfAvailable)(() => validatorCached = initValidator(params.schema)); var oldBulkWrite = instance.bulkWrite.bind(instance); instance.bulkWrite = (documentWrites, context) => { if (!validatorCached) { validatorCached = initValidator(params.schema); } var errors = []; var continueWrites = []; documentWrites.forEach(row => { var documentId = row.document[primaryPath]; var validationErrors = validatorCached(row.document); if (validationErrors.length > 0) { errors.push({ status: 422, isError: true, documentId, writeRow: row, validationErrors, schema: instance.schema }); } else { continueWrites.push(row); } }); var writePromise = continueWrites.length > 0 ? oldBulkWrite(continueWrites, context) : Promise.resolve({ error: [], success: [] }); return writePromise.then(writeResult => { errors.forEach(validationError => { writeResult.error.push(validationError); }); return writeResult; }); }; return instance; } }); }; } /** * Used in plugins to easily modify all in- and outgoing * data of that storage instance. */ function wrapRxStorageInstance(originalSchema, instance, modifyToStorage, modifyFromStorage, modifyAttachmentFromStorage = v => v) { async function toStorage(docData) { if (!docData) { return docData; } return await modifyToStorage(docData); } async function fromStorage(docData) { if (!docData) { return docData; } return await modifyFromStorage(docData); } async function errorFromStorage(error) { var ret = (0, _index.flatClone)(error); ret.writeRow = (0, _index.flatClone)(ret.writeRow); if (ret.documentInDb) { ret.documentInDb = await fromStorage(ret.documentInDb); } if (ret.writeRow.previous) { ret.writeRow.previous = await fromStorage(ret.writeRow.previous); } ret.writeRow.document = await fromStorage(ret.writeRow.document); return ret; } var processingChangesCount$ = new _rxjs.BehaviorSubject(0); var wrappedInstance = { databaseName: instance.databaseName, internals: instance.internals, cleanup: instance.cleanup.bind(instance), options: instance.options, close: instance.close.bind(instance), schema: originalSchema, collectionName: instance.collectionName, count: instance.count.bind(instance), remove: instance.remove.bind(instance), originalStorageInstance: instance, bulkWrite: async (documentWrites, context) => { var useRows = []; await Promise.all(documentWrites.map(async row => { var [previous, document] = await Promise.all([row.previous ? toStorage(row.previous) : undefined, toStorage(row.document)]); useRows.push({ previous, document }); })); var writeResult = await instance.bulkWrite(useRows, context); var ret = { error: [] }; var promises = []; writeResult.error.forEach(error => { promises.push(errorFromStorage(error).then(err => ret.error.push(err))); }); await Promise.all(promises); /** * By definition, all change events must be emitted * BEFORE the write call resolves. * To ensure that even when the modifiers are async, * we wait here until the processing queue is empty. */ await (0, _rxjs.firstValueFrom)(processingChangesCount$.pipe((0, _operators.filter)(v => v === 0))); return ret; }, query: preparedQuery => { return instance.query(preparedQuery).then(queryResult => { return Promise.all(queryResult.documents.map(doc => fromStorage(doc))); }).then(documents => ({ documents: documents })); }, getAttachmentData: async (documentId, attachmentId, digest) => { var data = await instance.getAttachmentData(documentId, attachmentId, digest); data = await modifyAttachmentFromStorage(data); return data; }, findDocumentsById: (ids, deleted) => { return instance.findDocumentsById(ids, deleted).then(async findResult => { var ret = []; await Promise.all(findResult.map(async doc => { ret.push(await fromStorage(doc)); })); return ret; }); }, getChangedDocumentsSince: !instance.getChangedDocumentsSince ? undefined : (limit, checkpoint) => { return instance.getChangedDocumentsSince(limit, checkpoint).then(async result => { return { checkpoint: result.checkpoint, documents: await Promise.all(result.documents.map(d => fromStorage(d))) }; }); }, changeStream: () => { return instance.changeStream().pipe((0, _operators.tap)(() => processingChangesCount$.next(processingChangesCount$.getValue() + 1)), (0, _operators.mergeMap)(async eventBulk => { var useEvents = await Promise.all(eventBulk.events.map(async event => { var [documentData, previousDocumentData] = await Promise.all([fromStorage(event.documentData), fromStorage(event.previousDocumentData)]); var ev = { operation: event.operation, documentId: event.documentId, documentData: documentData, previousDocumentData: previousDocumentData, isLocal: false }; return ev; })); var ret = { id: eventBulk.id, events: useEvents, checkpoint: eventBulk.checkpoint, context: eventBulk.context }; return ret; }), (0, _operators.tap)(() => processingChangesCount$.next(processingChangesCount$.getValue() - 1))); } }; return wrappedInstance; } //# sourceMappingURL=plugin-helpers.js.map