Skip to content

Commit dd0b7fd

Browse files
authored
Merge pull request #520 from hsource/fix-invert
Fall back to doing a _hardRollback when `op.type.invert` fails
2 parents 46c4aba + 20dbec2 commit dd0b7fd

File tree

3 files changed

+48
-1
lines changed

3 files changed

+48
-1
lines changed

lib/client/doc.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -948,7 +948,13 @@ Doc.prototype._rollback = function(err) {
948948
var op = this.inflightOp;
949949

950950
if ('op' in op && op.type.invert) {
951-
op.op = op.type.invert(op.op);
951+
try {
952+
op.op = op.type.invert(op.op);
953+
} catch (error) {
954+
// If the op doesn't support `.invert()`, we just reload the doc
955+
// instead of trying to locally revert it.
956+
return this._hardRollback(err);
957+
}
952958

953959
// Transform the undo operation by any pending ops.
954960
for (var i = 0; i < this.pendingOps.length; i++) {

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"nyc": "^14.1.1",
2121
"ot-json0-v2": "ottypes/json0",
2222
"ot-json1": "^0.3.0",
23+
"rich-text": "^4.1.0",
2324
"sharedb-legacy": "npm:sharedb@=1.1.0",
2425
"sinon": "^7.5.0"
2526
},

test/client/doc.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
var Backend = require('../../lib/backend');
22
var expect = require('chai').expect;
33
var async = require('async');
4+
var json0 = require('ot-json0').type;
5+
var richText = require('rich-text').type;
6+
var ShareDBError = require('../../lib/error');
47
var errorHandler = require('../util').errorHandler;
58

69
describe('Doc', function() {
@@ -407,6 +410,43 @@ describe('Doc', function() {
407410
], done);
408411
});
409412

413+
it('rolls the doc back even if the op is not invertible', function(done) {
414+
var backend = this.backend;
415+
416+
async.series([
417+
function(next) {
418+
// Register the rich text type, which can't be inverted
419+
json0.registerSubtype(richText);
420+
421+
var validOp = {p: ['richName'], oi: {ops: [{insert: 'Scooby\n'}]}};
422+
doc.submitOp(validOp, function(error) {
423+
expect(error).to.not.exist;
424+
next();
425+
});
426+
},
427+
function(next) {
428+
// Make the server reject this insertion
429+
backend.use('submit', function(_context, backendNext) {
430+
backendNext(new ShareDBError(ShareDBError.CODES.ERR_UNKNOWN_ERROR, 'Custom unknown error'));
431+
});
432+
var nonInvertibleOp = {p: ['richName'], t: 'rich-text', o: [{insert: 'e'}]};
433+
434+
// The server error should get all the way back to our handler
435+
doc.submitOp(nonInvertibleOp, function(error) {
436+
expect(error.message).to.eql('Custom unknown error');
437+
next();
438+
});
439+
},
440+
doc.whenNothingPending.bind(doc),
441+
function(next) {
442+
// The doc should have been reverted successfully
443+
expect(doc.data).to.eql({name: 'Scooby', richName: {ops: [{insert: 'Scooby\n'}]}});
444+
next();
445+
}
446+
], done);
447+
});
448+
449+
410450
it('rescues an irreversible op collision', function(done) {
411451
// This test case attempts to reconstruct the following corner case, with
412452
// two independent references to the same document. We submit two simultaneous, but

0 commit comments

Comments
 (0)