@webex/internal-plugin-presence 2.59.2 → 2.59.3-next.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.
@@ -1,268 +1,268 @@
1
- import {debounce} from 'lodash';
2
-
3
- import {
4
- FETCH_DELAY,
5
- GROUNDSKEEPER_INTERVAL,
6
- SUBSCRIPTION_DELAY,
7
- UPDATE_PRESENCE_DELAY,
8
- EXPIRED_PRESENCE_TIME,
9
- PREMATURE_EXPIRATION_SUBSCRIPTION_TIME,
10
- DEFAULT_SUBSCRIPTION_TTL,
11
- APHELEIA_SUBSCRIPTION_UPDATE,
12
- PRESENCE_UPDATE,
13
- ENVELOPE_TYPE,
14
- } from './constants';
15
-
16
- /**
17
- * Presence Worker
18
- * Manages fetches and subscriptions for presence
19
- * @class
20
- */
21
- export default class PresenceWorker {
22
- /**
23
- * Constructs a presence worker to execute and
24
- * maintain tasks related to presence upkeep.
25
- * @returns {undefined}
26
- */
27
- constructor() {
28
- this.presences = {}; // current presence objects; updated time
29
- this.watchers = {}; // counter of visible presence-required objects
30
- this.fetchers = {}; // waiting to get presence object
31
- this.flights = {}; // in flight to get presence object
32
- this.campers = {}; // waiting to subscribe; updated time
33
- this.subscribers = {}; // current subscriptions; expiration time
34
- }
35
-
36
- /**
37
- * Connect to the mercury for presence and starts worker.
38
- * @param {object} webex
39
- * @returns {undefined}
40
- */
41
- initialize(webex) {
42
- if (!webex || !webex.internal) {
43
- throw new Error('Must initialize Presence Worker with webex!');
44
- }
45
-
46
- this.webex = webex;
47
-
48
- const mercury = this.webex.internal.mercury.connected
49
- ? Promise.resolve()
50
- : this.webex.internal.mercury.connect();
51
-
52
- mercury.then(() => {
53
- this.webex.internal.mercury.on(
54
- APHELEIA_SUBSCRIPTION_UPDATE,
55
- this.subscriptionUpdate.bind(this)
56
- );
57
- });
58
-
59
- setInterval(this.groundskeeper.bind(this), GROUNDSKEEPER_INTERVAL);
60
- }
61
-
62
- /**
63
- * Trigger a subscription update event.
64
- * @param {string} event
65
- * @returns {undefined}
66
- */
67
- subscriptionUpdate(event) {
68
- this.presences[event.data.subject] = new Date().getTime();
69
-
70
- this.webex.internal.presence.emitEvent(PRESENCE_UPDATE, {
71
- type: ENVELOPE_TYPE.SUBSCRIPTION,
72
- payload: event.data,
73
- });
74
- }
75
-
76
- /**
77
- * Retrieves and subscribes to a user's presence.
78
- * @param {string} id
79
- * @returns {undefined}
80
- */
81
- enqueue(id) {
82
- const now = new Date().getTime();
83
-
84
- if (this.watchers[id]) {
85
- this.watchers[id] += 1;
86
- } else {
87
- this.watchers[id] = 1;
88
- }
89
-
90
- if (this.subscribers[id]) {
91
- return;
92
- }
93
-
94
- if (!this.campers[id]) {
95
- this.campers[id] = now;
96
- }
97
-
98
- // Retrieve presence if:
99
- // not in flight or
100
- // don't already have the presence or
101
- // presence has gone stale
102
- if (
103
- !this.flights[id] &&
104
- (!this.presences[id] || this.presences[id] < now - UPDATE_PRESENCE_DELAY)
105
- ) {
106
- this.fetchers[id] = id;
107
- this.debouncedFetch();
108
- }
109
- }
110
-
111
- /**
112
- * Retract from subscribing to a user's presence.
113
- * @param {string} id
114
- * @returns {undefined}
115
- */
116
- dequeue(id) {
117
- if (this.watchers[id]) {
118
- this.watchers[id] -= 1;
119
-
120
- if (this.watchers[id] <= 0) {
121
- delete this.watchers[id];
122
- delete this.fetchers[id];
123
- delete this.campers[id];
124
- }
125
- }
126
- }
127
-
128
- /**
129
- * Retrieve users' presences.
130
- * @returns {undefined}
131
- */
132
- checkFetchers() {
133
- const boarding = this.fetchers;
134
-
135
- Object.assign(this.flights, boarding);
136
- this.fetchers = {};
137
-
138
- this.webex.internal.presence.list(Object.keys(boarding)).then((response) => {
139
- const now = new Date().getTime();
140
-
141
- response.statusList.forEach((presence) => {
142
- const id = presence.subject;
143
-
144
- delete this.flights[id];
145
- this.presences[id] = now;
146
- });
147
-
148
- this.webex.internal.presence.emitEvent(PRESENCE_UPDATE, {
149
- type: ENVELOPE_TYPE.PRESENCE,
150
- payload: response,
151
- });
152
- });
153
- }
154
-
155
- debouncedFetch = debounce(this.checkFetchers, FETCH_DELAY);
156
-
157
- /**
158
- * Determine if we should subscribe to users' presences.
159
- * @returns {Array}: User ids to subscribe.
160
- */
161
- checkCampers() {
162
- const now = new Date().getTime();
163
- const subscribers = [];
164
-
165
- Object.entries(this.campers).forEach((camper) => {
166
- const id = camper[0];
167
- const time = camper[1];
168
-
169
- // Subscribe if they've been camping for a minute
170
- if (time < now - SUBSCRIPTION_DELAY) {
171
- delete this.campers[id];
172
- this.subscribers[id] = null;
173
- subscribers.push(id);
174
- }
175
- });
176
-
177
- return subscribers;
178
- }
179
-
180
- /**
181
- * Determine if we should re-subscribe or remove users' subscriptions.
182
- * @returns {Array}: User ids to re-subscribe.
183
- */
184
- checkSubscriptions() {
185
- const now = new Date().getTime();
186
-
187
- const renewIds = [];
188
-
189
- Object.entries(this.subscribers).forEach((subscription) => {
190
- const id = subscription[0];
191
- const expiration = subscription[1];
192
-
193
- if (expiration) {
194
- // Renew subscription if they're about to expire
195
- if (this.watchers[id] && now > expiration - PREMATURE_EXPIRATION_SUBSCRIPTION_TIME) {
196
- renewIds.push(id);
197
- } else if (now > expiration) {
198
- delete this.subscribers[id];
199
- }
200
- }
201
- });
202
-
203
- return renewIds;
204
- }
205
-
206
- /**
207
- * Remove expired presence objects.
208
- * @returns {undefined}
209
- */
210
- cleanPresences() {
211
- const trash = [];
212
- const tenMinutesAgo = new Date().getTime() - EXPIRED_PRESENCE_TIME;
213
-
214
- Object.entries(this.presences).forEach((presence) => {
215
- const id = presence[0];
216
- const lastUpdated = presence[1];
217
-
218
- // Delete the object if it is stale
219
- if (lastUpdated < tenMinutesAgo) {
220
- delete this.presences[id];
221
- trash.push(id);
222
- }
223
- });
224
-
225
- // Tells client to delete it too
226
- if (trash.length) {
227
- this.webex.internal.presence.emitEvent(PRESENCE_UPDATE, {
228
- type: ENVELOPE_TYPE.DELETE,
229
- payload: trash,
230
- });
231
- }
232
- }
233
-
234
- /**
235
- * Execute chores on an interval.
236
- * Checks if we should make new subscribe,
237
- * checks if we should re-subscribe,
238
- * removes expired subscriptions,
239
- * removes expired presence objects.
240
- * @returns {undefined}
241
- */
242
- groundskeeper() {
243
- const campers = this.checkCampers();
244
- const renewSubscriptions = this.checkSubscriptions();
245
-
246
- const ids = [...campers, ...renewSubscriptions];
247
-
248
- if (ids.length) {
249
- this.webex.internal.presence.subscribe(ids).then((body) => {
250
- const now = new Date().getTime();
251
-
252
- body.responses.forEach((response) => {
253
- if (response.responseCode === 200) {
254
- const ttl = response.subscriptionTtl * 1000;
255
-
256
- this.subscribers[response.subject] = now + ttl;
257
- this.presences[response.status.subject] = now;
258
- } else {
259
- // If it errored for any reason, set the ttl so we clean it out eventually
260
- this.subscribers[response.subject] = now + DEFAULT_SUBSCRIPTION_TTL;
261
- }
262
- });
263
- });
264
- }
265
-
266
- this.cleanPresences();
267
- }
268
- }
1
+ import {debounce} from 'lodash';
2
+
3
+ import {
4
+ FETCH_DELAY,
5
+ GROUNDSKEEPER_INTERVAL,
6
+ SUBSCRIPTION_DELAY,
7
+ UPDATE_PRESENCE_DELAY,
8
+ EXPIRED_PRESENCE_TIME,
9
+ PREMATURE_EXPIRATION_SUBSCRIPTION_TIME,
10
+ DEFAULT_SUBSCRIPTION_TTL,
11
+ APHELEIA_SUBSCRIPTION_UPDATE,
12
+ PRESENCE_UPDATE,
13
+ ENVELOPE_TYPE,
14
+ } from './constants';
15
+
16
+ /**
17
+ * Presence Worker
18
+ * Manages fetches and subscriptions for presence
19
+ * @class
20
+ */
21
+ export default class PresenceWorker {
22
+ /**
23
+ * Constructs a presence worker to execute and
24
+ * maintain tasks related to presence upkeep.
25
+ * @returns {undefined}
26
+ */
27
+ constructor() {
28
+ this.presences = {}; // current presence objects; updated time
29
+ this.watchers = {}; // counter of visible presence-required objects
30
+ this.fetchers = {}; // waiting to get presence object
31
+ this.flights = {}; // in flight to get presence object
32
+ this.campers = {}; // waiting to subscribe; updated time
33
+ this.subscribers = {}; // current subscriptions; expiration time
34
+ }
35
+
36
+ /**
37
+ * Connect to the mercury for presence and starts worker.
38
+ * @param {object} webex
39
+ * @returns {undefined}
40
+ */
41
+ initialize(webex) {
42
+ if (!webex || !webex.internal) {
43
+ throw new Error('Must initialize Presence Worker with webex!');
44
+ }
45
+
46
+ this.webex = webex;
47
+
48
+ const mercury = this.webex.internal.mercury.connected
49
+ ? Promise.resolve()
50
+ : this.webex.internal.mercury.connect();
51
+
52
+ mercury.then(() => {
53
+ this.webex.internal.mercury.on(
54
+ APHELEIA_SUBSCRIPTION_UPDATE,
55
+ this.subscriptionUpdate.bind(this)
56
+ );
57
+ });
58
+
59
+ setInterval(this.groundskeeper.bind(this), GROUNDSKEEPER_INTERVAL);
60
+ }
61
+
62
+ /**
63
+ * Trigger a subscription update event.
64
+ * @param {string} event
65
+ * @returns {undefined}
66
+ */
67
+ subscriptionUpdate(event) {
68
+ this.presences[event.data.subject] = new Date().getTime();
69
+
70
+ this.webex.internal.presence.emitEvent(PRESENCE_UPDATE, {
71
+ type: ENVELOPE_TYPE.SUBSCRIPTION,
72
+ payload: event.data,
73
+ });
74
+ }
75
+
76
+ /**
77
+ * Retrieves and subscribes to a user's presence.
78
+ * @param {string} id
79
+ * @returns {undefined}
80
+ */
81
+ enqueue(id) {
82
+ const now = new Date().getTime();
83
+
84
+ if (this.watchers[id]) {
85
+ this.watchers[id] += 1;
86
+ } else {
87
+ this.watchers[id] = 1;
88
+ }
89
+
90
+ if (this.subscribers[id]) {
91
+ return;
92
+ }
93
+
94
+ if (!this.campers[id]) {
95
+ this.campers[id] = now;
96
+ }
97
+
98
+ // Retrieve presence if:
99
+ // not in flight or
100
+ // don't already have the presence or
101
+ // presence has gone stale
102
+ if (
103
+ !this.flights[id] &&
104
+ (!this.presences[id] || this.presences[id] < now - UPDATE_PRESENCE_DELAY)
105
+ ) {
106
+ this.fetchers[id] = id;
107
+ this.debouncedFetch();
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Retract from subscribing to a user's presence.
113
+ * @param {string} id
114
+ * @returns {undefined}
115
+ */
116
+ dequeue(id) {
117
+ if (this.watchers[id]) {
118
+ this.watchers[id] -= 1;
119
+
120
+ if (this.watchers[id] <= 0) {
121
+ delete this.watchers[id];
122
+ delete this.fetchers[id];
123
+ delete this.campers[id];
124
+ }
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Retrieve users' presences.
130
+ * @returns {undefined}
131
+ */
132
+ checkFetchers() {
133
+ const boarding = this.fetchers;
134
+
135
+ Object.assign(this.flights, boarding);
136
+ this.fetchers = {};
137
+
138
+ this.webex.internal.presence.list(Object.keys(boarding)).then((response) => {
139
+ const now = new Date().getTime();
140
+
141
+ response.statusList.forEach((presence) => {
142
+ const id = presence.subject;
143
+
144
+ delete this.flights[id];
145
+ this.presences[id] = now;
146
+ });
147
+
148
+ this.webex.internal.presence.emitEvent(PRESENCE_UPDATE, {
149
+ type: ENVELOPE_TYPE.PRESENCE,
150
+ payload: response,
151
+ });
152
+ });
153
+ }
154
+
155
+ debouncedFetch = debounce(this.checkFetchers, FETCH_DELAY);
156
+
157
+ /**
158
+ * Determine if we should subscribe to users' presences.
159
+ * @returns {Array}: User ids to subscribe.
160
+ */
161
+ checkCampers() {
162
+ const now = new Date().getTime();
163
+ const subscribers = [];
164
+
165
+ Object.entries(this.campers).forEach((camper) => {
166
+ const id = camper[0];
167
+ const time = camper[1];
168
+
169
+ // Subscribe if they've been camping for a minute
170
+ if (time < now - SUBSCRIPTION_DELAY) {
171
+ delete this.campers[id];
172
+ this.subscribers[id] = null;
173
+ subscribers.push(id);
174
+ }
175
+ });
176
+
177
+ return subscribers;
178
+ }
179
+
180
+ /**
181
+ * Determine if we should re-subscribe or remove users' subscriptions.
182
+ * @returns {Array}: User ids to re-subscribe.
183
+ */
184
+ checkSubscriptions() {
185
+ const now = new Date().getTime();
186
+
187
+ const renewIds = [];
188
+
189
+ Object.entries(this.subscribers).forEach((subscription) => {
190
+ const id = subscription[0];
191
+ const expiration = subscription[1];
192
+
193
+ if (expiration) {
194
+ // Renew subscription if they're about to expire
195
+ if (this.watchers[id] && now > expiration - PREMATURE_EXPIRATION_SUBSCRIPTION_TIME) {
196
+ renewIds.push(id);
197
+ } else if (now > expiration) {
198
+ delete this.subscribers[id];
199
+ }
200
+ }
201
+ });
202
+
203
+ return renewIds;
204
+ }
205
+
206
+ /**
207
+ * Remove expired presence objects.
208
+ * @returns {undefined}
209
+ */
210
+ cleanPresences() {
211
+ const trash = [];
212
+ const tenMinutesAgo = new Date().getTime() - EXPIRED_PRESENCE_TIME;
213
+
214
+ Object.entries(this.presences).forEach((presence) => {
215
+ const id = presence[0];
216
+ const lastUpdated = presence[1];
217
+
218
+ // Delete the object if it is stale
219
+ if (lastUpdated < tenMinutesAgo) {
220
+ delete this.presences[id];
221
+ trash.push(id);
222
+ }
223
+ });
224
+
225
+ // Tells client to delete it too
226
+ if (trash.length) {
227
+ this.webex.internal.presence.emitEvent(PRESENCE_UPDATE, {
228
+ type: ENVELOPE_TYPE.DELETE,
229
+ payload: trash,
230
+ });
231
+ }
232
+ }
233
+
234
+ /**
235
+ * Execute chores on an interval.
236
+ * Checks if we should make new subscribe,
237
+ * checks if we should re-subscribe,
238
+ * removes expired subscriptions,
239
+ * removes expired presence objects.
240
+ * @returns {undefined}
241
+ */
242
+ groundskeeper() {
243
+ const campers = this.checkCampers();
244
+ const renewSubscriptions = this.checkSubscriptions();
245
+
246
+ const ids = [...campers, ...renewSubscriptions];
247
+
248
+ if (ids.length) {
249
+ this.webex.internal.presence.subscribe(ids).then((body) => {
250
+ const now = new Date().getTime();
251
+
252
+ body.responses.forEach((response) => {
253
+ if (response.responseCode === 200) {
254
+ const ttl = response.subscriptionTtl * 1000;
255
+
256
+ this.subscribers[response.subject] = now + ttl;
257
+ this.presences[response.status.subject] = now;
258
+ } else {
259
+ // If it errored for any reason, set the ttl so we clean it out eventually
260
+ this.subscribers[response.subject] = now + DEFAULT_SUBSCRIPTION_TTL;
261
+ }
262
+ });
263
+ });
264
+ }
265
+
266
+ this.cleanPresences();
267
+ }
268
+ }