@atlaskit/collab-provider 8.4.0 → 8.5.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/CHANGELOG.md +34 -0
- package/dist/cjs/analytics/{index.js → analytics-helper.js} +43 -4
- package/dist/cjs/analytics/performance.js +6 -5
- package/dist/cjs/channel.js +222 -224
- package/dist/cjs/{provider → document}/catchup.js +2 -2
- package/dist/cjs/document/document-service.js +617 -0
- package/dist/cjs/document/step-queue-state.js +51 -0
- package/dist/cjs/errors/error-code-mapper.js +86 -67
- package/dist/cjs/errors/error-types.js +251 -21
- package/dist/cjs/helpers/utils.js +1 -12
- package/dist/cjs/participants/participants-helper.js +51 -0
- package/dist/cjs/participants/participants-service.js +217 -0
- package/dist/cjs/participants/participants-state.js +53 -0
- package/dist/cjs/{provider/telepointers.js → participants/telepointers-helper.js} +6 -6
- package/dist/cjs/provider/commit-step.js +4 -4
- package/dist/cjs/provider/index.js +212 -774
- package/dist/cjs/types.js +3 -0
- package/dist/cjs/version-wrapper.js +1 -1
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/analytics/{index.js → analytics-helper.js} +15 -4
- package/dist/es2019/analytics/performance.js +5 -6
- package/dist/es2019/channel.js +123 -116
- package/dist/es2019/{provider → document}/catchup.js +2 -2
- package/dist/es2019/document/document-service.js +495 -0
- package/dist/es2019/document/step-queue-state.js +30 -0
- package/dist/es2019/errors/error-code-mapper.js +87 -63
- package/dist/es2019/errors/error-types.js +143 -5
- package/dist/es2019/helpers/utils.js +0 -10
- package/dist/es2019/participants/participants-helper.js +25 -0
- package/dist/es2019/participants/participants-service.js +166 -0
- package/dist/es2019/participants/participants-state.js +28 -0
- package/dist/es2019/{provider/telepointers.js → participants/telepointers-helper.js} +2 -2
- package/dist/es2019/provider/commit-step.js +4 -4
- package/dist/es2019/provider/index.js +162 -637
- package/dist/es2019/types.js +4 -0
- package/dist/es2019/version-wrapper.js +1 -1
- package/dist/es2019/version.json +1 -1
- package/dist/esm/analytics/{index.js → analytics-helper.js} +43 -4
- package/dist/esm/analytics/performance.js +5 -6
- package/dist/esm/channel.js +224 -226
- package/dist/esm/{provider → document}/catchup.js +2 -2
- package/dist/esm/document/document-service.js +609 -0
- package/dist/esm/document/step-queue-state.js +43 -0
- package/dist/esm/errors/error-code-mapper.js +87 -64
- package/dist/esm/errors/error-types.js +243 -18
- package/dist/esm/helpers/utils.js +0 -10
- package/dist/esm/participants/participants-helper.js +43 -0
- package/dist/esm/participants/participants-service.js +209 -0
- package/dist/esm/participants/participants-state.js +45 -0
- package/dist/esm/{provider/telepointers.js → participants/telepointers-helper.js} +4 -4
- package/dist/esm/provider/commit-step.js +4 -4
- package/dist/esm/provider/index.js +212 -774
- package/dist/esm/types.js +4 -0
- package/dist/esm/version-wrapper.js +1 -1
- package/dist/esm/version.json +1 -1
- package/dist/types/analytics/{index.d.ts → analytics-helper.d.ts} +3 -1
- package/dist/types/analytics/performance.d.ts +3 -1
- package/dist/types/analytics/ufo.d.ts +1 -1
- package/dist/types/channel.d.ts +12 -5
- package/dist/types/document/document-service.d.ts +105 -0
- package/dist/types/document/step-queue-state.d.ts +16 -0
- package/dist/types/errors/error-code-mapper.d.ts +2 -36
- package/dist/types/errors/error-types.d.ts +439 -4
- package/dist/types/helpers/const.d.ts +2 -2
- package/dist/types/helpers/utils.d.ts +0 -6
- package/dist/types/index.d.ts +2 -1
- package/dist/types/participants/participants-helper.d.ts +15 -0
- package/dist/types/participants/participants-service.d.ts +70 -0
- package/dist/types/participants/participants-state.d.ts +13 -0
- package/dist/types/participants/telepointers-helper.d.ts +4 -0
- package/dist/types/provider/commit-step.d.ts +6 -6
- package/dist/types/provider/index.d.ts +80 -73
- package/dist/types/types.d.ts +47 -30
- package/package.json +4 -4
- package/report.api.md +172 -19
- package/dist/types/provider/telepointers.d.ts +0 -5
- /package/dist/types/{provider → document}/catchup.d.ts +0 -0
|
@@ -1,78 +1,102 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
code: 'DOCUMENT_NOT_FOUND',
|
|
8
|
-
message: 'The requested document is not found'
|
|
9
|
-
},
|
|
10
|
-
hasToLogin: {
|
|
11
|
-
code: 'HAS_TO_LOGIN',
|
|
12
|
-
message: 'The user needs to login'
|
|
13
|
-
},
|
|
14
|
-
catchupFail: {
|
|
15
|
-
code: 'CATCHUP_FAILED',
|
|
16
|
-
message: 'Cannot fetch catchup from collab service'
|
|
17
|
-
},
|
|
18
|
-
serviceUnvailable: {
|
|
19
|
-
code: 'SERVICE_UNAVAILABLE',
|
|
20
|
-
message: 'Service is not available'
|
|
21
|
-
},
|
|
22
|
-
failToSave: {
|
|
23
|
-
code: 'FAIL_TO_SAVE',
|
|
24
|
-
message: 'Collab service is not able to save changes'
|
|
25
|
-
},
|
|
26
|
-
restoreError: {
|
|
27
|
-
code: 'DOCUMENT_RESTORE_ERROR',
|
|
28
|
-
message: 'Collab service unable to restore document'
|
|
29
|
-
},
|
|
30
|
-
internalError: {
|
|
31
|
-
code: 'INTERNAL_SERVICE_ERROR',
|
|
32
|
-
message: 'Collab service has experienced an internal server error'
|
|
33
|
-
}
|
|
34
|
-
};
|
|
1
|
+
import { NCS_ERROR_CODE } from './error-types';
|
|
2
|
+
import { INTERNAL_ERROR_CODE, PROVIDER_ERROR_CODE } from './error-types';
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Maps internal collab provider errors to an emitted error format
|
|
6
|
+
*/
|
|
35
7
|
export const errorCodeMapper = error => {
|
|
36
|
-
var _error$data;
|
|
8
|
+
var _error$data, _error$data2, _error$data3;
|
|
37
9
|
switch ((_error$data = error.data) === null || _error$data === void 0 ? void 0 : _error$data.code) {
|
|
38
|
-
case
|
|
10
|
+
case NCS_ERROR_CODE.HEAD_VERSION_UPDATE_FAILED:
|
|
11
|
+
case NCS_ERROR_CODE.VERSION_NUMBER_ALREADY_EXISTS:
|
|
12
|
+
// This should never be called with these errors
|
|
13
|
+
return;
|
|
14
|
+
case INTERNAL_ERROR_CODE.ADD_STEPS_ERROR:
|
|
15
|
+
case INTERNAL_ERROR_CODE.RECONNECTION_ERROR:
|
|
16
|
+
case INTERNAL_ERROR_CODE.CONNECTION_ERROR:
|
|
17
|
+
// These errors shouldn't be emitted, we're hoping the provider self-recovers over time
|
|
18
|
+
return;
|
|
19
|
+
case NCS_ERROR_CODE.INSUFFICIENT_EDITING_PERMISSION:
|
|
20
|
+
case INTERNAL_ERROR_CODE.TOKEN_PERMISSION_ERROR:
|
|
21
|
+
return {
|
|
22
|
+
code: PROVIDER_ERROR_CODE.NO_PERMISSION_ERROR,
|
|
23
|
+
message: 'User does not have permissions to access this document or document is not found',
|
|
24
|
+
reason: error.data.meta.reason,
|
|
25
|
+
recoverable: true,
|
|
26
|
+
status: 403
|
|
27
|
+
};
|
|
28
|
+
case NCS_ERROR_CODE.FORBIDDEN_USER_TOKEN:
|
|
29
|
+
return {
|
|
30
|
+
code: PROVIDER_ERROR_CODE.INVALID_USER_TOKEN,
|
|
31
|
+
message: 'The user token was invalid',
|
|
32
|
+
recoverable: true,
|
|
33
|
+
status: 403
|
|
34
|
+
};
|
|
35
|
+
case INTERNAL_ERROR_CODE.DOCUMENT_NOT_FOUND:
|
|
36
|
+
return {
|
|
37
|
+
code: PROVIDER_ERROR_CODE.DOCUMENT_NOT_FOUND,
|
|
38
|
+
message: 'The requested document is not found',
|
|
39
|
+
recoverable: true,
|
|
40
|
+
status: 404
|
|
41
|
+
};
|
|
42
|
+
case NCS_ERROR_CODE.TENANT_INSTANCE_MAINTENANCE:
|
|
43
|
+
case NCS_ERROR_CODE.LOCKED_DOCUMENT:
|
|
44
|
+
return {
|
|
45
|
+
code: PROVIDER_ERROR_CODE.LOCKED,
|
|
46
|
+
message: 'The document is currently not available, please try again later',
|
|
47
|
+
recoverable: true
|
|
48
|
+
};
|
|
49
|
+
case NCS_ERROR_CODE.DYNAMO_ERROR:
|
|
50
|
+
return {
|
|
51
|
+
code: PROVIDER_ERROR_CODE.FAIL_TO_SAVE,
|
|
52
|
+
message: 'Collab service is not able to save changes',
|
|
53
|
+
recoverable: false,
|
|
54
|
+
status: 500
|
|
55
|
+
};
|
|
56
|
+
case INTERNAL_ERROR_CODE.DOCUMENT_RESTORE_ERROR:
|
|
39
57
|
return {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// Typescript magic so it detects the union type
|
|
45
|
-
typeof error.data.meta === 'object' ? error.data.meta.reason : undefined
|
|
58
|
+
code: PROVIDER_ERROR_CODE.DOCUMENT_RESTORE_ERROR,
|
|
59
|
+
message: 'Collab service unable to restore document',
|
|
60
|
+
recoverable: false,
|
|
61
|
+
status: 500
|
|
46
62
|
};
|
|
47
|
-
case
|
|
63
|
+
case NCS_ERROR_CODE.INIT_DATA_LOAD_FAILED:
|
|
48
64
|
return {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
65
|
+
code: PROVIDER_ERROR_CODE.INITIALISATION_ERROR,
|
|
66
|
+
message: "The initial document couldn't be loaded from the collab service",
|
|
67
|
+
recoverable: false,
|
|
68
|
+
status: 500
|
|
52
69
|
};
|
|
53
|
-
case
|
|
54
|
-
case 'DYNAMO_ERROR':
|
|
70
|
+
case INTERNAL_ERROR_CODE.RECONNECTION_NETWORK_ISSUE:
|
|
55
71
|
return {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
72
|
+
code: PROVIDER_ERROR_CODE.NETWORK_ISSUE,
|
|
73
|
+
message: "Couldn't reconnect to the collab service due to network issues",
|
|
74
|
+
recoverable: true,
|
|
75
|
+
status: 500
|
|
59
76
|
};
|
|
60
|
-
case
|
|
77
|
+
case NCS_ERROR_CODE.NAMESPACE_INVALID:
|
|
78
|
+
case NCS_ERROR_CODE.INVALID_ACTIVATION_ID:
|
|
79
|
+
case NCS_ERROR_CODE.INVALID_DOCUMENT_ARI:
|
|
80
|
+
case NCS_ERROR_CODE.INVALID_CLOUD_ID:
|
|
61
81
|
return {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
82
|
+
code: PROVIDER_ERROR_CODE.INVALID_PROVIDER_CONFIGURATION,
|
|
83
|
+
message: 'Invalid provider configuration',
|
|
84
|
+
recoverable: false,
|
|
85
|
+
reason: (_error$data2 = error.data) === null || _error$data2 === void 0 ? void 0 : _error$data2.code,
|
|
86
|
+
status: 400
|
|
65
87
|
};
|
|
66
|
-
|
|
67
|
-
case
|
|
68
|
-
case
|
|
69
|
-
case
|
|
88
|
+
case NCS_ERROR_CODE.NAMESPACE_NOT_FOUND:
|
|
89
|
+
case NCS_ERROR_CODE.ERROR_MAPPING_ERROR:
|
|
90
|
+
case NCS_ERROR_CODE.EMPTY_BROADCAST:
|
|
91
|
+
case INTERNAL_ERROR_CODE.CATCHUP_FAILED:
|
|
70
92
|
return {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
93
|
+
code: PROVIDER_ERROR_CODE.INTERNAL_SERVICE_ERROR,
|
|
94
|
+
message: 'Collab Provider experienced an unrecoverable error',
|
|
95
|
+
recoverable: false,
|
|
96
|
+
reason: (_error$data3 = error.data) === null || _error$data3 === void 0 ? void 0 : _error$data3.code,
|
|
97
|
+
status: 500
|
|
74
98
|
};
|
|
75
99
|
default:
|
|
76
|
-
|
|
100
|
+
return;
|
|
77
101
|
}
|
|
78
102
|
};
|
|
@@ -1,13 +1,151 @@
|
|
|
1
1
|
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
// Internal error codes (generated by collab provider)
|
|
3
|
+
export let INTERNAL_ERROR_CODE;
|
|
4
|
+
// NCS error coded (generated by NCS)
|
|
5
|
+
(function (INTERNAL_ERROR_CODE) {
|
|
6
|
+
INTERNAL_ERROR_CODE["TOKEN_PERMISSION_ERROR"] = "TOKEN_PERMISSION_ERROR";
|
|
7
|
+
INTERNAL_ERROR_CODE["RECONNECTION_NETWORK_ISSUE"] = "RECONNECTION_NETWORK_ISSUE";
|
|
8
|
+
INTERNAL_ERROR_CODE["CONNECTION_ERROR"] = "CONNECTION_ERROR";
|
|
9
|
+
INTERNAL_ERROR_CODE["RECONNECTION_ERROR"] = "RECONNECTION_ERROR";
|
|
10
|
+
INTERNAL_ERROR_CODE["DOCUMENT_NOT_FOUND"] = "DOCUMENT_NOT_FOUND";
|
|
11
|
+
INTERNAL_ERROR_CODE["CATCHUP_FAILED"] = "CATCHUP_FAILED";
|
|
12
|
+
INTERNAL_ERROR_CODE["DOCUMENT_RESTORE_ERROR"] = "DOCUMENT_RESTORE_ERROR";
|
|
13
|
+
INTERNAL_ERROR_CODE["ADD_STEPS_ERROR"] = "ADD_STEPS_ERROR";
|
|
14
|
+
})(INTERNAL_ERROR_CODE || (INTERNAL_ERROR_CODE = {}));
|
|
15
|
+
export let NCS_ERROR_CODE;
|
|
16
|
+
|
|
17
|
+
// TODO: Import emitted error codes from NCS
|
|
18
|
+
|
|
19
|
+
// NCS Errors
|
|
20
|
+
// - Step rejection errors
|
|
21
|
+
(function (NCS_ERROR_CODE) {
|
|
22
|
+
NCS_ERROR_CODE["HEAD_VERSION_UPDATE_FAILED"] = "HEAD_VERSION_UPDATE_FAILED";
|
|
23
|
+
NCS_ERROR_CODE["VERSION_NUMBER_ALREADY_EXISTS"] = "VERSION_NUMBER_ALREADY_EXISTS";
|
|
24
|
+
NCS_ERROR_CODE["INSUFFICIENT_EDITING_PERMISSION"] = "INSUFFICIENT_EDITING_PERMISSION";
|
|
25
|
+
NCS_ERROR_CODE["FORBIDDEN_USER_TOKEN"] = "FORBIDDEN_USER_TOKEN";
|
|
26
|
+
NCS_ERROR_CODE["DOCUMENT_NOT_FOUND"] = "DOCUMENT_NOT_FOUND";
|
|
27
|
+
NCS_ERROR_CODE["INIT_DATA_LOAD_FAILED"] = "INIT_DATA_LOAD_FAILED";
|
|
28
|
+
NCS_ERROR_CODE["ERROR_MAPPING_ERROR"] = "ERROR_MAPPING_ERROR";
|
|
29
|
+
NCS_ERROR_CODE["NAMESPACE_INVALID"] = "NAMESPACE_INVALID";
|
|
30
|
+
NCS_ERROR_CODE["NAMESPACE_NOT_FOUND"] = "NAMESPACE_NOT_FOUND";
|
|
31
|
+
NCS_ERROR_CODE["TENANT_INSTANCE_MAINTENANCE"] = "TENANT_INSTANCE_MAINTENANCE";
|
|
32
|
+
NCS_ERROR_CODE["LOCKED_DOCUMENT"] = "LOCKED_DOCUMENT";
|
|
33
|
+
NCS_ERROR_CODE["EMPTY_BROADCAST"] = "EMPTY_BROADCAST";
|
|
34
|
+
NCS_ERROR_CODE["DYNAMO_ERROR"] = "DYNAMO_ERROR";
|
|
35
|
+
NCS_ERROR_CODE["INVALID_ACTIVATION_ID"] = "INVALID_ACTIVATION_ID";
|
|
36
|
+
NCS_ERROR_CODE["INVALID_DOCUMENT_ARI"] = "INVALID_DOCUMENT_ARI";
|
|
37
|
+
NCS_ERROR_CODE["INVALID_CLOUD_ID"] = "INVALID_CLOUD_ID";
|
|
38
|
+
})(NCS_ERROR_CODE || (NCS_ERROR_CODE = {}));
|
|
39
|
+
// Emitted errors
|
|
40
|
+
export let PROVIDER_ERROR_CODE;
|
|
41
|
+
|
|
42
|
+
/*
|
|
43
|
+
* This is what a generic ProviderError type would look like:
|
|
44
|
+
* type ProviderError = {
|
|
45
|
+
* // Unique code, identifies the specific emitted error
|
|
46
|
+
* // Also exposed as a PROVIDER_ERROR_CODE enum to allow subscribers to use them
|
|
47
|
+
* code: PROVIDER_ERROR_CODE;
|
|
48
|
+
* // Informative message describing what went wrong
|
|
49
|
+
* message: string;
|
|
50
|
+
* // Flag indicating whether an error is recoverable or not
|
|
51
|
+
* // used by consumers to disable the provider and show an error message
|
|
52
|
+
* recoverable: boolean;
|
|
53
|
+
* // A reason code used to give more detail about why a certain error was thrown
|
|
54
|
+
* reason?: string;
|
|
55
|
+
* }
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* This occurs when the provided user token is considered invalid for the given document ARI.
|
|
60
|
+
* It happens during initialisation of the provider.
|
|
61
|
+
* It could mean the document has been deleted (hence not found).
|
|
62
|
+
* @message Message returned to editor, i.e User does not have permissions to access this document or document is not found
|
|
63
|
+
* @recoverable It is recoverable, as we will try to refresh the token.
|
|
64
|
+
*/
|
|
65
|
+
(function (PROVIDER_ERROR_CODE) {
|
|
66
|
+
PROVIDER_ERROR_CODE["NO_PERMISSION_ERROR"] = "NO_PERMISSION_ERROR";
|
|
67
|
+
PROVIDER_ERROR_CODE["INVALID_USER_TOKEN"] = "INVALID_USER_TOKEN";
|
|
68
|
+
PROVIDER_ERROR_CODE["DOCUMENT_NOT_FOUND"] = "DOCUMENT_NOT_FOUND";
|
|
69
|
+
PROVIDER_ERROR_CODE["LOCKED"] = "LOCKED";
|
|
70
|
+
PROVIDER_ERROR_CODE["FAIL_TO_SAVE"] = "FAIL_TO_SAVE";
|
|
71
|
+
PROVIDER_ERROR_CODE["DOCUMENT_RESTORE_ERROR"] = "DOCUMENT_RESTORE_ERROR";
|
|
72
|
+
PROVIDER_ERROR_CODE["INITIALISATION_ERROR"] = "INITIALISATION_ERROR";
|
|
73
|
+
PROVIDER_ERROR_CODE["NETWORK_ISSUE"] = "NETWORK_ISSUE";
|
|
74
|
+
PROVIDER_ERROR_CODE["INVALID_PROVIDER_CONFIGURATION"] = "INVALID_PROVIDER_CONFIGURATION";
|
|
75
|
+
PROVIDER_ERROR_CODE["INTERNAL_SERVICE_ERROR"] = "INTERNAL_SERVICE_ERROR";
|
|
76
|
+
})(PROVIDER_ERROR_CODE || (PROVIDER_ERROR_CODE = {}));
|
|
77
|
+
// Custom Errors
|
|
78
|
+
class CustomError extends Error {
|
|
79
|
+
constructor(message, error) {
|
|
4
80
|
super(message);
|
|
81
|
+
if (typeof (error === null || error === void 0 ? void 0 : error.message) === 'string') {
|
|
82
|
+
this.message = error.message;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
toJSON() {
|
|
86
|
+
return {
|
|
87
|
+
name: this.name,
|
|
88
|
+
message: this.message
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
export class NotConnectedError extends CustomError {
|
|
93
|
+
constructor(...args) {
|
|
94
|
+
super(...args);
|
|
5
95
|
_defineProperty(this, "name", 'NotConnectedError');
|
|
6
96
|
}
|
|
7
97
|
}
|
|
8
|
-
export class NotInitializedError extends
|
|
9
|
-
constructor(
|
|
10
|
-
super(
|
|
98
|
+
export class NotInitializedError extends CustomError {
|
|
99
|
+
constructor(...args) {
|
|
100
|
+
super(...args);
|
|
11
101
|
_defineProperty(this, "name", 'NotInitializedError');
|
|
12
102
|
}
|
|
103
|
+
}
|
|
104
|
+
export class ProviderInitialisationError extends CustomError {
|
|
105
|
+
constructor(...args) {
|
|
106
|
+
super(...args);
|
|
107
|
+
_defineProperty(this, "name", 'ProviderInitialisationError');
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export class SendTransactionError extends CustomError {
|
|
111
|
+
constructor(...args) {
|
|
112
|
+
super(...args);
|
|
113
|
+
_defineProperty(this, "name", 'SendTransactionError');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export class DestroyError extends CustomError {
|
|
117
|
+
constructor(...args) {
|
|
118
|
+
super(...args);
|
|
119
|
+
_defineProperty(this, "name", 'DestroyError');
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
export class SetTitleError extends CustomError {
|
|
123
|
+
constructor(...args) {
|
|
124
|
+
super(...args);
|
|
125
|
+
_defineProperty(this, "name", 'SetTitleError');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export class SetEditorWidthError extends CustomError {
|
|
129
|
+
constructor(...args) {
|
|
130
|
+
super(...args);
|
|
131
|
+
_defineProperty(this, "name", 'SetEditorWidthError');
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export class SetMetadataError extends CustomError {
|
|
135
|
+
constructor(...args) {
|
|
136
|
+
super(...args);
|
|
137
|
+
_defineProperty(this, "name", 'SetMetadataError');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
export class GetCurrentStateError extends CustomError {
|
|
141
|
+
constructor(...args) {
|
|
142
|
+
super(...args);
|
|
143
|
+
_defineProperty(this, "name", 'GetCurrentStateError');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
export class GetFinalAcknowledgedStateError extends CustomError {
|
|
147
|
+
constructor(...args) {
|
|
148
|
+
super(...args);
|
|
149
|
+
_defineProperty(this, "name", 'GetFinalAcknowledgedStateError');
|
|
150
|
+
}
|
|
13
151
|
}
|
|
@@ -4,16 +4,6 @@ export const createLogger = (prefix, color = 'blue') => (msg, data = null) => {
|
|
|
4
4
|
console.log(`%cCollab-${prefix}: ${msg}`, `color: ${color}; font-weight: bold`, data);
|
|
5
5
|
}
|
|
6
6
|
};
|
|
7
|
-
const logger = createLogger('Helper:util', 'black');
|
|
8
|
-
export const getParticipant = userId => {
|
|
9
|
-
logger('getParticipant: ', userId);
|
|
10
|
-
return {
|
|
11
|
-
userId: userId,
|
|
12
|
-
name: userId,
|
|
13
|
-
avatar: '',
|
|
14
|
-
email: `${userId.replace(/\s/g, '').toLocaleLowerCase()}@atlassian.com`
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
7
|
export function sleep(ms) {
|
|
18
8
|
return new Promise(resolve => {
|
|
19
9
|
setTimeout(resolve, ms);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export const PARTICIPANT_UPDATE_INTERVAL = 300 * 1000; // 300 seconds
|
|
2
|
+
|
|
3
|
+
export const createParticipantFromPayload = async (payload, getUser) => {
|
|
4
|
+
var _user, _user2, _user3;
|
|
5
|
+
const {
|
|
6
|
+
sessionId,
|
|
7
|
+
timestamp,
|
|
8
|
+
clientId,
|
|
9
|
+
userId
|
|
10
|
+
} = payload;
|
|
11
|
+
let user;
|
|
12
|
+
if (getUser) {
|
|
13
|
+
user = await getUser(userId);
|
|
14
|
+
}
|
|
15
|
+
const participant = {
|
|
16
|
+
name: ((_user = user) === null || _user === void 0 ? void 0 : _user.name) || '',
|
|
17
|
+
email: ((_user2 = user) === null || _user2 === void 0 ? void 0 : _user2.email) || '',
|
|
18
|
+
avatar: ((_user3 = user) === null || _user3 === void 0 ? void 0 : _user3.avatar) || '',
|
|
19
|
+
sessionId,
|
|
20
|
+
lastActive: timestamp,
|
|
21
|
+
userId,
|
|
22
|
+
clientId
|
|
23
|
+
};
|
|
24
|
+
return participant;
|
|
25
|
+
};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
import { disconnectedReasonMapper } from '../disconnected-reason-mapper';
|
|
3
|
+
import { EVENT_ACTION, EVENT_STATUS } from '../helpers/const';
|
|
4
|
+
import { telepointerFromStep } from './telepointers-helper';
|
|
5
|
+
import { createParticipantFromPayload as enrichParticipant, PARTICIPANT_UPDATE_INTERVAL } from './participants-helper';
|
|
6
|
+
import { ParticipantsState } from './participants-state';
|
|
7
|
+
export class ParticipantsService {
|
|
8
|
+
constructor(analyticsHelper, participantsState = new ParticipantsState()) {
|
|
9
|
+
_defineProperty(this, "updateParticipant", async (payload, getUser, emit) => {
|
|
10
|
+
const {
|
|
11
|
+
userId
|
|
12
|
+
} = payload;
|
|
13
|
+
|
|
14
|
+
// If userId does not exist, does nothing here to prevent duplication.
|
|
15
|
+
if (!userId) {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
let participant;
|
|
19
|
+
// getUser is a failable callback, hence try-catch
|
|
20
|
+
try {
|
|
21
|
+
participant = await enrichParticipant(
|
|
22
|
+
// userId _must_ be defined, this lets the compiler know
|
|
23
|
+
{
|
|
24
|
+
...payload,
|
|
25
|
+
userId
|
|
26
|
+
}, getUser);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
var _this$analyticsHelper;
|
|
29
|
+
// We don't want to throw errors for Presence features as they tend to self-restore
|
|
30
|
+
(_this$analyticsHelper = this.analyticsHelper) === null || _this$analyticsHelper === void 0 ? void 0 : _this$analyticsHelper.sendErrorEvent(error, 'enriching participant');
|
|
31
|
+
}
|
|
32
|
+
if (!participant) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
const isNewParticipant = this.participantsState.doesntHave(participant.sessionId);
|
|
36
|
+
this.participantsState.setBySessionId(participant.sessionId, participant);
|
|
37
|
+
if (!isNewParticipant) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this.emitPresence({
|
|
41
|
+
joined: [participant]
|
|
42
|
+
}, emit, 'handling participant updated event');
|
|
43
|
+
});
|
|
44
|
+
_defineProperty(this, "participantLeft", ({
|
|
45
|
+
sessionId
|
|
46
|
+
}, emit) => {
|
|
47
|
+
this.participantsState.removeBySessionId(sessionId);
|
|
48
|
+
this.emitPresence({
|
|
49
|
+
left: [{
|
|
50
|
+
sessionId
|
|
51
|
+
}]
|
|
52
|
+
}, emit, 'participant leaving');
|
|
53
|
+
});
|
|
54
|
+
_defineProperty(this, "disconnect", (reason, sessionId, emit) => {
|
|
55
|
+
const left = this.participantsState.getParticipants();
|
|
56
|
+
this.participantsState.clear();
|
|
57
|
+
try {
|
|
58
|
+
emit('disconnected', {
|
|
59
|
+
reason: disconnectedReasonMapper(reason),
|
|
60
|
+
sid: sessionId
|
|
61
|
+
});
|
|
62
|
+
} catch (error) {
|
|
63
|
+
var _this$analyticsHelper2;
|
|
64
|
+
// We don't want to throw errors for Presence features as they tend to self-restore
|
|
65
|
+
(_this$analyticsHelper2 = this.analyticsHelper) === null || _this$analyticsHelper2 === void 0 ? void 0 : _this$analyticsHelper2.sendErrorEvent(error, 'emitting disconnected data');
|
|
66
|
+
}
|
|
67
|
+
if (left.length) {
|
|
68
|
+
this.emitPresence({
|
|
69
|
+
left
|
|
70
|
+
}, emit, 'emitting presence update on disconnect');
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
_defineProperty(this, "updateLastActive", (userIds = []) => this.participantsState.updateLastActive(Date.now(), userIds));
|
|
74
|
+
_defineProperty(this, "participantTelepointer", (payload, thisSessionId, getUser, emit) => {
|
|
75
|
+
const {
|
|
76
|
+
sessionId,
|
|
77
|
+
selection,
|
|
78
|
+
timestamp
|
|
79
|
+
} = payload;
|
|
80
|
+
const participant = this.participantsState.getBySessionId(sessionId);
|
|
81
|
+
if (sessionId === thisSessionId ||
|
|
82
|
+
// Ignore old telepointer events
|
|
83
|
+
participant && participant.lastActive > timestamp) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const userId = payload.userId ? [payload.userId] : undefined;
|
|
87
|
+
|
|
88
|
+
// Set last active
|
|
89
|
+
this.updateLastActive(userId);
|
|
90
|
+
this.emitTelepointer({
|
|
91
|
+
type: 'telepointer',
|
|
92
|
+
selection,
|
|
93
|
+
sessionId
|
|
94
|
+
}, emit, 'handling participant telepointer event');
|
|
95
|
+
});
|
|
96
|
+
_defineProperty(this, "removeInactiveParticipants", (sessionId, emit) => {
|
|
97
|
+
clearTimeout(this.participantUpdateTimeout);
|
|
98
|
+
try {
|
|
99
|
+
this.filterInactive(sessionId, emit);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
var _this$analyticsHelper3;
|
|
102
|
+
(_this$analyticsHelper3 = this.analyticsHelper) === null || _this$analyticsHelper3 === void 0 ? void 0 : _this$analyticsHelper3.sendErrorEvent(err, 'Failed filtering inactive participants');
|
|
103
|
+
}
|
|
104
|
+
this.participantUpdateTimeout = window.setTimeout(() => this.removeInactiveParticipants(sessionId, emit), PARTICIPANT_UPDATE_INTERVAL);
|
|
105
|
+
});
|
|
106
|
+
_defineProperty(this, "filterInactive", (sessionId, emit) => {
|
|
107
|
+
const now = Date.now();
|
|
108
|
+
const left = this.participantsState.getParticipants().filter(p => p.sessionId !== sessionId && now - p.lastActive > PARTICIPANT_UPDATE_INTERVAL);
|
|
109
|
+
left.forEach(p => this.participantsState.removeBySessionId(p.sessionId));
|
|
110
|
+
this.emitPresence({
|
|
111
|
+
left
|
|
112
|
+
}, emit, 'filtering inactive participants');
|
|
113
|
+
});
|
|
114
|
+
_defineProperty(this, "emitPresence", (data, emit, errorMessage) => {
|
|
115
|
+
try {
|
|
116
|
+
var _this$analyticsHelper4;
|
|
117
|
+
emit('presence', data);
|
|
118
|
+
(_this$analyticsHelper4 = this.analyticsHelper) === null || _this$analyticsHelper4 === void 0 ? void 0 : _this$analyticsHelper4.sendActionEvent(EVENT_ACTION.UPDATE_PARTICIPANTS, EVENT_STATUS.SUCCESS, {
|
|
119
|
+
participants: this.participantsState.size()
|
|
120
|
+
});
|
|
121
|
+
} catch (error) {
|
|
122
|
+
var _this$analyticsHelper5;
|
|
123
|
+
// We don't want to throw errors for Presence features as they tend to self-restore
|
|
124
|
+
(_this$analyticsHelper5 = this.analyticsHelper) === null || _this$analyticsHelper5 === void 0 ? void 0 : _this$analyticsHelper5.sendErrorEvent(error, `Error while ${errorMessage}`);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
_defineProperty(this, "emitTelepointer", (data, emit, errorMessage) => {
|
|
128
|
+
try {
|
|
129
|
+
emit('telepointer', data);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
var _this$analyticsHelper6;
|
|
132
|
+
// We don't want to throw errors for Presence features as they tend to self-restore
|
|
133
|
+
(_this$analyticsHelper6 = this.analyticsHelper) === null || _this$analyticsHelper6 === void 0 ? void 0 : _this$analyticsHelper6.sendErrorEvent(error, `Error while ${errorMessage}`);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
this.participantsState = participantsState;
|
|
137
|
+
this.analyticsHelper = analyticsHelper;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Carries out 3 things: 1) enriches the participant with user data, 2) updates the participantsState, 3) emits the presence event
|
|
142
|
+
* @param payload Payload from incoming socket event
|
|
143
|
+
* @param getUser Function to get user data from confluence
|
|
144
|
+
* @param emit Function to execute emit from provider socket
|
|
145
|
+
* @returns Awaitable Promise, due to getUser
|
|
146
|
+
*/
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Called on receiving steps, emits each step's telepointer
|
|
150
|
+
* @param steps Steps to extract telepointers from
|
|
151
|
+
* @param emit Provider emit function
|
|
152
|
+
*/
|
|
153
|
+
emitTelepointersFromSteps(steps, emit) {
|
|
154
|
+
steps.forEach(step => {
|
|
155
|
+
const event = telepointerFromStep(this.participantsState.getParticipants(), step);
|
|
156
|
+
if (event) {
|
|
157
|
+
this.emitTelepointer(event, emit, 'emitting telepointers from steps');
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Called when we receive a telepointer update from another
|
|
164
|
+
* participant.
|
|
165
|
+
*/
|
|
166
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import _defineProperty from "@babel/runtime/helpers/defineProperty";
|
|
2
|
+
export class ParticipantsState {
|
|
3
|
+
constructor(baseParticipants = new Map()) {
|
|
4
|
+
_defineProperty(this, "getBySessionId", sessionId => {
|
|
5
|
+
const participant = this.participants.get(sessionId);
|
|
6
|
+
// Spread to ensure we get a deep copy
|
|
7
|
+
return participant ? {
|
|
8
|
+
...participant
|
|
9
|
+
} : undefined;
|
|
10
|
+
});
|
|
11
|
+
_defineProperty(this, "setBySessionId", (sessionId, participant) => {
|
|
12
|
+
this.participants.set(sessionId, participant);
|
|
13
|
+
});
|
|
14
|
+
_defineProperty(this, "getParticipants", () =>
|
|
15
|
+
// Spread to get deep copy
|
|
16
|
+
[...this.participants.values()].map(p => ({
|
|
17
|
+
...p
|
|
18
|
+
})));
|
|
19
|
+
_defineProperty(this, "removeBySessionId", sessionId => this.participants.delete(sessionId));
|
|
20
|
+
_defineProperty(this, "clear", () => this.participants.clear());
|
|
21
|
+
_defineProperty(this, "doesntHave", sessionId => !this.participants.has(sessionId));
|
|
22
|
+
_defineProperty(this, "size", () => this.participants.size);
|
|
23
|
+
_defineProperty(this, "updateLastActive", (now, userIds) => this.participants.forEach(p => {
|
|
24
|
+
p.lastActive = userIds.includes(p.userId) ? now : p.lastActive;
|
|
25
|
+
}));
|
|
26
|
+
this.participants = baseParticipants;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -2,8 +2,8 @@ import { createLogger } from '../helpers/utils';
|
|
|
2
2
|
import { ExperiencePerformanceTypes, ExperienceTypes, UFOExperience } from '@atlaskit/ufo';
|
|
3
3
|
import { AcknowledgementResponseTypes } from '../types';
|
|
4
4
|
const logger = createLogger('Telepointer', 'green');
|
|
5
|
-
export const
|
|
6
|
-
const [participant] =
|
|
5
|
+
export const telepointerFromStep = (participants, step) => {
|
|
6
|
+
const [participant] = participants.filter(p => p.clientId === step.clientId);
|
|
7
7
|
if (participant) {
|
|
8
8
|
var _node$text;
|
|
9
9
|
const {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import countBy from 'lodash/countBy';
|
|
2
2
|
import { ADD_STEPS_TYPE, EVENT_ACTION, EVENT_STATUS } from '../helpers/const';
|
|
3
3
|
import { AcknowledgementResponseTypes } from '../types';
|
|
4
|
+
import { NCS_ERROR_CODE } from '../errors/error-types';
|
|
4
5
|
export const commitStep = ({
|
|
5
|
-
|
|
6
|
+
broadcast,
|
|
6
7
|
steps,
|
|
7
8
|
version,
|
|
8
9
|
userId,
|
|
@@ -18,7 +19,7 @@ export const commitStep = ({
|
|
|
18
19
|
}));
|
|
19
20
|
const start = new Date().getTime();
|
|
20
21
|
try {
|
|
21
|
-
|
|
22
|
+
broadcast('steps:commit', {
|
|
22
23
|
steps: stepsWithClientAndUserId,
|
|
23
24
|
version,
|
|
24
25
|
userId
|
|
@@ -35,13 +36,12 @@ export const commitStep = ({
|
|
|
35
36
|
stepType: countBy(stepsWithClientAndUserId, stepWithClientAndUserId => stepWithClientAndUserId.stepType)
|
|
36
37
|
});
|
|
37
38
|
} else if (response.type === AcknowledgementResponseTypes.ERROR) {
|
|
38
|
-
var _response$error, _response$error$data, _response$error2, _response$error2$data;
|
|
39
39
|
onErrorHandled(response.error);
|
|
40
40
|
analyticsHelper === null || analyticsHelper === void 0 ? void 0 : analyticsHelper.sendActionEvent(EVENT_ACTION.ADD_STEPS, EVENT_STATUS.FAILURE, {
|
|
41
41
|
// User tried committing steps but they were rejected because:
|
|
42
42
|
// - HEAD_VERSION_UPDATE_FAILED: the collab service's latest stored step tail version didn't correspond to the head version of the first step submitted
|
|
43
43
|
// - VERSION_NUMBER_ALREADY_EXISTS: while storing the steps there was a conflict meaning someone else wrote steps into the database more quickly
|
|
44
|
-
type:
|
|
44
|
+
type: response.error.data.code === NCS_ERROR_CODE.HEAD_VERSION_UPDATE_FAILED || response.error.data.code === NCS_ERROR_CODE.VERSION_NUMBER_ALREADY_EXISTS ? ADD_STEPS_TYPE.REJECTED : ADD_STEPS_TYPE.ERROR,
|
|
45
45
|
latency
|
|
46
46
|
});
|
|
47
47
|
analyticsHelper === null || analyticsHelper === void 0 ? void 0 : analyticsHelper.sendErrorEvent(response.error, 'Error while adding steps - Acknowledgement Error');
|