@webex/plugin-meetings 3.0.0-beta.241 → 3.0.0-beta.243

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.
@@ -25,6 +25,8 @@ import ControlsUtils from './controlsUtils';
25
25
  import EmbeddedAppsUtils from './embeddedAppsUtils';
26
26
  import MediaSharesUtils from './mediaSharesUtils';
27
27
  import LocusDeltaParser from './parser';
28
+ import Metrics from '../metrics';
29
+ import BEHAVIORAL_METRICS from '../metrics/constants';
28
30
 
29
31
  /**
30
32
  * @description LocusInfo extends ChildEmitter to convert locusInfo info a private emitter to parent object
@@ -110,6 +112,37 @@ export default class LocusInfo extends EventsScope {
110
112
  // return value ignored on purpose
111
113
  meeting.meetingRequest
112
114
  .getLocusDTO({url})
115
+ .catch((e) => {
116
+ if (isDelta) {
117
+ LoggerProxy.logger.info(
118
+ 'Locus-info:index#doLocusSync --> delta sync failed, falling back to full sync'
119
+ );
120
+
121
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.LOCUS_DELTA_SYNC_FAILED, {
122
+ correlationId: meeting.correlationId,
123
+ url,
124
+ reason: e.message,
125
+ errorName: e.name,
126
+ stack: e.stack,
127
+ code: e.code,
128
+ });
129
+
130
+ isDelta = false;
131
+
132
+ return meeting.meetingRequest.getLocusDTO({url: meeting.locusUrl}).catch((err) => {
133
+ LoggerProxy.logger.info(
134
+ 'Locus-info:index#doLocusSync --> fallback full sync failed, destroying the meeting'
135
+ );
136
+ this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.LOCUS_DTO_SYNC_FAILED);
137
+ throw err;
138
+ });
139
+ }
140
+ LoggerProxy.logger.info(
141
+ 'Locus-info:index#doLocusSync --> fallback full sync failed, destroying the meeting'
142
+ );
143
+ this.webex.meetings.destroy(meeting, MEETING_REMOVED_REASON.LOCUS_DTO_SYNC_FAILED);
144
+ throw e;
145
+ })
113
146
  .then((res) => {
114
147
  if (isDelta) {
115
148
  if (!isEmpty(res.body)) {
@@ -122,8 +155,6 @@ export default class LocusInfo extends EventsScope {
122
155
  } else {
123
156
  meeting.locusInfo.onFullLocus(res.body);
124
157
  }
125
- })
126
- .finally(() => {
127
158
  // Notify parser to resume processing delta events.
128
159
  // Any deltas in the queue that have now been superseded by this sync will simply be ignored
129
160
  this.locusParser.resume();
@@ -3,6 +3,9 @@ import {difference} from 'lodash';
3
3
  import SortedQueue from '../common/queue';
4
4
  import LoggerProxy from '../common/logs/logger-proxy';
5
5
 
6
+ import Metrics from '../metrics';
7
+ import BEHAVIORAL_METRICS from '../metrics/constants';
8
+
6
9
  const MAX_OOO_DELTA_COUNT = 5; // when we receive an out-of-order delta and the queue builds up to MAX_OOO_DELTA_COUNT, we do a sync with Locus
7
10
  const OOO_DELTA_WAIT_TIME = 10000; // [ms] minimum wait time before we do a sync if we get out-of-order deltas
8
11
  const OOO_DELTA_WAIT_TIME_RANDOM_DELAY = 5000; // [ms] max random delay added to OOO_DELTA_WAIT_TIME
@@ -293,6 +296,10 @@ export default class Parser {
293
296
  // the incoming locus has baseSequence from the future, so it is out-of-order,
294
297
  // we are missing 1 or more locus that should be in front of it, we need to wait for it
295
298
  comparison = WAIT;
299
+
300
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.LOCUS_DELTA_OUT_OF_ORDER, {
301
+ stack: new Error().stack,
302
+ });
296
303
  }
297
304
  break;
298
305
  default:
@@ -416,7 +416,7 @@ export default class MeetingRequest extends StatelessWebexPlugin {
416
416
  `Meeting:request#getLocusDTO --> Error getting latest locus, error ${err}`
417
417
  );
418
418
 
419
- return err;
419
+ throw err;
420
420
  });
421
421
  }
422
422
 
@@ -55,6 +55,8 @@ const BEHAVIORAL_METRICS = {
55
55
  MOVE_FROM_FAILURE: 'js_sdk_move_from_failure',
56
56
  TURN_DISCOVERY_FAILURE: 'js_sdk_turn_discovery_failure',
57
57
  MEETING_INFO_POLICY_ERROR: 'js_sdk_meeting_info_policy_error',
58
+ LOCUS_DELTA_SYNC_FAILED: 'js_sdk_locus_delta_sync_failed',
59
+ LOCUS_DELTA_OUT_OF_ORDER: 'js_sdk_locus_delta_ooo',
58
60
  };
59
61
 
60
62
  export {BEHAVIORAL_METRICS as default};
@@ -10,6 +10,7 @@ import SelfUtils from '@webex/plugin-meetings/src/locus-info/selfUtils';
10
10
  import InfoUtils from '@webex/plugin-meetings/src/locus-info/infoUtils';
11
11
  import EmbeddedAppsUtils from '@webex/plugin-meetings/src/locus-info/embeddedAppsUtils';
12
12
  import LocusDeltaParser from '@webex/plugin-meetings/src/locus-info/parser';
13
+ import Metrics from '@webex/plugin-meetings/src/metrics';
13
14
 
14
15
  import {
15
16
  LOCUSINFO,
@@ -24,7 +25,6 @@ import {
24
25
  } from '../../../../src/constants';
25
26
 
26
27
  import {self, selfWithInactivity} from './selfConstant';
27
- import uuid from 'uuid';
28
28
 
29
29
  describe('plugin-meetings', () => {
30
30
  describe('LocusInfo index', () => {
@@ -39,6 +39,7 @@ describe('plugin-meetings', () => {
39
39
  const locus = {};
40
40
  const meetingId = 'meetingId';
41
41
  let locusInfo;
42
+ let sendBehavioralMetricStub;
42
43
 
43
44
  const webex = new MockWebex({
44
45
  children: {
@@ -65,6 +66,12 @@ describe('plugin-meetings', () => {
65
66
  },
66
67
  },
67
68
  };
69
+
70
+ sendBehavioralMetricStub = sinon.stub(Metrics, 'sendBehavioralMetric');
71
+ });
72
+
73
+ afterEach(() => {
74
+ sinon.restore();
68
75
  });
69
76
 
70
77
  describe('#updateControls', () => {
@@ -1877,6 +1884,114 @@ describe('plugin-meetings', () => {
1877
1884
  });
1878
1885
  });
1879
1886
 
1887
+ describe('edge cases for sync failing', () => {
1888
+ const {DESYNC} = LocusDeltaParser.loci;
1889
+ const fakeFullLocusDto = {id: 'fake full locus dto'};
1890
+ let meeting;
1891
+
1892
+ beforeEach(() => {
1893
+ sinon.stub(locusInfo.locusParser, 'resume');
1894
+ sinon.stub(webex.meetings, 'destroy');
1895
+
1896
+ meeting = {
1897
+ meetingRequest: {
1898
+ getLocusDTO: sandbox.stub(),
1899
+ },
1900
+ locusInfo: {
1901
+ handleLocusDelta: sandbox.stub(),
1902
+ onFullLocus: sandbox.stub(),
1903
+ },
1904
+ locusUrl: 'fullSyncUrl',
1905
+ };
1906
+
1907
+ locusInfo.locusParser.workingCopy = {
1908
+ syncUrl: 'deltaSyncUrl',
1909
+ };
1910
+ });
1911
+
1912
+ it('applyLocusDeltaData gets full locus on DESYNC action if we do not have a syncUrl and destroys the meeting if that fails', () => {
1913
+ meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
1914
+
1915
+ locusInfo.locusParser.workingCopy = {}; // no syncUrl
1916
+
1917
+ // Since we have a promise inside a function we want to test that's not returned,
1918
+ // we will wait and stub it's last function to resolve this waiting promise.
1919
+ return new Promise((resolve) => {
1920
+ webex.meetings.destroy.callsFake(() => resolve());
1921
+ locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1922
+ }).then(() => {
1923
+ assert.calledOnceWithExactly(meeting.meetingRequest.getLocusDTO, {url: 'fullSyncUrl'});
1924
+
1925
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
1926
+ assert.notCalled(meeting.locusInfo.onFullLocus);
1927
+ assert.notCalled(locusInfo.locusParser.resume);
1928
+
1929
+ assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
1930
+ });
1931
+ });
1932
+
1933
+ it('applyLocusDeltaData first tries a delta sync on DESYNC action and if that fails, does a full locus sync', () => {
1934
+ meeting.meetingRequest.getLocusDTO.onCall(0).rejects(new Error('fake error'));
1935
+ meeting.meetingRequest.getLocusDTO.onCall(1).resolves({body: fakeFullLocusDto});
1936
+
1937
+ // Since we have a promise inside a function we want to test that's not returned,
1938
+ // we will wait and stub it's last function to resolve this waiting promise.
1939
+ return new Promise((resolve) => {
1940
+ locusInfo.locusParser.resume.callsFake(() => resolve());
1941
+ locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1942
+ }).then(() => {
1943
+ assert.calledTwice(meeting.meetingRequest.getLocusDTO);
1944
+
1945
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [{url: 'deltaSyncUrl'}]);
1946
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [{url: 'fullSyncUrl'}]);
1947
+
1948
+ assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
1949
+ correlationId: meeting.correlationId,
1950
+ url: 'deltaSyncUrl',
1951
+ reason: 'fake error',
1952
+ errorName: 'Error',
1953
+ stack: sinon.match.any,
1954
+ code: sinon.match.any,
1955
+ });
1956
+
1957
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
1958
+ assert.calledOnceWithExactly(meeting.locusInfo.onFullLocus, fakeFullLocusDto);
1959
+ assert.calledOnce(locusInfo.locusParser.resume);
1960
+ });
1961
+ });
1962
+
1963
+ it('applyLocusDeltaData destroys the meeting if both delta sync and full sync fail', () => {
1964
+ meeting.meetingRequest.getLocusDTO.rejects(new Error('fake error'));
1965
+
1966
+ // Since we have a promise inside a function we want to test that's not returned,
1967
+ // we will wait and stub it's last function to resolve this waiting promise.
1968
+ return new Promise((resolve) => {
1969
+ webex.meetings.destroy.callsFake(() => resolve());
1970
+ locusInfo.applyLocusDeltaData(DESYNC, fakeLocus, meeting);
1971
+ }).then(() => {
1972
+ assert.calledTwice(meeting.meetingRequest.getLocusDTO);
1973
+
1974
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[0].args, [{url: 'deltaSyncUrl'}]);
1975
+ assert.deepEqual(meeting.meetingRequest.getLocusDTO.getCalls()[1].args, [{url: 'fullSyncUrl'}]);
1976
+
1977
+ assert.calledWith(sendBehavioralMetricStub, 'js_sdk_locus_delta_sync_failed', {
1978
+ correlationId: meeting.correlationId,
1979
+ url: 'deltaSyncUrl',
1980
+ reason: 'fake error',
1981
+ errorName: 'Error',
1982
+ stack: sinon.match.any,
1983
+ code: sinon.match.any,
1984
+ });
1985
+
1986
+ assert.notCalled(meeting.locusInfo.handleLocusDelta);
1987
+ assert.notCalled(meeting.locusInfo.onFullLocus);
1988
+ assert.notCalled(locusInfo.locusParser.resume);
1989
+
1990
+ assert.calledOnceWithExactly(webex.meetings.destroy, meeting, 'LOCUS_DTO_SYNC_FAILED');
1991
+ });
1992
+ });
1993
+ });
1994
+
1880
1995
  it('onDeltaLocus handle delta data', () => {
1881
1996
  fakeLocus.participants = {};
1882
1997
  const fakeBreakout = {
@@ -2420,6 +2535,8 @@ describe('plugin-meetings', () => {
2420
2535
  // send an out-of-order delta
2421
2536
  locusInfo.handleLocusDelta(oooDelta, mockMeeting);
2422
2537
 
2538
+ assert.calledOnceWithExactly(sendBehavioralMetricStub, 'js_sdk_locus_delta_ooo', { stack: sinon.match.any})
2539
+
2423
2540
  await clock.tickAsync(12499);
2424
2541
  await testUtils.flushPromises();
2425
2542
  assert.notCalled(syncRequestStub);