@webex/internal-plugin-conversation 3.0.0-beta.8 → 3.0.0-bnr.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/README.md +1 -3
- package/dist/activities.js +8 -69
- package/dist/activities.js.map +1 -1
- package/dist/activity-thread-ordering.js +19 -79
- package/dist/activity-thread-ordering.js.map +1 -1
- package/dist/config.js +1 -7
- package/dist/config.js.map +1 -1
- package/dist/constants.js +4 -5
- package/dist/constants.js.map +1 -1
- package/dist/conversation.js +790 -1199
- package/dist/conversation.js.map +1 -1
- package/dist/convo-error.js +0 -23
- package/dist/convo-error.js.map +1 -1
- package/dist/decryption-transforms.js +35 -98
- package/dist/decryption-transforms.js.map +1 -1
- package/dist/encryption-transforms.js +11 -48
- package/dist/encryption-transforms.js.map +1 -1
- package/dist/index.js +7 -50
- package/dist/index.js.map +1 -1
- package/dist/internal-plugin-conversation.d.ts +21 -0
- package/dist/share-activity.js +40 -106
- package/dist/share-activity.js.map +1 -1
- package/dist/to-array.js +9 -11
- package/dist/to-array.js.map +1 -1
- package/dist/tsdoc-metadata.json +11 -0
- package/dist/types/activities.d.ts +32 -0
- package/dist/types/activity-thread-ordering.d.ts +18 -0
- package/dist/types/config.d.ts +19 -0
- package/dist/types/constants.d.ts +5 -0
- package/dist/types/conversation.d.ts +2 -0
- package/dist/types/convo-error.d.ts +10 -0
- package/dist/types/decryption-transforms.d.ts +1 -0
- package/dist/types/encryption-transforms.d.ts +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/share-activity.d.ts +7 -0
- package/dist/types/to-array.d.ts +9 -0
- package/package.json +15 -15
- package/src/activities.js +10 -7
- package/src/activity-thread-ordering.js +27 -30
- package/src/activity-threading.md +68 -49
- package/src/config.js +5 -5
- package/src/conversation.js +621 -589
- package/src/decryption-transforms.js +103 -62
- package/src/encryption-transforms.js +103 -83
- package/src/index.js +82 -66
- package/src/share-activity.js +64 -55
- package/src/to-array.js +2 -2
- package/test/integration/spec/create.js +184 -118
- package/test/integration/spec/encryption.js +250 -186
- package/test/integration/spec/get.js +761 -513
- package/test/integration/spec/mercury.js +37 -27
- package/test/integration/spec/share.js +292 -229
- package/test/integration/spec/verbs.js +628 -441
- package/test/unit/spec/conversation.js +265 -163
- package/test/unit/spec/decrypt-transforms.js +112 -131
- package/test/unit/spec/encryption-transforms.js +24 -18
- package/test/unit/spec/share-activity.js +37 -40
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
declare namespace _default {
|
|
2
|
+
namespace conversation {
|
|
3
|
+
const allowedInboundTags: {
|
|
4
|
+
'webex-mention': string[];
|
|
5
|
+
};
|
|
6
|
+
const allowedOutboundTags: {
|
|
7
|
+
'webex-mention': string[];
|
|
8
|
+
};
|
|
9
|
+
function inboundProcessFunc(): void;
|
|
10
|
+
function outboundProcessFunc(): void;
|
|
11
|
+
const allowedInboundStyles: any[];
|
|
12
|
+
const allowedOutboundStyles: any[];
|
|
13
|
+
const thumbnailMaxHeight: number;
|
|
14
|
+
const thumbnailMaxWidth: number;
|
|
15
|
+
const keepEncryptedProperties: boolean;
|
|
16
|
+
const decryptionFailureMessage: string;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export default _default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const transforms: any[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const transforms: any[];
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper to convert objects into arrays of transforms. probably belongs in
|
|
3
|
+
* webex-core
|
|
4
|
+
* @param {string} direction "inbound"|"outbound"
|
|
5
|
+
* @param {Object} obj
|
|
6
|
+
* @private
|
|
7
|
+
* @returns {Array}
|
|
8
|
+
*/
|
|
9
|
+
export default function toArray(direction: string, obj: any): any[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webex/internal-plugin-conversation",
|
|
3
|
-
"version": "3.0.0-
|
|
3
|
+
"version": "3.0.0-bnr.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,26 +20,26 @@
|
|
|
20
20
|
]
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@webex/common": "3.0.0-
|
|
24
|
-
"@webex/helper-html": "3.0.0-
|
|
25
|
-
"@webex/helper-image": "3.0.0-
|
|
26
|
-
"@webex/internal-plugin-conversation": "3.0.0-
|
|
27
|
-
"@webex/internal-plugin-encryption": "3.0.0-
|
|
28
|
-
"@webex/internal-plugin-user": "3.0.0-
|
|
29
|
-
"@webex/webex-core": "3.0.0-
|
|
23
|
+
"@webex/common": "3.0.0-bnr.0",
|
|
24
|
+
"@webex/helper-html": "3.0.0-bnr.0",
|
|
25
|
+
"@webex/helper-image": "3.0.0-bnr.0",
|
|
26
|
+
"@webex/internal-plugin-conversation": "3.0.0-bnr.0",
|
|
27
|
+
"@webex/internal-plugin-encryption": "3.0.0-bnr.0",
|
|
28
|
+
"@webex/internal-plugin-user": "3.0.0-bnr.0",
|
|
29
|
+
"@webex/webex-core": "3.0.0-bnr.0",
|
|
30
30
|
"crypto-js": "^4.1.1",
|
|
31
31
|
"lodash": "^4.17.21",
|
|
32
32
|
"node-scr": "^0.3.0",
|
|
33
33
|
"uuid": "^3.3.2"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@webex/test-helper-chai": "3.0.0-
|
|
37
|
-
"@webex/test-helper-file": "3.0.0-
|
|
38
|
-
"@webex/test-helper-make-local-url": "3.0.0-
|
|
39
|
-
"@webex/test-helper-mocha": "3.0.0-
|
|
40
|
-
"@webex/test-helper-mock-webex": "3.0.0-
|
|
41
|
-
"@webex/test-helper-retry": "3.0.0-
|
|
42
|
-
"@webex/test-helper-test-users": "3.0.0-
|
|
36
|
+
"@webex/test-helper-chai": "3.0.0-bnr.0",
|
|
37
|
+
"@webex/test-helper-file": "3.0.0-bnr.0",
|
|
38
|
+
"@webex/test-helper-make-local-url": "3.0.0-bnr.0",
|
|
39
|
+
"@webex/test-helper-mocha": "3.0.0-bnr.0",
|
|
40
|
+
"@webex/test-helper-mock-webex": "3.0.0-bnr.0",
|
|
41
|
+
"@webex/test-helper-retry": "3.0.0-bnr.0",
|
|
42
|
+
"@webex/test-helper-test-users": "3.0.0-bnr.0",
|
|
43
43
|
"sinon": "^9.2.4"
|
|
44
44
|
}
|
|
45
45
|
}
|
package/src/activities.js
CHANGED
|
@@ -14,7 +14,7 @@ export const ACTIVITY_TYPES = {
|
|
|
14
14
|
CREATE: 'CREATE',
|
|
15
15
|
TOMBSTONE: 'TOMBSTONE',
|
|
16
16
|
DELETE: 'DELETE',
|
|
17
|
-
REPLY_EDIT: 'REPLY_EDIT'
|
|
17
|
+
REPLY_EDIT: 'REPLY_EDIT',
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
const REPLY = 'reply';
|
|
@@ -59,9 +59,11 @@ export const getPublishedDate = (activity = {}) => new Date(activity.published).
|
|
|
59
59
|
* @param {Object} activity2
|
|
60
60
|
* @returns {boolean} true if first activity is newer than second
|
|
61
61
|
*/
|
|
62
|
-
export const isNewer = (activity1, activity2) =>
|
|
62
|
+
export const isNewer = (activity1, activity2) =>
|
|
63
|
+
getPublishedDate(activity1) > getPublishedDate(activity2);
|
|
63
64
|
|
|
64
|
-
export const sortActivitiesByPublishedDate = (activities) =>
|
|
65
|
+
export const sortActivitiesByPublishedDate = (activities) =>
|
|
66
|
+
sortBy(activities, (activity) => getPublishedDate(activity));
|
|
65
67
|
|
|
66
68
|
export const getParentId = (activity) => activity?.parent?.id;
|
|
67
69
|
|
|
@@ -83,7 +85,8 @@ export const sanitizeActivity = (activity) => {
|
|
|
83
85
|
return final;
|
|
84
86
|
};
|
|
85
87
|
|
|
86
|
-
export const getIsActivityOrphaned = (activity, activities) =>
|
|
88
|
+
export const getIsActivityOrphaned = (activity, activities) =>
|
|
89
|
+
activity.parent && activity.parent.id && !activities[activity.parent.id];
|
|
87
90
|
|
|
88
91
|
const getIsReplyEditActivity = (activity, activities) => {
|
|
89
92
|
const parentId = activity.parent.id;
|
|
@@ -111,7 +114,7 @@ export const createRootActivity = (activity) => activity;
|
|
|
111
114
|
export const createReplyActivity = (activity) => {
|
|
112
115
|
const replyAct = {
|
|
113
116
|
...activity,
|
|
114
|
-
replyParent: activity.parent
|
|
117
|
+
replyParent: activity.parent,
|
|
115
118
|
};
|
|
116
119
|
|
|
117
120
|
return replyAct;
|
|
@@ -128,7 +131,7 @@ export const createEditActivity = (editActivity, activities) => {
|
|
|
128
131
|
parent: editActParentObj,
|
|
129
132
|
editParent: editActParentObj,
|
|
130
133
|
object: editActivity.object,
|
|
131
|
-
published: editActivity.published
|
|
134
|
+
published: editActivity.published,
|
|
132
135
|
};
|
|
133
136
|
|
|
134
137
|
return joinedEditAct;
|
|
@@ -147,7 +150,7 @@ export const createReplyEditActivity = (editActivity, activities) => {
|
|
|
147
150
|
editParent: editActParentObj,
|
|
148
151
|
replyParent: parentReplyAct.parent,
|
|
149
152
|
object: editActivity.object,
|
|
150
|
-
published: editActivity.published
|
|
153
|
+
published: editActivity.published,
|
|
151
154
|
};
|
|
152
155
|
|
|
153
156
|
return joinedReplyEditActivity;
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
NEWER,
|
|
10
10
|
OLDER,
|
|
11
11
|
INITIAL,
|
|
12
|
-
MID
|
|
12
|
+
MID,
|
|
13
13
|
} from './activities';
|
|
14
14
|
|
|
15
15
|
export const defaultMinDisplayableActivities = 20;
|
|
@@ -32,7 +32,8 @@ export const setValue = (destination, key, value) => destination.set(key, value)
|
|
|
32
32
|
*/
|
|
33
33
|
export const getValue = (source, key) => source.get(key);
|
|
34
34
|
|
|
35
|
-
export const getActivityObjectsFromMap = (hashMap) =>
|
|
35
|
+
export const getActivityObjectsFromMap = (hashMap) =>
|
|
36
|
+
Array.from(hashMap).map(([, activity]) => activity);
|
|
36
37
|
/**
|
|
37
38
|
* creates maps for various activity types and defines handlers for working with stored activities
|
|
38
39
|
* utilizes revealing module pattern to close over state and only expose certain necessary functions for altering state
|
|
@@ -53,8 +54,7 @@ export const activityManager = () => {
|
|
|
53
54
|
|
|
54
55
|
if (existingReplyHash) {
|
|
55
56
|
setValue(existingReplyHash, replyAct.id, replyAct);
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
57
|
+
} else {
|
|
58
58
|
const replyHash = new Map();
|
|
59
59
|
|
|
60
60
|
setValue(replyHash, replyAct.id, replyAct);
|
|
@@ -98,23 +98,25 @@ export const activityManager = () => {
|
|
|
98
98
|
reactionHelper(reactionSelfAct, reactionSelfActivityHash);
|
|
99
99
|
};
|
|
100
100
|
|
|
101
|
-
const getActivityHandlerByKey = (key) =>
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
101
|
+
const getActivityHandlerByKey = (key) =>
|
|
102
|
+
({
|
|
103
|
+
[ACTIVITY_TYPES.REACTION]: handleNewReaction,
|
|
104
|
+
[ACTIVITY_TYPES.REACTION_SELF]: handleNewReactionSelf,
|
|
105
|
+
[ACTIVITY_TYPES.EDIT]: handleNewEdit,
|
|
106
|
+
[ACTIVITY_TYPES.REPLY]: handleNewReply,
|
|
107
|
+
}[key]);
|
|
108
|
+
|
|
109
|
+
const getActivityByTypeAndParentId = (type, id) =>
|
|
110
|
+
({
|
|
111
|
+
[ACTIVITY_TYPES.EDIT]: getValue(editActivityHash, id),
|
|
112
|
+
[ACTIVITY_TYPES.REPLY]: getValue(replyActivityHash, id),
|
|
113
|
+
[ACTIVITY_TYPES.REACTION]: getValue(reactionActivityHash, id),
|
|
114
|
+
[ACTIVITY_TYPES.REACTION_SELF]: getValue(reactionSelfActivityHash, id),
|
|
115
|
+
}[type]);
|
|
114
116
|
|
|
115
117
|
return {
|
|
116
118
|
getActivityHandlerByKey,
|
|
117
|
-
getActivityByTypeAndParentId
|
|
119
|
+
getActivityByTypeAndParentId,
|
|
118
120
|
};
|
|
119
121
|
};
|
|
120
122
|
|
|
@@ -133,8 +135,7 @@ export const bookendManager = () => {
|
|
|
133
135
|
const setOldestAct = (act) => {
|
|
134
136
|
if (!oldestAct) {
|
|
135
137
|
oldestAct = act;
|
|
136
|
-
}
|
|
137
|
-
else if (isNewer(oldestAct, act)) {
|
|
138
|
+
} else if (isNewer(oldestAct, act)) {
|
|
138
139
|
oldestAct = act;
|
|
139
140
|
}
|
|
140
141
|
};
|
|
@@ -142,8 +143,7 @@ export const bookendManager = () => {
|
|
|
142
143
|
const setNewestAct = (act) => {
|
|
143
144
|
if (!newestAct) {
|
|
144
145
|
newestAct = act;
|
|
145
|
-
}
|
|
146
|
-
else if (isNewer(act, newestAct)) {
|
|
146
|
+
} else if (isNewer(act, newestAct)) {
|
|
147
147
|
newestAct = act;
|
|
148
148
|
}
|
|
149
149
|
};
|
|
@@ -161,7 +161,7 @@ export const bookendManager = () => {
|
|
|
161
161
|
return {
|
|
162
162
|
setBookends,
|
|
163
163
|
getNewestAct,
|
|
164
|
-
getOldestAct
|
|
164
|
+
getOldestAct,
|
|
165
165
|
};
|
|
166
166
|
};
|
|
167
167
|
|
|
@@ -204,7 +204,7 @@ export const noMoreActivitiesManager = () => {
|
|
|
204
204
|
getNoMoreActs,
|
|
205
205
|
checkAndSetNoMoreActs,
|
|
206
206
|
checkAndSetNoNewerActs,
|
|
207
|
-
checkAndSetNoOlderActs
|
|
207
|
+
checkAndSetNoOlderActs,
|
|
208
208
|
};
|
|
209
209
|
};
|
|
210
210
|
|
|
@@ -223,7 +223,7 @@ export const rootActivityManager = () => {
|
|
|
223
223
|
|
|
224
224
|
return {
|
|
225
225
|
addNewRoot,
|
|
226
|
-
getRootActivityHash
|
|
226
|
+
getRootActivityHash,
|
|
227
227
|
};
|
|
228
228
|
};
|
|
229
229
|
|
|
@@ -249,9 +249,7 @@ export const getLoopCounterFailsafe = () => {
|
|
|
249
249
|
* @returns {object}
|
|
250
250
|
*/
|
|
251
251
|
export const getQuery = (type, queryOptions) => {
|
|
252
|
-
const {
|
|
253
|
-
newestPublishedDate, oldestPublishedDate, batchSize, activityToSearch = {}
|
|
254
|
-
} = queryOptions;
|
|
252
|
+
const {newestPublishedDate, oldestPublishedDate, batchSize, activityToSearch = {}} = queryOptions;
|
|
255
253
|
|
|
256
254
|
switch (type) {
|
|
257
255
|
case NEWER: {
|
|
@@ -266,8 +264,7 @@ export const getQuery = (type, queryOptions) => {
|
|
|
266
264
|
|
|
267
265
|
if (searchType === ACTIVITY_TYPES.REPLY || searchType === ACTIVITY_TYPES.EDIT) {
|
|
268
266
|
midDate = activityToSearch.parent.published;
|
|
269
|
-
}
|
|
270
|
-
else {
|
|
267
|
+
} else {
|
|
271
268
|
midDate = activityToSearch.published;
|
|
272
269
|
}
|
|
273
270
|
|
|
@@ -3,22 +3,24 @@
|
|
|
3
3
|
Activity thread ordering (or "threading") is the act of flattening the hierarchical relationship of thread parents and thread replies.
|
|
4
4
|
|
|
5
5
|
## Why Thread Order?
|
|
6
|
+
|
|
6
7
|
When a client fetches activities from conversation service, convo returns chronologically ordered activities (by published date). If you are a client that attempts to display activities to a user, this is mostly useless. An example:
|
|
7
8
|
|
|
8
9
|
Client: hey convo, give me 10 activities
|
|
9
10
|
|
|
10
11
|
Convo: here.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
|
|
13
|
+
1. reaction
|
|
14
|
+
2. root
|
|
15
|
+
3. reaction
|
|
16
|
+
4. reply
|
|
17
|
+
5. edit
|
|
18
|
+
6. edit
|
|
19
|
+
7. reply
|
|
20
|
+
8. reaction
|
|
21
|
+
9. meeting
|
|
22
|
+
10. reply
|
|
23
|
+
|
|
22
24
|
Client: ...
|
|
23
25
|
|
|
24
26
|
Convo: ...
|
|
@@ -28,17 +30,18 @@ By contrast, thread ordering returns activities in _thread order_, otherwise kno
|
|
|
28
30
|
Client: hey convo, give me 10 activities in thread order
|
|
29
31
|
|
|
30
32
|
Convo: here.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
|
|
34
|
+
1. root 4
|
|
35
|
+
2. reply to root 4
|
|
36
|
+
3. reply to root 4
|
|
37
|
+
4. root 3
|
|
38
|
+
5. reply to root 3
|
|
39
|
+
6. reply to root 3
|
|
40
|
+
7. root 2
|
|
41
|
+
8. reply to root 2
|
|
42
|
+
9. reply to root 2
|
|
43
|
+
10. root 1 (newest root activity)
|
|
44
|
+
|
|
42
45
|
Client: 👍
|
|
43
46
|
|
|
44
47
|
Convo: 👍
|
|
@@ -57,6 +60,7 @@ As you can see, thread ordering is useful, and as you may _not_ see, it is diffi
|
|
|
57
60
|
# The Code
|
|
58
61
|
|
|
59
62
|
Thread ordering methods live in the internal converation plugin
|
|
63
|
+
|
|
60
64
|
```
|
|
61
65
|
webex.internal.conversation[*]
|
|
62
66
|
```
|
|
@@ -64,36 +68,39 @@ webex.internal.conversation[*]
|
|
|
64
68
|
There are two methods that compose thread ordering, a public and private method. The public method is simply a facade of the private method, abstracting the complex away from the caller.
|
|
65
69
|
|
|
66
70
|
### `_listActivitiesThreadOrdered(options)` (private)
|
|
71
|
+
|
|
67
72
|
The `_listActivitiesThreadOrdered` method is a stateful async generator function that fetches thread ordered activities.
|
|
68
73
|
|
|
69
74
|
#### Description
|
|
70
|
-
|
|
75
|
+
|
|
76
|
+
The `_listActivitiesThreadOrdered` method instantiates a generator function that yields a promise. It yields results in an infinite loop for the life of the generator (until the instance is no longer referenced and JS garbage collects). This means when the method yields a result it pauses execution until its next invocation, freezing its current state to be used by the next invocation. The generator instance internally tracks the oldest and newest activities it has fetched, using them as the timestamps to query the next set of results in either direction, thus the caller is not required to maintain any state to fetch older and newer activities. This method does not cache all fetched activities, therefore the caller is still required to store their fetched activities or be forced to refetch them.
|
|
71
77
|
|
|
72
78
|
The generator's returned `next` method accepts arguments as well, which allows the caller to change the direction of their querying from newer messages to older ones without creating a new generator instance. `next`
|
|
73
79
|
|
|
74
80
|
#### Parameters
|
|
81
|
+
|
|
75
82
|
_generator initialization options_
|
|
76
83
|
|
|
77
84
|
options
|
|
78
85
|
|
|
79
|
-
|
|
86
|
+
options.url: the conversation's convo URL
|
|
80
87
|
|
|
81
|
-
|
|
88
|
+
options.minActivities: the minimum number of activities you would like in your batch
|
|
82
89
|
|
|
83
|
-
|
|
90
|
+
options.queryType: the direction to fetch activities. One of 'newer', 'older', 'mid'
|
|
84
91
|
|
|
85
|
-
|
|
92
|
+
options.search: a server activity object to return as the middle point of the batch, along with its surrounding activities
|
|
86
93
|
|
|
87
94
|
_parameters accepted by the generator's `next` method_
|
|
88
95
|
|
|
89
96
|
options
|
|
90
97
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
options.search: see above
|
|
98
|
+
options.queryType: see above
|
|
94
99
|
|
|
100
|
+
options.search: see above
|
|
95
101
|
|
|
96
102
|
#### Return value
|
|
103
|
+
|
|
97
104
|
Calling `_listActivitiesThreadOrdered` returns the generator instance, and does not execute any work. The generator returned has a `next` method that begins the first round of work and returns a `done` boolean and a `value`, the asked-for thread ordered activities.
|
|
98
105
|
|
|
99
106
|
```
|
|
@@ -109,6 +116,7 @@ value: IActivity[]
|
|
|
109
116
|
```
|
|
110
117
|
|
|
111
118
|
#### Examples
|
|
119
|
+
|
|
112
120
|
```
|
|
113
121
|
const options = {
|
|
114
122
|
url: 'myconvourl.com',
|
|
@@ -123,31 +131,35 @@ await threadOrderedFetcher.next() // => { done: boolean, value: IActivity[] }
|
|
|
123
131
|
#### The nitty gritty: design
|
|
124
132
|
|
|
125
133
|
There are 3 main phases of the method, with one major subphase:
|
|
134
|
+
|
|
126
135
|
1. initialization: up to the ` while (true)` loop
|
|
127
136
|
2. execution: the ` while (true)` loop
|
|
128
137
|
3. (subphase of execution) fetching: the ` while (!getNoMoreActs())` loop
|
|
129
138
|
4. re-calculation: after the ` yield`
|
|
130
139
|
|
|
131
140
|
##### initialization
|
|
141
|
+
|
|
132
142
|
Parsing the arguments and initializing state is done in this first phase, before the generator's first execution. State that must be retained independent of each execution loop (the code run by the generator's `next` method) is instantiated here. The following variables (or grouped of variables) are created and maintained for the life of the generator:
|
|
133
143
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
- oldestAct - the oldest activity (by published date) that has been fetched by the gen. instance
|
|
145
|
+
- newestAct - the newest activity that has been fetched by the gen. instance
|
|
146
|
+
- batchSize - the number of activities to fetch to achieve minActivities. This value is set lower in cases where we fetch children, because adding the children to their parents may help us reach minActivities count
|
|
147
|
+
- query - initialize fetching query. The first query will be forced to fetch newest activities in a space to initialize the activity states, unless a search is requested
|
|
148
|
+
- \*\*\*ActivityHash(es): cache objects for children activities. These caches store all child activity types returned from convo fetches in order to keep track of "orphans", or child activities that come in before their parent activity
|
|
139
149
|
|
|
140
150
|
##### execution
|
|
151
|
+
|
|
141
152
|
The execution phase will be run for every call to `next` method. Its job is to fetch activities until the minimum activity limit is reached, then it will build the ordered list of activities and yield them to the caller. Similar to init phase, execution phase has variables declared outside the fetching loop in order to keep track of what we have fetched so far, and whether continuing to fetch is necessary. The execuction phase runs the fetching subphase. The following state variables are tracked by this outer loop, before the fetching phase:
|
|
142
153
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
154
|
+
- rootActivityHash - the data structure used to track root activities we have fetched in a given batch
|
|
155
|
+
- [* helpers] - many helper functions are declared. They close over state variables and are used to group functionality used by the fetching loop, and for self-documenting purposes
|
|
156
|
+
- fetchLoopCount - track count of fetch loops run. Used to break out of loop in unlikely case that we fetch the same query over and over.
|
|
146
157
|
|
|
147
158
|
##### fetching
|
|
148
|
-
The fetching phase is contained within the execution phase, and is a loop that will run until we run out of activities to fetch, or explicitly break the loop when we reach the minActivities count with what we've fetched so far. Each fetch loop begins by setting up the query and passing options to `#listActivities` method. All query types depend on a first query to activities to get N root activities. midDate queries (aliased MID) and sinceDate queries (aliased NEWER) also must recursively fetch children to ensure we don't return to caller a root activity that is missing children.*
|
|
149
159
|
|
|
150
|
-
|
|
160
|
+
The fetching phase is contained within the execution phase, and is a loop that will run until we run out of activities to fetch, or explicitly break the loop when we reach the minActivities count with what we've fetched so far. Each fetch loop begins by setting up the query and passing options to `#listActivities` method. All query types depend on a first query to activities to get N root activities. midDate queries (aliased MID) and sinceDate queries (aliased NEWER) also must recursively fetch children to ensure we don't return to caller a root activity that is missing children.\*
|
|
161
|
+
|
|
162
|
+
\*NOTE: These sometimes-necessary children queries depend on the addition of a new method, `#listAllChildActivitiesByParentId`. This method is similar to `listChildActivitiesByParentId`, but this method recursively fetches all the chilren of a certain type, rather than forcing the calling method to fetch 10 children at a time (as is the current child limit set by underlying convo API).
|
|
151
163
|
|
|
152
164
|
Activities returned to us by convo fetches are sorted and placed into maps based off their activity type. Root activities are stored on a per-batch basis, whereas children are stored globally (prevents orphans). Root activities are keyed by their ID, where children are keyed by their parent's ID (`activity.parent.id`).
|
|
153
165
|
|
|
@@ -156,24 +168,29 @@ When reactions are fetched, however, we call a _different_ convo API that helps
|
|
|
156
168
|
Fetching loop can be explicitly broken out of when we have decided we have the necessary activities for the current batch. If we _don't_ have enough activities, we initialize a new query derived from the former query and return to the top of the fetching loop to continue building our list.
|
|
157
169
|
|
|
158
170
|
##### execution (revisited)
|
|
159
|
-
|
|
171
|
+
|
|
172
|
+
After a successful series of fetches have given us the activities necessary for the current batch, we initialize an empty array that acts as our ordered list. To begin, the current batch's root activities are sorted by their published date, and using the ordered list of IDs, we begin to loop over the rootActivityHash.
|
|
160
173
|
|
|
161
174
|
For every root activity, the ID of the root is used to check all the child hashes (keyed by _parent ID_, recall), and implement IActivity. When a root activity has replies, a similar process is undergone for the reply activity: sorting it by published date, checking its ID against the edits and reactions hash, and sanitizing to implement IActivity. Replies are then looped over and added to the ordered list before moving on to the next root.
|
|
162
175
|
|
|
163
176
|
We have successfully finished a batch, and will now yield the ordered list to the caller.
|
|
164
177
|
|
|
165
178
|
##### recalculation
|
|
179
|
+
|
|
166
180
|
The generator's `next` method may accepts arguments in order to alter the behavior of the existing generator. Assigning `yield` to a variable captures the arguments passed into `next` method. The `next` method accepts a new value for minActivities, as well as an override for `queryType`. This allows the same generator (and its state) to be used for multiple queries that build one upon another.
|
|
167
181
|
|
|
168
182
|
The first call to `next` (the first call) is initialized automatically as an INITIAL fetch, to populate the generator state, but subsequent calls to `next` method without a passed-in `queryType` will close the generator.
|
|
169
183
|
|
|
170
184
|
### `listActivitiesThreadOrdered(options)` (public)
|
|
185
|
+
|
|
171
186
|
The public facade exposes wrappers for ease of managing an instance of the generator. All methods implement the iterator protocol The following methods are exposed:
|
|
172
187
|
|
|
173
188
|
#### `getOlder()`
|
|
189
|
+
|
|
174
190
|
Implements iterator protocol. Returns oldest activities, then returns any older activities than the oldest fetched.
|
|
175
191
|
|
|
176
192
|
example:
|
|
193
|
+
|
|
177
194
|
```
|
|
178
195
|
const myGen = webex.internal.conversation.listActivitiesThreadOrdered(opts);
|
|
179
196
|
const firstBatch = await myGen.getOlder();
|
|
@@ -195,9 +212,11 @@ console.log(secondBatch)
|
|
|
195
212
|
```
|
|
196
213
|
|
|
197
214
|
#### jumpToActivity(searchActivity)
|
|
198
|
-
Implements iterator protocol. Returns searched activity as the middle activity in a batch, with older and newer activities surrounding.
|
|
199
215
|
|
|
200
|
-
|
|
216
|
+
Implements iterator protocol. Returns searched activity as the middle activity in a batch, with older and newer activities surrounding.
|
|
217
|
+
|
|
218
|
+
example:
|
|
219
|
+
|
|
201
220
|
```
|
|
202
221
|
const activitySearchResult = IServerActivity // actual activity object returned by server, most commonly returned by activity search
|
|
203
222
|
const myGen = webex.internal.conversation.listActivitiesThreadOrdered(opts);
|
|
@@ -210,10 +229,13 @@ console.log(results)
|
|
|
210
229
|
}
|
|
211
230
|
*/
|
|
212
231
|
```
|
|
213
|
-
|
|
232
|
+
|
|
233
|
+
#### `getNewer()`
|
|
234
|
+
|
|
214
235
|
Implements iterator protocol. Returns most recent activities, then returns any newer activities than the newest fetched.
|
|
215
236
|
|
|
216
237
|
example:
|
|
238
|
+
|
|
217
239
|
```
|
|
218
240
|
const myGen = webex.internal.conversation.listActivitiesThreadOrdered(opts);
|
|
219
241
|
const firstBatch = await myGen.getNewer();
|
|
@@ -234,7 +256,9 @@ console.log(secondBatch)
|
|
|
234
256
|
}
|
|
235
257
|
*/
|
|
236
258
|
```
|
|
259
|
+
|
|
237
260
|
`getNewer()` is of limited use at first, but consider the following example, when used in conjunction with `jumpToActivity`:
|
|
261
|
+
|
|
238
262
|
```
|
|
239
263
|
const activitySearchResult = IServerActivity // actual activity object returned by server, most commonly returned by activity search
|
|
240
264
|
const myGen = webex.internal.conversation.listActivitiesThreadOrdered(opts);
|
|
@@ -256,8 +280,3 @@ console.log(secondBatch);
|
|
|
256
280
|
}
|
|
257
281
|
*/
|
|
258
282
|
```
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
package/src/config.js
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
export default {
|
|
6
6
|
conversation: {
|
|
7
7
|
allowedInboundTags: {
|
|
8
|
-
'webex-mention': ['data-object-type', 'data-object-id', 'data-object-url']
|
|
8
|
+
'webex-mention': ['data-object-type', 'data-object-id', 'data-object-url'],
|
|
9
9
|
},
|
|
10
10
|
allowedOutboundTags: {
|
|
11
|
-
'webex-mention': ['data-object-type', 'data-object-id', 'data-object-url']
|
|
11
|
+
'webex-mention': ['data-object-type', 'data-object-id', 'data-object-url'],
|
|
12
12
|
},
|
|
13
13
|
// eslint-disable-next-line no-empty-function
|
|
14
14
|
inboundProcessFunc: () => {},
|
|
@@ -27,11 +27,11 @@ export default {
|
|
|
27
27
|
*/
|
|
28
28
|
thumbnailMaxWidth: 640,
|
|
29
29
|
/**
|
|
30
|
-
|
|
30
|
+
* Primarily for testing. When true, decrypting an activity will create a
|
|
31
31
|
* sister property with the original encrypted string
|
|
32
32
|
* @type {Boolean}
|
|
33
33
|
*/
|
|
34
34
|
keepEncryptedProperties: false,
|
|
35
|
-
decryptionFailureMessage: 'This message cannot be decrypted'
|
|
36
|
-
}
|
|
35
|
+
decryptionFailureMessage: 'This message cannot be decrypted',
|
|
36
|
+
},
|
|
37
37
|
};
|