sharedb 5.0.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/submit-request.js +24 -1
- package/package.json +1 -1
- package/test/client/submit.js +113 -32
package/lib/submit-request.js
CHANGED
|
@@ -109,7 +109,7 @@ SubmitRequest.prototype.submit = function(callback) {
|
|
|
109
109
|
// case, we should return a non-fatal 'Op already submitted' error. We
|
|
110
110
|
// must get the past ops and check their src and seq values to
|
|
111
111
|
// differentiate.
|
|
112
|
-
|
|
112
|
+
request._fetchCreateOpVersion(function(error, version) {
|
|
113
113
|
if (err) return callback(err);
|
|
114
114
|
if (version == null) {
|
|
115
115
|
callback(request.alreadyCreatedError());
|
|
@@ -194,6 +194,15 @@ SubmitRequest.prototype.commit = function(callback) {
|
|
|
194
194
|
backend.trigger(backend.MIDDLEWARE_ACTIONS.commit, this.agent, this, function(err) {
|
|
195
195
|
if (err) return callback(err);
|
|
196
196
|
if (request._fixupOps.length) request.op.m.fixup = request._fixupOps;
|
|
197
|
+
if (request.op.create) {
|
|
198
|
+
// When we create the snapshot, we store a pointer to the op that created
|
|
199
|
+
// it. This allows us to return OP_ALREADY_SUBMITTED errors when appropriate.
|
|
200
|
+
request.snapshot.m._create = {
|
|
201
|
+
src: request.op.src,
|
|
202
|
+
seq: request.op.seq,
|
|
203
|
+
v: request.op.v
|
|
204
|
+
};
|
|
205
|
+
}
|
|
197
206
|
|
|
198
207
|
// Try committing the operation and snapshot to the database atomically
|
|
199
208
|
backend.db.commit(
|
|
@@ -290,6 +299,20 @@ SubmitRequest.prototype._shouldSaveMilestoneSnapshot = function(snapshot) {
|
|
|
290
299
|
return this.saveMilestoneSnapshot;
|
|
291
300
|
};
|
|
292
301
|
|
|
302
|
+
SubmitRequest.prototype._fetchCreateOpVersion = function(callback) {
|
|
303
|
+
var create = this.snapshot.m._create;
|
|
304
|
+
if (create) {
|
|
305
|
+
var version = (create.src === this.op.src && create.seq === this.op.seq) ? create.v : null;
|
|
306
|
+
return callback(null, version);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// We can only reach here if the snapshot is missing the create metadata.
|
|
310
|
+
// This can happen if a client tries to re-create or resubmit a create op to
|
|
311
|
+
// a "legacy" snapshot that existed before we started adding the meta (should
|
|
312
|
+
// be uncommon) or when using a driver that doesn't support metadata (eg Postgres).
|
|
313
|
+
this.backend.db.getCommittedOpVersion(this.collection, this.id, this.snapshot, this.op, null, callback);
|
|
314
|
+
};
|
|
315
|
+
|
|
293
316
|
// Non-fatal client errors:
|
|
294
317
|
SubmitRequest.prototype.alreadySubmittedError = function() {
|
|
295
318
|
return new ShareDBError(ERROR_CODE.ERR_OP_ALREADY_SUBMITTED, 'Op already submitted');
|
package/package.json
CHANGED
package/test/client/submit.js
CHANGED
|
@@ -6,6 +6,7 @@ var deserializedType = require('./deserialized-type');
|
|
|
6
6
|
var numberType = require('./number-type');
|
|
7
7
|
var errorHandler = require('../util').errorHandler;
|
|
8
8
|
var richText = require('rich-text');
|
|
9
|
+
var MemoryDB = require('../../lib/db/memory');
|
|
9
10
|
types.register(deserializedType.type);
|
|
10
11
|
types.register(deserializedType.type2);
|
|
11
12
|
types.register(numberType.type);
|
|
@@ -206,44 +207,124 @@ module.exports = function() {
|
|
|
206
207
|
});
|
|
207
208
|
});
|
|
208
209
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
doc.fetch(function(err) {
|
|
214
|
-
if (err) return done(err);
|
|
215
|
-
expect(doc.data).eql({age: 3});
|
|
216
|
-
expect(doc.version).eql(1);
|
|
217
|
-
done();
|
|
218
|
-
});
|
|
210
|
+
|
|
211
|
+
describe('create', function() {
|
|
212
|
+
describe('metadata enabled', function() {
|
|
213
|
+
runCreateTests();
|
|
219
214
|
});
|
|
220
|
-
});
|
|
221
215
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
216
|
+
describe('no snapshot metadata available', function() {
|
|
217
|
+
beforeEach(function() {
|
|
218
|
+
var getSnapshot = MemoryDB.prototype.getSnapshot;
|
|
219
|
+
sinon.stub(MemoryDB.prototype, 'getSnapshot')
|
|
220
|
+
.callsFake(function() {
|
|
221
|
+
var args = Array.from(arguments);
|
|
222
|
+
var callback = args.pop();
|
|
223
|
+
args.push(function(error, snapshot) {
|
|
224
|
+
if (snapshot) delete snapshot.m;
|
|
225
|
+
callback(error, snapshot);
|
|
226
|
+
});
|
|
227
|
+
getSnapshot.apply(this, args);
|
|
228
|
+
});
|
|
231
229
|
});
|
|
232
|
-
});
|
|
233
|
-
});
|
|
234
230
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
var doc2 = this.backend.connect().get('dogs', 'fido');
|
|
238
|
-
doc.create({age: 3}, function(err) {
|
|
239
|
-
if (err) return done(err);
|
|
240
|
-
doc2.create({age: 4}, function(err) {
|
|
241
|
-
expect(err).instanceOf(Error);
|
|
242
|
-
expect(doc2.version).equal(1);
|
|
243
|
-
expect(doc2.data).eql({age: 3});
|
|
244
|
-
done();
|
|
231
|
+
afterEach(function() {
|
|
232
|
+
sinon.restore();
|
|
245
233
|
});
|
|
234
|
+
|
|
235
|
+
runCreateTests();
|
|
246
236
|
});
|
|
237
|
+
|
|
238
|
+
function runCreateTests() {
|
|
239
|
+
it('can create a new doc then fetch', function(done) {
|
|
240
|
+
var doc = this.backend.connect().get('dogs', 'fido');
|
|
241
|
+
doc.create({age: 3}, function(err) {
|
|
242
|
+
if (err) return done(err);
|
|
243
|
+
doc.fetch(function(err) {
|
|
244
|
+
if (err) return done(err);
|
|
245
|
+
expect(doc.data).eql({age: 3});
|
|
246
|
+
expect(doc.version).eql(1);
|
|
247
|
+
done();
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('calling create on the same doc twice fails', function(done) {
|
|
253
|
+
var doc = this.backend.connect().get('dogs', 'fido');
|
|
254
|
+
doc.create({age: 3}, function(err) {
|
|
255
|
+
if (err) return done(err);
|
|
256
|
+
doc.create({age: 4}, function(err) {
|
|
257
|
+
expect(err).instanceOf(Error);
|
|
258
|
+
expect(doc.version).equal(1);
|
|
259
|
+
expect(doc.data).eql({age: 3});
|
|
260
|
+
done();
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('trying to create an already created doc without fetching fails and fetches', function(done) {
|
|
266
|
+
var doc = this.backend.connect().get('dogs', 'fido');
|
|
267
|
+
var doc2 = this.backend.connect().get('dogs', 'fido');
|
|
268
|
+
doc.create({age: 3}, function(err) {
|
|
269
|
+
if (err) return done(err);
|
|
270
|
+
doc2.create({age: 4}, function(err) {
|
|
271
|
+
expect(err).instanceOf(Error);
|
|
272
|
+
expect(doc2.version).equal(1);
|
|
273
|
+
expect(doc2.data).eql({age: 3});
|
|
274
|
+
done();
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('does not fail when resubmitting a create op', function(done) {
|
|
280
|
+
var backend = this.backend;
|
|
281
|
+
var connection = backend.connect();
|
|
282
|
+
var submitted = false;
|
|
283
|
+
backend.use('submit', function(request, next) {
|
|
284
|
+
if (!submitted) {
|
|
285
|
+
submitted = true;
|
|
286
|
+
connection.close();
|
|
287
|
+
backend.connect(connection);
|
|
288
|
+
}
|
|
289
|
+
next();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
var doc = connection.get('dogs', 'fido');
|
|
293
|
+
doc.create({age: 3}, function(error) {
|
|
294
|
+
expect(doc.version).to.equal(1);
|
|
295
|
+
done(error);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('does not fail when resubmitting a create op on a doc that was deleted', function(done) {
|
|
300
|
+
var backend = this.backend;
|
|
301
|
+
var connection1 = backend.connect();
|
|
302
|
+
var connection2 = backend.connect();
|
|
303
|
+
var doc1 = connection1.get('dogs', 'fido');
|
|
304
|
+
var doc2 = connection2.get('dogs', 'fido');
|
|
305
|
+
|
|
306
|
+
async.series([
|
|
307
|
+
doc1.create.bind(doc1, {age: 3}),
|
|
308
|
+
doc1.del.bind(doc1),
|
|
309
|
+
function(next) {
|
|
310
|
+
var submitted = false;
|
|
311
|
+
backend.use('submit', function(request, next) {
|
|
312
|
+
if (!submitted) {
|
|
313
|
+
submitted = true;
|
|
314
|
+
connection2.close();
|
|
315
|
+
backend.connect(connection2);
|
|
316
|
+
}
|
|
317
|
+
next();
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
doc2.create({name: 'Fido'}, function(error) {
|
|
321
|
+
expect(doc2.version).to.equal(3);
|
|
322
|
+
next(error);
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
], done);
|
|
326
|
+
});
|
|
327
|
+
}
|
|
247
328
|
});
|
|
248
329
|
|
|
249
330
|
it('server fetches and transforms by already committed op', function(done) {
|