sharedb 2.1.0 → 2.2.0

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/agent.js CHANGED
@@ -736,17 +736,25 @@ Agent.prototype._src = function() {
736
736
 
737
737
  Agent.prototype._broadcastPresence = function(presence, callback) {
738
738
  var agent = this;
739
+ var backend = this.backend;
739
740
  var requests = this.presenceRequests[presence.ch] || (this.presenceRequests[presence.ch] = {});
740
741
  var previousRequest = requests[presence.id];
741
742
  if (!previousRequest || previousRequest.pv < presence.pv) {
742
743
  this.presenceRequests[presence.ch][presence.id] = presence;
743
744
  }
744
- this.backend.transformPresenceToLatestVersion(this, presence, function(error, presence) {
745
+ var context = {
746
+ presence: presence,
747
+ collection: presence.c
748
+ };
749
+ backend.trigger(backend.MIDDLEWARE_ACTIONS.receivePresence, this, context, function(error) {
745
750
  if (error) return callback(error);
746
- var channel = agent._getPresenceChannel(presence.ch);
747
- agent.backend.pubsub.publish([channel], presence, function(error) {
751
+ backend.transformPresenceToLatestVersion(agent, presence, function(error, presence) {
748
752
  if (error) return callback(error);
749
- callback(null, presence);
753
+ var channel = agent._getPresenceChannel(presence.ch);
754
+ agent.backend.pubsub.publish([channel], presence, function(error) {
755
+ if (error) return callback(error);
756
+ callback(null, presence);
757
+ });
750
758
  });
751
759
  });
752
760
  };
@@ -815,7 +823,9 @@ Agent.prototype._handlePresenceData = function(presence) {
815
823
  var agent = this;
816
824
  backend.trigger(backend.MIDDLEWARE_ACTIONS.sendPresence, this, context, function(error) {
817
825
  if (error) {
818
- return agent.send({a: 'p', ch: presence.ch, id: presence.id, error: getReplyErrorObject(error)});
826
+ if (backend.doNotForwardSendPresenceErrorsToClient) backend.errorHandler(error, {agent: agent});
827
+ else agent.send({a: 'p', ch: presence.ch, id: presence.id, error: getReplyErrorObject(error)});
828
+ return;
819
829
  }
820
830
  agent.send(presence);
821
831
  });
package/lib/backend.js CHANGED
@@ -14,6 +14,7 @@ var StreamSocket = require('./stream-socket');
14
14
  var SubmitRequest = require('./submit-request');
15
15
  var ReadSnapshotsRequest = require('./read-snapshots-request');
16
16
  var util = require('./util');
17
+ var logger = require('./logger');
17
18
 
18
19
  var ERROR_CODE = ShareDBError.CODES;
19
20
 
@@ -34,6 +35,14 @@ function Backend(options) {
34
35
  this.suppressPublish = !!options.suppressPublish;
35
36
  this.maxSubmitRetries = options.maxSubmitRetries || null;
36
37
  this.presenceEnabled = !!options.presence;
38
+ this.doNotForwardSendPresenceErrorsToClient = !!options.doNotForwardSendPresenceErrorsToClient;
39
+ if (!this.doNotForwardSendPresenceErrorsToClient) {
40
+ logger.warn(
41
+ 'Broadcasting "sendPresence" middleware errors to clients is deprecated ' +
42
+ 'and will be removed in a future release. Disable this behaviour with:\n\n' +
43
+ 'new Backend({doNotForwardSendPresenceErrorsToClient: true})\n\n'
44
+ );
45
+ }
37
46
 
38
47
  // Map from event name to a list of middleware
39
48
  this.middleware = {};
@@ -41,6 +50,13 @@ function Backend(options) {
41
50
  // The number of open agents for monitoring and testing memory leaks
42
51
  this.agentsCount = 0;
43
52
  this.remoteAgentsCount = 0;
53
+
54
+ this.errorHandler = typeof options.errorHandler === 'function' ?
55
+ options.errorHandler :
56
+ // eslint-disable-next-line no-unused-vars
57
+ function(error, context) {
58
+ logger.error(error);
59
+ };
44
60
  }
45
61
  module.exports = Backend;
46
62
  emitter.mixin(Backend);
@@ -68,6 +84,8 @@ Backend.prototype.MIDDLEWARE_ACTIONS = {
68
84
  // by design, changing existing reply properties can cause weird bugs, since
69
85
  // the rest of ShareDB would be unaware of those changes.
70
86
  reply: 'reply',
87
+ // The server received presence information
88
+ receivePresence: 'receivePresence',
71
89
  // About to send presence information to a client
72
90
  sendPresence: 'sendPresence',
73
91
  // An operation is about to be submitted to the database
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sharedb",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "JSON OT database backend",
5
5
  "main": "lib/index.js",
6
6
  "dependencies": {
package/test/backend.js CHANGED
@@ -1,29 +1,51 @@
1
1
  var Backend = require('../lib/backend');
2
2
  var expect = require('chai').expect;
3
3
  var sinon = require('sinon');
4
+ var logger = require('../lib/logger');
4
5
 
5
6
  describe('Backend', function() {
6
7
  var backend;
7
- var agent = {
8
- custom: {
9
- foo: 'bar'
10
- }
11
- };
12
- var fetchOptions = {
13
- snapshotOptions: {
14
- fizz: 'buzz'
15
- }
16
- };
17
-
18
- beforeEach(function() {
19
- backend = new Backend();
20
- });
21
8
 
22
9
  afterEach(function(done) {
23
10
  backend.close(done);
24
11
  });
25
12
 
13
+ describe('options', function() {
14
+ describe('errorHandler', function() {
15
+ it('logs by default', function() {
16
+ backend = new Backend();
17
+ var error = new Error('foo');
18
+ sinon.spy(logger, 'error');
19
+ backend.errorHandler(error);
20
+ expect(logger.error.callCount).to.equal(1);
21
+ });
22
+
23
+ it('overrides with another function', function() {
24
+ var handler = sinon.spy();
25
+ backend = new Backend({errorHandler: handler});
26
+ var error = new Error('foo');
27
+ backend.errorHandler(error);
28
+ expect(handler.callCount).to.equal(1);
29
+ });
30
+ });
31
+ });
32
+
26
33
  describe('a simple document', function() {
34
+ var agent = {
35
+ custom: {
36
+ foo: 'bar'
37
+ }
38
+ };
39
+ var fetchOptions = {
40
+ snapshotOptions: {
41
+ fizz: 'buzz'
42
+ }
43
+ };
44
+
45
+ beforeEach(function() {
46
+ backend = new Backend();
47
+ });
48
+
27
49
  beforeEach(function(done) {
28
50
  var doc = backend.connect().get('books', '1984');
29
51
  doc.create({title: '1984'}, function(error) {
@@ -487,4 +487,102 @@ describe('Presence', function() {
487
487
  }
488
488
  ], done);
489
489
  });
490
+
491
+ describe('middleware', function() {
492
+ describe('receivePresence', function() {
493
+ it('provides the presence in the middleware', function(done) {
494
+ backend.use(backend.MIDDLEWARE_ACTIONS.receivePresence, function(context) {
495
+ expect(context.presence.p).to.eql({index: 5});
496
+ done();
497
+ });
498
+
499
+ var localPresence1 = presence1.create('presence-1');
500
+ localPresence1.submit({index: 5}, errorHandler(done));
501
+ });
502
+
503
+ it('can mutate the presence in the middleware', function(done) {
504
+ backend.use(backend.MIDDLEWARE_ACTIONS.receivePresence, function(context, next) {
505
+ context.presence.p.index++;
506
+ next();
507
+ });
508
+
509
+ var localPresence1 = presence1.create('presence-1');
510
+
511
+ async.series([
512
+ presence2.subscribe.bind(presence2),
513
+ function(next) {
514
+ presence2.on('receive', function(id, value) {
515
+ expect(value).to.eql({index: 6});
516
+ next();
517
+ });
518
+ localPresence1.submit({index: 5}, errorHandler(done));
519
+ }
520
+ ], done);
521
+ });
522
+
523
+ it('can cancel a presence broadcast by erroring', function(done) {
524
+ backend.use(backend.MIDDLEWARE_ACTIONS.receivePresence, function(context, next) {
525
+ next(new Error('bad!'));
526
+ });
527
+
528
+ var localPresence1 = presence1.create('presence-1');
529
+
530
+ async.series([
531
+ presence2.subscribe.bind(presence2),
532
+ function(next) {
533
+ presence2.on('receive', function() {
534
+ done(new Error('should not have received presence'));
535
+ });
536
+ localPresence1.submit({index: 5}, function(error) {
537
+ expect(error.message).to.contain('bad!');
538
+ next();
539
+ });
540
+ }
541
+ ], done);
542
+ });
543
+ });
544
+
545
+ describe('sendPresence', function() {
546
+ // TODO: This functionality is deprecated
547
+ it('sends an error to a subscribed client', function(done) {
548
+ var localPresence1 = presence1.create('presence-1');
549
+
550
+ backend.use(backend.MIDDLEWARE_ACTIONS.sendPresence, function(context, next) {
551
+ next(new Error('uh-oh!'));
552
+ });
553
+
554
+ async.series([
555
+ presence2.subscribe.bind(presence2),
556
+ function(next) {
557
+ localPresence1.submit({index: 3}, errorHandler(done));
558
+ presence2.once('error', function(error) {
559
+ expect(error.message).to.equal('uh-oh!');
560
+ next();
561
+ });
562
+ }
563
+ ], done);
564
+ });
565
+
566
+ it('emits errors on the server instead of sending to the client', function(done) {
567
+ var localPresence1 = presence1.create('presence-1');
568
+
569
+ backend.doNotForwardSendPresenceErrorsToClient = true;
570
+ backend.use(backend.MIDDLEWARE_ACTIONS.sendPresence, function(context, next) {
571
+ next(new Error('uh-oh!'));
572
+ });
573
+
574
+ async.series([
575
+ presence2.subscribe.bind(presence2),
576
+ function(next) {
577
+ localPresence1.submit({index: 3}, errorHandler(done));
578
+ presence2.on('error', errorHandler(done));
579
+ backend.errorHandler = function(error) {
580
+ expect(error.message).to.equal('uh-oh!');
581
+ next();
582
+ };
583
+ }
584
+ ], done);
585
+ });
586
+ });
587
+ });
490
588
  });