From 52311c4594f14f1086607553c17c013808ded89a Mon Sep 17 00:00:00 2001 From: Rafa Mel Date: Sun, 30 Sep 2018 06:39:50 +0200 Subject: [PATCH 1/2] Fixes #830 --- src/rx-document.js | 2 +- test/unit/rx-document.test.js | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/rx-document.js b/src/rx-document.js index 1b06c95eaef..f197366b535 100644 --- a/src/rx-document.js +++ b/src/rx-document.js @@ -377,7 +377,7 @@ export const basePrototype = { * instead we keep the values and only set _deleted: true * @return {Promise} */ - remove() { + async remove() { if (this.deleted) { throw RxError.newRxError('DOC13', { document: this, diff --git a/test/unit/rx-document.test.js b/test/unit/rx-document.test.js index b55ec120430..a5237fc7769 100644 --- a/test/unit/rx-document.test.js +++ b/test/unit/rx-document.test.js @@ -811,5 +811,21 @@ config.parallel('rx-document.test.js', () => { // clean up afterwards db.destroy(); }); + it('#830 RxDocument.remove() rejects -doesn\'t sync throw', async () => { + const c = await humansCollection.create(5); + const doc = await c.findOne().exec(); + await doc.remove(); + + let asyncE, syncE; + try { + await doc.remove().catch(e => (asyncE = e)); + } catch(e) { + syncE = e; + } + assert.equal(syncE, undefined); + assert.equal(asyncE instanceof Error, true); + + c.database.destroy(); + }); }); }); From ef12a7206e3b0cd8dc82e03fdeb6bb9f29003d58 Mon Sep 17 00:00:00 2001 From: Rafa Mel Date: Sun, 30 Sep 2018 07:15:31 +0200 Subject: [PATCH 2/2] Promise returning functions to async --- src/data-migrator.js | 12 ++++++------ src/hooks.js | 2 +- src/plugins/attachments.js | 6 +++--- src/plugins/in-memory.js | 8 ++++---- src/plugins/json-dump.js | 8 ++++---- src/plugins/leader-election.js | 2 +- src/plugins/local-documents.js | 4 ++-- src/plugins/watch-for-changes.js | 6 +++--- src/pouch-db.js | 4 ++-- src/rx-collection.js | 24 ++++++++++++------------ src/rx-database.js | 22 +++++++++++----------- src/rx-document.js | 10 +++++----- src/rx-query.js | 8 ++++---- src/util.js | 7 +++---- 14 files changed, 61 insertions(+), 62 deletions(-) diff --git a/src/data-migrator.js b/src/data-migrator.js index 9742ecace18..3d27019d242 100644 --- a/src/data-migrator.js +++ b/src/data-migrator.js @@ -150,11 +150,11 @@ class OldCollection { /** * @return {Promise} */ - countAllUndeleted() { + async countAllUndeleted() { return PouchDB.countAllUndeleted(this.pouchdb); } - getBatch(batchSize) { + async getBatch(batchSize) { return PouchDB .getBatch(this.pouchdb, batchSize) .then(docs => docs @@ -264,7 +264,7 @@ class OldCollection { * deletes this.pouchdb and removes it from the database.collectionsCollection * @return {Promise} */ - delete() { + async delete() { return this .pouchdb.destroy() .then(() => this.database.removeCollectionDoc(this.dataMigrator.name, this.schema)); @@ -328,7 +328,7 @@ class OldCollection { * get an array with OldCollection-instances from all existing old pouchdb-instance * @return {Promise} */ -export function _getOldCollections(dataMigrator) { +export async function _getOldCollections(dataMigrator) { return Promise .all( dataMigrator.currentSchema.previousVersions @@ -346,8 +346,8 @@ export function _getOldCollections(dataMigrator) { * returns true if a migration is needed * @return {Promise} */ -export function mustMigrate(dataMigrator) { - if (dataMigrator.currentSchema.version === 0) return Promise.resolve(false); +export async function mustMigrate(dataMigrator) { + if (dataMigrator.currentSchema.version === 0) return false; return _getOldCollections(dataMigrator) .then(oldCols => { if (oldCols.length === 0) return false; diff --git a/src/hooks.js b/src/hooks.js index 1b53fab9b34..5f0608d428d 100644 --- a/src/hooks.js +++ b/src/hooks.js @@ -66,7 +66,7 @@ export function runPluginHooks(hookKey, obj) { /** * @return {Promise} */ -export function runAsyncPluginHooks(hookKey, obj) { +export async function runAsyncPluginHooks(hookKey, obj) { return Promise.all( HOOKS[hookKey].map(fun => fun(obj)) ); diff --git a/src/plugins/attachments.js b/src/plugins/attachments.js index 5ee2a0bd66b..6b29ca6dc0f 100644 --- a/src/plugins/attachments.js +++ b/src/plugins/attachments.js @@ -19,7 +19,7 @@ function ensureSchemaSupportsAttachments(doc) { } } -function resyncRxDocument(doc) { +async function resyncRxDocument(doc) { return doc.collection.pouch.get(doc.primary).then(docData => { const data = doc.collection._handleFromPouch(docData); const changeEvent = RxChangeEvent.create( @@ -65,7 +65,7 @@ export const blobBufferUtil = { } return blobBuffer; }, - toString(blobBuffer) { + async toString(blobBuffer) { if (blobBuffer instanceof Buffer) { // node return nextTick() @@ -165,7 +165,7 @@ function shouldEncrypt(doc) { /** * @return {Promise} */ -export function putAttachment({ +export async function putAttachment({ id, data, type = 'text/plain' diff --git a/src/plugins/in-memory.js b/src/plugins/in-memory.js index 89f73417648..9a2edfc2798 100644 --- a/src/plugins/in-memory.js +++ b/src/plugins/in-memory.js @@ -137,8 +137,8 @@ export class InMemoryRxCollection extends RxCollection.RxCollection { * in the parent collection * @return {Promise} */ - awaitPersistence() { - if (this._nonPersistentRevisions.size === 0) return Promise.resolve(); + async awaitPersistence() { + if (this._nonPersistentRevisions.size === 0) return; return this._nonPersistentRevisionsSubject.pipe( filter(() => this._nonPersistentRevisions.size === 0), first() @@ -216,7 +216,7 @@ function toCleanSchema(rxSchema) { * @param {RxCollection} toCollection * @return {Promise<{}[]>} Promise that resolves with an array of the docs data */ -export function replicateExistingDocuments(fromCollection, toCollection) { +export async function replicateExistingDocuments(fromCollection, toCollection) { return fromCollection.pouch.allDocs({ attachments: false, include_docs: true @@ -247,7 +247,7 @@ export function replicateExistingDocuments(fromCollection, toCollection) { * @param {PouchDB} pouch * @return {Promise} */ -export function setIndexes(schema, pouch) { +export async function setIndexes(schema, pouch) { return Promise.all( schema.indexes .map(indexAr => { diff --git a/src/plugins/json-dump.js b/src/plugins/json-dump.js index 05ca14c9664..0e08f02d08b 100644 --- a/src/plugins/json-dump.js +++ b/src/plugins/json-dump.js @@ -11,7 +11,7 @@ import RxChangeEvent from '../rx-change-event'; /** * @return {Promise} */ -const dumpRxDatabase = function (decrypted = false, collections = null) { +const dumpRxDatabase = async function (decrypted = false, collections = null) { const json = { name: this.name, instanceToken: this.token, @@ -40,7 +40,7 @@ const dumpRxDatabase = function (decrypted = false, collections = null) { }); }; -const importDumpRxDatabase = function (dump) { +const importDumpRxDatabase = async function (dump) { /** * collections must be created before the import * because we do not know about the other collection-settings here @@ -60,7 +60,7 @@ const importDumpRxDatabase = function (dump) { ); }; -const dumpRxCollection = function (decrypted = false) { +const dumpRxCollection = async function (decrypted = false) { const encrypted = !decrypted; const json = { @@ -91,7 +91,7 @@ const dumpRxCollection = function (decrypted = false) { /** * @return {Promise} */ -const importDumpRxCollection = function (exportedJSON) { +const importDumpRxCollection = async function (exportedJSON) { // check schemaHash if (exportedJSON.schemaHash !== this.schema.hash) { throw RxError.newRxError('JD2', { diff --git a/src/plugins/leader-election.js b/src/plugins/leader-election.js index 268a89b66d9..1a79d3a09bd 100644 --- a/src/plugins/leader-election.js +++ b/src/plugins/leader-election.js @@ -20,7 +20,7 @@ class LeaderElector { /** * @return {Promise} promise which resolve when the instance becomes leader */ - waitForLeadership() { + async waitForLeadership() { return this.elector.awaitLeadership().then(() => { this.isLeader = true; return true; diff --git a/src/plugins/local-documents.js b/src/plugins/local-documents.js index 548032b4c58..14d3c0c210c 100644 --- a/src/plugins/local-documents.js +++ b/src/plugins/local-documents.js @@ -189,7 +189,7 @@ const RxLocalDocumentPrototype = { /** * @return {Promise} */ - remove() { + async remove() { const removeId = LOCAL_PREFIX + this.id; return this.parentPouch.remove(removeId, this._data._rev) .then(() => { @@ -263,7 +263,7 @@ const _getPouchByParent = parent => { * throws if already exists * @return {Promise} */ -const insertLocal = function (id, data) { +const insertLocal = async function (id, data) { if (RxCollection.isInstanceOf(this) && this._isInMemory) return this._parentCollection.insertLocal(id, data); diff --git a/src/plugins/watch-for-changes.js b/src/plugins/watch-for-changes.js index ffe603e07ae..bb1e96682c3 100644 --- a/src/plugins/watch-for-changes.js +++ b/src/plugins/watch-for-changes.js @@ -55,8 +55,8 @@ export function watchForChanges() { * @param {*} change * @return {Promise} */ -function _handleSingleChange(collection, change) { - if (change.id.charAt(0) === '_') return Promise.resolve(false); // do not handle changes of internal docs +async function _handleSingleChange(collection, change) { + if (change.id.charAt(0) === '_') return false; // do not handle changes of internal docs // wait 2 ticks and 20 ms to give the internal event-handling time to run return promiseWait(20) @@ -65,7 +65,7 @@ function _handleSingleChange(collection, change) { .then(() => { const docData = change.doc; // already handled by internal event-stream - if (collection._changeEventBuffer.hasChangeWithRevision(docData._rev)) return Promise.resolve(false); + if (collection._changeEventBuffer.hasChangeWithRevision(docData._rev)) return false; const cE = RxChangeEvent.fromPouchChange(docData, collection); diff --git a/src/pouch-db.js b/src/pouch-db.js index c771bcde958..b4630a31881 100644 --- a/src/pouch-db.js +++ b/src/pouch-db.js @@ -24,7 +24,7 @@ import RxError from './rx-error'; * @param {PouchDB} pouchdb instance * @return {Promise} number of documents */ -PouchDB.countAllUndeleted = function(pouchdb) { +PouchDB.countAllUndeleted = async function(pouchdb) { return pouchdb .allDocs({ include_docs: false, @@ -43,7 +43,7 @@ PouchDB.countAllUndeleted = function(pouchdb) { * @param {number} limit * @return {Promise<{}[]>} array with documents */ -PouchDB.getBatch = function(pouchdb, limit) { +PouchDB.getBatch = async function(pouchdb, limit) { if (limit <= 1) { throw RxError.newRxError('P1', { limit diff --git a/src/rx-collection.js b/src/rx-collection.js index 571b86a2edf..5ae48094838 100644 --- a/src/rx-collection.js +++ b/src/rx-collection.js @@ -67,7 +67,7 @@ export class RxCollection { _applyHookFunctions(this); } - prepare() { + async prepare() { this.pouch = this.database._spawnPouchDB(this.name, this.schema.version, this._pouchSettings); if (this.schema.doKeyCompression()) { @@ -250,7 +250,7 @@ export class RxCollection { * @param {[type]} key [description] * @return {[type]} [description] */ - _pouchGet(key) { + async _pouchGet(key) { return this .pouch .get(key) @@ -264,7 +264,7 @@ export class RxCollection { * @param {?boolean} noDecrypt if true, decryption will not be made * @return {Object[]} array with documents-data */ - _pouchFind(rxQuery, limit, noDecrypt = false) { + async _pouchFind(rxQuery, limit, noDecrypt = false) { const compressedQueryJSON = rxQuery.keyCompress(); if (limit) compressedQueryJSON.limit = limit; @@ -352,7 +352,7 @@ export class RxCollection { * @param {RxDocument} doc which was created * @return {Promise} */ - insert(json) { + async insert(json) { // inserting a temporary-document let tempDoc = null; if (RxDocument.isInstanceOf(json)) { @@ -413,7 +413,7 @@ export class RxCollection { * same as insert but overwrites existing document with same primary * @return {Promise} */ - upsert(json) { + async upsert(json) { json = clone(json); const primary = json[this.schema.primaryPath]; if (!primary) { @@ -441,7 +441,7 @@ export class RxCollection { * @param {object} json * @return {Promise} */ - atomicUpsert(json) { + async atomicUpsert(json) { json = clone(json); const primary = json[this.schema.primaryPath]; if (!primary) { @@ -606,9 +606,9 @@ export class RxCollection { /** * @return {Promise} */ - _runHooks(when, key, data, instance) { + async _runHooks(when, key, data, instance) { const hooks = this.getHooks(when, key); - if (!hooks) return Promise.resolve(); + if (!hooks) return; // run parallel: false const tasks = hooks.series.map(hook => () => hook(data, instance)); @@ -798,7 +798,7 @@ const checkOrmMethods = function(statics) { /** * @return {Promise} */ -function _atomicUpsertUpdate(doc, json) { +async function _atomicUpsertUpdate(doc, json) { return doc.atomicUpdate(innerDoc => { json._rev = innerDoc._rev; innerDoc._data = json; @@ -812,7 +812,7 @@ function _atomicUpsertUpdate(doc, json) { * @param {any} json * @return {Promise<{ doc: RxDocument, inserted: boolean}>} promise that resolves with new doc and flag if inserted */ -function _atomicUpsertEnsureRxDocumentExists(rxCollection, primary, json) { +async function _atomicUpsertEnsureRxDocumentExists(rxCollection, primary, json) { return rxCollection.findOne(primary).exec() .then(doc => { if (!doc) { @@ -848,7 +848,7 @@ export function getDocumentOrmPrototype(rxCollection) { /** * creates the indexes in the pouchdb */ -function _prepareCreateIndexes(rxCollection, spawnedPouchPromise) { +async function _prepareCreateIndexes(rxCollection, spawnedPouchPromise) { return Promise.all( rxCollection.schema.indexes .map(indexAr => { @@ -881,7 +881,7 @@ function _prepareCreateIndexes(rxCollection, spawnedPouchPromise) { * @param {?Object} [migrationStrategies={}] * @return {Promise} promise with collection */ -export function create({ +export async function create({ database, name, schema, diff --git a/src/rx-database.js b/src/rx-database.js index 5131ba6a2d5..c62c8f755bb 100644 --- a/src/rx-database.js +++ b/src/rx-database.js @@ -62,7 +62,7 @@ export class RxDatabase { ); } - dangerousRemoveCollectionInfo() { + async dangerousRemoveCollectionInfo() { const colPouch = this._collectionsPouch; return colPouch.allDocs() .then(docsRes => { @@ -102,8 +102,8 @@ export class RxDatabase { /** * @return {Promise} */ - waitForLeadership() { - if (!this.multiInstance) return Promise.resolve(true); + async waitForLeadership() { + if (!this.multiInstance) return true; return this.leaderElector.waitForLeadership(); } @@ -135,9 +135,9 @@ export class RxDatabase { /** * removes the collection-doc from this._collectionsPouch - * @return {Promise} + * } */ - removeCollectionDoc(name, schema) { + async removeCollectionDoc(name, schema) { const docId = _collectionNamePrimary(name, schema); return this ._collectionsPouch @@ -351,7 +351,7 @@ export class RxDatabase { * deletes the database and its stored data * @return {Promise} */ - remove() { + async remove() { return this .destroy() .then(() => removeDatabase(this.name, this.adapter)); @@ -472,7 +472,7 @@ export async function _ensureStorageTokenExists(rxDatabase) { * @param {RxChangeEvent} changeEvent * @return {Promise} */ -export function writeToSocket(rxDatabase, changeEvent) { +export async function writeToSocket(rxDatabase, changeEvent) { if ( rxDatabase.multiInstance && !changeEvent.isIntern() && @@ -488,7 +488,7 @@ export function writeToSocket(rxDatabase, changeEvent) { }; return rxDatabase.broadcastChannel.postMessage(sendOverChannel); } else - return Promise.resolve(false); + return false; } /** @@ -506,7 +506,7 @@ export function _collectionNamePrimary(name, schema) { * @param {string} collectionName * @return {Promise} resolves all known collection-versions */ -export function _removeAllOfCollection(rxDatabase, collectionName) { +export async function _removeAllOfCollection(rxDatabase, collectionName) { return rxDatabase.lockedRun( () => rxDatabase._collectionsPouch.allDocs({ @@ -577,7 +577,7 @@ async function prepare(rxDatabase) { } -export function create({ +export async function create({ name, adapter, password, @@ -720,7 +720,7 @@ export async function removeDatabase(databaseName, adapter) { * check is the given adapter can be used * @return {Promise} */ -export function checkAdapter(adapter) { +export async function checkAdapter(adapter) { return overwritable.checkAdapter(adapter); } diff --git a/src/rx-document.js b/src/rx-document.js index f197366b535..abafa59c8be 100644 --- a/src/rx-document.js +++ b/src/rx-document.js @@ -148,7 +148,7 @@ export const basePrototype = { * @param {string} path * @return {Promise} */ - populate(path) { + async populate(path) { const schemaObj = this.collection.schema.getSchemaByObjectPath(path); const value = this.get(path); if (!schemaObj) { @@ -274,7 +274,7 @@ export const basePrototype = { * @param {function(any)} fun that takes the document-data and returns a new data-object * @return {Promise} */ - atomicUpdate(fun) { + async atomicUpdate(fun) { this._atomicQueue = this._atomicQueue .then(async () => { const oldData = clone(this._dataSync$.getValue()); @@ -302,7 +302,7 @@ export const basePrototype = { * @param {any} oldData * @return {Promise} */ - _saveData(newData, oldData) { + async _saveData(newData, oldData) { newData = clone(newData); @@ -348,9 +348,9 @@ export const basePrototype = { /** * saves the temporary document and makes a non-temporary out of it * Saving a temporary doc is basically the same as RxCollection.insert() - * @return {boolean} false if nothing to save + * @return {Promise} false if nothing to save */ - save() { + async save() { // .save() cannot be called on non-temporary-documents if (!this._isTemporary) { throw RxError.newRxError('DOC17', { diff --git a/src/rx-query.js b/src/rx-query.js index 9d618e9d402..e17900bf3bd 100644 --- a/src/rx-query.js +++ b/src/rx-query.js @@ -98,7 +98,7 @@ export class RxQuery { * executes the query on the database * @return {Promise<{}[]>} results-array with document-data */ - _execOverDatabase() { + async _execOverDatabase() { this._execOverDatabaseCount = this._execOverDatabaseCount + 1; let docsPromise; @@ -293,7 +293,7 @@ export class RxQuery { * deletes all found documents * @return {Promise(RxDocument|RxDocument[])} promise with deleted documents */ - remove() { + async remove() { let ret; return this .exec() @@ -311,7 +311,7 @@ export class RxQuery { * @param {object} updateObj * @return {Promise(RxDocument|RxDocument[])} promise with updated documents */ - update() { + async update() { throw RxError.pluginMissing('update'); } @@ -477,7 +477,7 @@ function _isResultsInSync(rxQuery) { * wraps __ensureEqual() * to ensure it does not run in parallel */ -function _ensureEqual(rxQuery) { +async function _ensureEqual(rxQuery) { rxQuery._ensureEqualQueue = rxQuery._ensureEqualQueue .then(() => new Promise(res => setTimeout(res, 0))) .then(() => __ensureEqual(rxQuery)) diff --git a/src/util.js b/src/util.js index 70c2efe76d6..a9a3ea4cda7 100644 --- a/src/util.js +++ b/src/util.js @@ -81,7 +81,7 @@ export function promiseWait(ms = 0) { return new Promise(res => setTimeout(res, ms)); } -export function requestIdlePromise(timeout = null) { +export async function requestIdlePromise(timeout = null) { if ( typeof window === 'object' && window.requestIdleCallback @@ -91,8 +91,7 @@ export function requestIdlePromise(timeout = null) { timeout }) ); - } else - return Promise.resolve(); + } } @@ -102,7 +101,7 @@ export function requestIdlePromise(timeout = null) { * @param {Function[]} tasks array with functions that return a promise * @return {Promise} */ -export function promiseSeries(tasks, initial) { +export async function promiseSeries(tasks, initial) { return tasks .reduce( (current, next) => current.then(next),