From 27f533d7354e22daca54ae9f4d464f8e3e917e9d Mon Sep 17 00:00:00 2001 From: Pranav Babu Date: Mon, 21 Oct 2024 11:52:00 +0200 Subject: [PATCH 1/2] Add model name and raw data info to errors --- src/Model/index.js | 8 ++++---- src/Model/test.js | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Model/index.js b/src/Model/index.js index 29596fa00..9096edb02 100644 --- a/src/Model/index.js +++ b/src/Model/index.js @@ -125,7 +125,7 @@ export default class Model { prepareUpdate(recordUpdater: (this) => void = noop): this { invariant( !this._preparedState, - `Cannot update a record with pending changes (${this.__debugName})`, + `Cannot update a record with pending changes for model ${this.constructor.name}, Raw Data: ${JSON.stringify(this._raw)} (${this.__debugName})`, ) this.__ensureNotDisposable(`Model.prepareUpdate()`) this._isEditing = true @@ -184,7 +184,7 @@ export default class Model { prepareMarkAsDeleted(): this { invariant( !this._preparedState, - `Cannot mark a record with pending changes as deleted (${this.__debugName})`, + `Cannot mark a record with pending changes as deleted for model ${this.constructor.name}, Raw Data: ${JSON.stringify(this._raw)} (${this.__debugName})`, ) this.__ensureNotDisposable(`Model.prepareMarkAsDeleted()`) this._raw._status = 'deleted' @@ -219,7 +219,7 @@ export default class Model { prepareDestroyPermanently(): this { invariant( !this._preparedState, - `Cannot destroy permanently record with pending changes (${this.__debugName})`, + `Cannot destroy permanently record with pending changes for model ${this.constructor.name}, Raw Data: ${JSON.stringify(this._raw)} (${this.__debugName})`, ) this.__ensureNotDisposable(`Model.prepareDestroyPermanently()`) this._raw._status = 'deleted' @@ -464,7 +464,7 @@ export default class Model { ) invariant( !(this._getChanges(): $FlowFixMe>).isStopped && - this._raw._status !== 'deleted', + this._raw._status !== 'deleted', `Not allowed to change deleted record ${this.__debugName}`, ) } diff --git a/src/Model/test.js b/src/Model/test.js index 33c3d7525..da2af8a2e 100644 --- a/src/Model/test.js +++ b/src/Model/test.js @@ -426,10 +426,10 @@ describe('Safety features', () => { await expectToRejectWithMessage( model.update(() => {}), - 'with pending changes', + `with pending changes for model MockModel, Raw Data: {"id":"${model._raw.id}"`, ) - await expectToRejectWithMessage(model.markAsDeleted(), 'with pending changes') - await expectToRejectWithMessage(model.destroyPermanently(), 'with pending changes') + await expectToRejectWithMessage(model.markAsDeleted(), `with pending changes as deleted for model MockModel, Raw Data: {"id":"${model._raw.id}"`) + await expectToRejectWithMessage(model.destroyPermanently(), `with pending changes for model MockModel, Raw Data: {"id":"${model._raw.id}"`) expect(() => model.observe()).toThrow('uncommitted') await db.batch(model) }) From 42440562caab8eff6f9de620adf04a58958bee2d Mon Sep 17 00:00:00 2001 From: Pranav Babu Date: Tue, 3 Dec 2024 15:23:40 +0100 Subject: [PATCH 2/2] Use verbose flag to show diagnostic errors with model name and record data --- src/Model/index.js | 20 ++++++++++++++++---- src/Model/test.js | 14 +++++++++++--- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/Model/index.js b/src/Model/index.js index 9096edb02..c02bcabfd 100644 --- a/src/Model/index.js +++ b/src/Model/index.js @@ -123,9 +123,13 @@ export default class Model { * @see {Database#batch} */ prepareUpdate(recordUpdater: (this) => void = noop): this { + this.__debugInvariant( + !this._preparedState, + `Model ${this.constructor.name}, Raw Data: ${JSON.stringify(this._raw)}`, + ) invariant( !this._preparedState, - `Cannot update a record with pending changes for model ${this.constructor.name}, Raw Data: ${JSON.stringify(this._raw)} (${this.__debugName})`, + `Cannot update a record with pending changes (${this.__debugName})`, ) this.__ensureNotDisposable(`Model.prepareUpdate()`) this._isEditing = true @@ -182,9 +186,10 @@ export default class Model { * @see {Database#batch} */ prepareMarkAsDeleted(): this { + this.__debugInvariant(!this._preparedState, `Model ${this.constructor.name}, Raw Data: ${JSON.stringify(this._raw)}`) invariant( !this._preparedState, - `Cannot mark a record with pending changes as deleted for model ${this.constructor.name}, Raw Data: ${JSON.stringify(this._raw)} (${this.__debugName})`, + `Cannot mark a record with pending changes as deleted (${this.__debugName})`, ) this.__ensureNotDisposable(`Model.prepareMarkAsDeleted()`) this._raw._status = 'deleted' @@ -217,9 +222,10 @@ export default class Model { * @see {Database#batch} */ prepareDestroyPermanently(): this { + this.__debugInvariant(!this._preparedState, `Model ${this.constructor.name}, Raw Data: ${JSON.stringify(this._raw)}`) invariant( !this._preparedState, - `Cannot destroy permanently record with pending changes for model ${this.constructor.name}, Raw Data: ${JSON.stringify(this._raw)} (${this.__debugName})`, + `Cannot destroy permanently record with pending changes (${this.__debugName})`, ) this.__ensureNotDisposable(`Model.prepareDestroyPermanently()`) this._raw._status = 'deleted' @@ -464,7 +470,7 @@ export default class Model { ) invariant( !(this._getChanges(): $FlowFixMe>).isStopped && - this._raw._status !== 'deleted', + this._raw._status !== 'deleted', `Not allowed to change deleted record ${this.__debugName}`, ) } @@ -485,4 +491,10 @@ export default class Model { logger.debug(`${debugName}: ${this.__debugName}`) } } + + __debugInvariant(condition: boolean, errorMessage: string): void { + if (this.db.experimentalIsVerbose && !condition) { + logger.debug(errorMessage) + } + } } diff --git a/src/Model/test.js b/src/Model/test.js index da2af8a2e..1714a35aa 100644 --- a/src/Model/test.js +++ b/src/Model/test.js @@ -10,6 +10,7 @@ import { field, date, readonly } from '../decorators' import { noop } from '../utils/fp' import sortBy from '../utils/fp/sortBy' import { sanitizedRaw } from '../RawRecord' +import { logger } from '../utils/common' import Model from './index' import { fetchDescendants } from './helpers' @@ -419,6 +420,9 @@ describe('Safety features', () => { }) it('disallows operations on uncommited records', async () => { const db = makeDatabase() + const spy = jest.spyOn(logger, 'debug') + db.experimentalIsVerbose = true + db.adapter.batch = jest.fn() await db.write(async () => { const model = MockModel._prepareCreate(db.get('mock'), () => {}) @@ -426,10 +430,14 @@ describe('Safety features', () => { await expectToRejectWithMessage( model.update(() => {}), - `with pending changes for model MockModel, Raw Data: {"id":"${model._raw.id}"`, + 'with pending changes', ) - await expectToRejectWithMessage(model.markAsDeleted(), `with pending changes as deleted for model MockModel, Raw Data: {"id":"${model._raw.id}"`) - await expectToRejectWithMessage(model.destroyPermanently(), `with pending changes for model MockModel, Raw Data: {"id":"${model._raw.id}"`) + expect(spy).toHaveBeenCalledWith(expect.stringMatching('Model MockModel, Raw Data: {')) + + await expectToRejectWithMessage(model.markAsDeleted(), 'with pending changes') + + await expectToRejectWithMessage(model.destroyPermanently(), 'with pending changes') + expect(() => model.observe()).toThrow('uncommitted') await db.batch(model) })