Skip to content

Commit

Permalink
Merge pull request #520 from hsource/fix-invert
Browse files Browse the repository at this point in the history
Fall back to doing a _hardRollback when `op.type.invert` fails
  • Loading branch information
alecgibson authored Oct 5, 2021
2 parents 46c4aba + 20dbec2 commit dd0b7fd
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 1 deletion.
8 changes: 7 additions & 1 deletion lib/client/doc.js
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,13 @@ Doc.prototype._rollback = function(err) {
var op = this.inflightOp;

if ('op' in op && op.type.invert) {
op.op = op.type.invert(op.op);
try {
op.op = op.type.invert(op.op);
} catch (error) {
// If the op doesn't support `.invert()`, we just reload the doc
// instead of trying to locally revert it.
return this._hardRollback(err);
}

// Transform the undo operation by any pending ops.
for (var i = 0; i < this.pendingOps.length; i++) {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"nyc": "^14.1.1",
"ot-json0-v2": "ottypes/json0",
"ot-json1": "^0.3.0",
"rich-text": "^4.1.0",
"sharedb-legacy": "npm:sharedb@=1.1.0",
"sinon": "^7.5.0"
},
Expand Down
40 changes: 40 additions & 0 deletions test/client/doc.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
var Backend = require('../../lib/backend');
var expect = require('chai').expect;
var async = require('async');
var json0 = require('ot-json0').type;
var richText = require('rich-text').type;
var ShareDBError = require('../../lib/error');
var errorHandler = require('../util').errorHandler;

describe('Doc', function() {
Expand Down Expand Up @@ -407,6 +410,43 @@ describe('Doc', function() {
], done);
});

it('rolls the doc back even if the op is not invertible', function(done) {
var backend = this.backend;

async.series([
function(next) {
// Register the rich text type, which can't be inverted
json0.registerSubtype(richText);

var validOp = {p: ['richName'], oi: {ops: [{insert: 'Scooby\n'}]}};
doc.submitOp(validOp, function(error) {
expect(error).to.not.exist;
next();
});
},
function(next) {
// Make the server reject this insertion
backend.use('submit', function(_context, backendNext) {
backendNext(new ShareDBError(ShareDBError.CODES.ERR_UNKNOWN_ERROR, 'Custom unknown error'));
});
var nonInvertibleOp = {p: ['richName'], t: 'rich-text', o: [{insert: 'e'}]};

// The server error should get all the way back to our handler
doc.submitOp(nonInvertibleOp, function(error) {
expect(error.message).to.eql('Custom unknown error');
next();
});
},
doc.whenNothingPending.bind(doc),
function(next) {
// The doc should have been reverted successfully
expect(doc.data).to.eql({name: 'Scooby', richName: {ops: [{insert: 'Scooby\n'}]}});
next();
}
], done);
});


it('rescues an irreversible op collision', function(done) {
// This test case attempts to reconstruct the following corner case, with
// two independent references to the same document. We submit two simultaneous, but
Expand Down

0 comments on commit dd0b7fd

Please sign in to comment.