@fluidframework/routerlicious-driver 2.5.0-302463 → 2.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 +4 -0
- package/dist/contracts.d.ts +6 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +7 -0
- package/dist/contracts.js.map +1 -1
- package/dist/documentDeltaConnection.d.ts +5 -1
- package/dist/documentDeltaConnection.d.ts.map +1 -1
- package/dist/documentDeltaConnection.js +12 -2
- package/dist/documentDeltaConnection.js.map +1 -1
- package/dist/documentService.d.ts.map +1 -1
- package/dist/documentService.js +6 -7
- package/dist/documentService.js.map +1 -1
- package/dist/errorUtils.d.ts +31 -4
- package/dist/errorUtils.d.ts.map +1 -1
- package/dist/errorUtils.js +78 -8
- package/dist/errorUtils.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/restWrapper.d.ts.map +1 -1
- package/dist/restWrapper.js +15 -10
- package/dist/restWrapper.js.map +1 -1
- package/dist/socketModule.d.ts +7 -0
- package/dist/socketModule.d.ts.map +1 -0
- package/dist/socketModule.js +11 -0
- package/dist/socketModule.js.map +1 -0
- package/lib/contracts.d.ts +6 -0
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js +6 -1
- package/lib/contracts.js.map +1 -1
- package/lib/documentDeltaConnection.d.ts +5 -1
- package/lib/documentDeltaConnection.d.ts.map +1 -1
- package/lib/documentDeltaConnection.js +13 -3
- package/lib/documentDeltaConnection.js.map +1 -1
- package/lib/documentService.d.ts.map +1 -1
- package/lib/documentService.js +6 -4
- package/lib/documentService.js.map +1 -1
- package/lib/errorUtils.d.ts +31 -4
- package/lib/errorUtils.d.ts.map +1 -1
- package/lib/errorUtils.js +75 -7
- package/lib/errorUtils.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/restWrapper.d.ts.map +1 -1
- package/lib/restWrapper.js +16 -11
- package/lib/restWrapper.js.map +1 -1
- package/lib/socketModule.d.ts +7 -0
- package/lib/socketModule.d.ts.map +1 -0
- package/lib/socketModule.js +8 -0
- package/lib/socketModule.js.map +1 -0
- package/package.json +11 -11
- package/src/contracts.ts +7 -0
- package/src/documentDeltaConnection.ts +32 -3
- package/src/documentService.ts +9 -5
- package/src/errorUtils.ts +103 -6
- package/src/packageVersion.ts +1 -1
- package/src/restWrapper.ts +29 -10
- package/src/socketModule.ts +9 -0
package/src/errorUtils.ts
CHANGED
|
@@ -13,9 +13,11 @@ import {
|
|
|
13
13
|
GenericNetworkError,
|
|
14
14
|
NonRetryableError,
|
|
15
15
|
createGenericNetworkError,
|
|
16
|
+
type DriverErrorTelemetryProps,
|
|
16
17
|
} from "@fluidframework/driver-utils/internal";
|
|
17
|
-
import { IFluidErrorBase } from "@fluidframework/telemetry-utils/internal";
|
|
18
|
+
import { IFluidErrorBase, LoggingError } from "@fluidframework/telemetry-utils/internal";
|
|
18
19
|
|
|
20
|
+
import { R11sServiceClusterDrainingErrorCode } from "./contracts.js";
|
|
19
21
|
import { pkgVersion as driverVersion } from "./packageVersion.js";
|
|
20
22
|
|
|
21
23
|
/**
|
|
@@ -31,6 +33,12 @@ export const RouterliciousErrorTypes = {
|
|
|
31
33
|
* SSL Certificate Error.
|
|
32
34
|
*/
|
|
33
35
|
sslCertError: "sslCertError",
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Error for when the service drains a cluster to which the socket connection is connected to and it disconnects
|
|
39
|
+
* all the clients in that cluster.
|
|
40
|
+
*/
|
|
41
|
+
clusterDrainingError: "clusterDrainingError",
|
|
34
42
|
} as const;
|
|
35
43
|
/**
|
|
36
44
|
* @internal
|
|
@@ -66,6 +74,24 @@ export interface IR11sSocketError {
|
|
|
66
74
|
* The client should wait this many milliseconds before retrying its request.
|
|
67
75
|
*/
|
|
68
76
|
retryAfterMs?: number;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Optional internalErrorCode to specify the specific error code for the error within the main code above.
|
|
80
|
+
*/
|
|
81
|
+
internalErrorCode?: string | number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export class ClusterDrainingError extends LoggingError implements IFluidErrorBase {
|
|
85
|
+
readonly errorType = RouterliciousErrorTypes.clusterDrainingError;
|
|
86
|
+
readonly canRetry = true;
|
|
87
|
+
|
|
88
|
+
constructor(
|
|
89
|
+
message: string,
|
|
90
|
+
readonly retryAfterSeconds: number,
|
|
91
|
+
props: DriverErrorTelemetryProps,
|
|
92
|
+
) {
|
|
93
|
+
super(message, props);
|
|
94
|
+
}
|
|
69
95
|
}
|
|
70
96
|
|
|
71
97
|
export interface IR11sError extends Omit<IDriverErrorBase, "errorType"> {
|
|
@@ -78,9 +104,11 @@ export function createR11sNetworkError(
|
|
|
78
104
|
errorMessage: string,
|
|
79
105
|
statusCode: number,
|
|
80
106
|
retryAfterMs?: number,
|
|
107
|
+
additionalProps?: DriverErrorTelemetryProps,
|
|
108
|
+
internalErrorCode?: string | number,
|
|
81
109
|
): IFluidErrorBase & R11sError {
|
|
82
110
|
let error: IFluidErrorBase & R11sError;
|
|
83
|
-
const props = { statusCode, driverVersion };
|
|
111
|
+
const props = { ...additionalProps, statusCode, driverVersion };
|
|
84
112
|
switch (statusCode) {
|
|
85
113
|
case 401:
|
|
86
114
|
// The first 401 is manually retried in RouterliciousRestWrapper with a refreshed token,
|
|
@@ -99,6 +127,15 @@ export function createR11sNetworkError(
|
|
|
99
127
|
case 502:
|
|
100
128
|
error = new GenericNetworkError(errorMessage, true, props);
|
|
101
129
|
break;
|
|
130
|
+
case 503:
|
|
131
|
+
if (internalErrorCode === R11sServiceClusterDrainingErrorCode) {
|
|
132
|
+
error = new ClusterDrainingError(
|
|
133
|
+
errorMessage,
|
|
134
|
+
retryAfterMs !== undefined ? retryAfterMs / 1000 : 660,
|
|
135
|
+
props,
|
|
136
|
+
);
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
102
139
|
default:
|
|
103
140
|
const retryInfo = { canRetry: retryAfterMs !== undefined, retryAfterMs };
|
|
104
141
|
error = createGenericNetworkError(errorMessage, retryInfo, props);
|
|
@@ -112,10 +149,9 @@ export function throwR11sNetworkError(
|
|
|
112
149
|
errorMessage: string,
|
|
113
150
|
statusCode: number,
|
|
114
151
|
retryAfterMs?: number,
|
|
152
|
+
additionalProps?: DriverErrorTelemetryProps,
|
|
115
153
|
): never {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
throw networkError;
|
|
154
|
+
throw createR11sNetworkError(errorMessage, statusCode, retryAfterMs, additionalProps);
|
|
119
155
|
}
|
|
120
156
|
|
|
121
157
|
/**
|
|
@@ -124,8 +160,69 @@ export function throwR11sNetworkError(
|
|
|
124
160
|
export function errorObjectFromSocketError(
|
|
125
161
|
socketError: IR11sSocketError,
|
|
126
162
|
handler: string,
|
|
163
|
+
additionalProps?: DriverErrorTelemetryProps,
|
|
127
164
|
): R11sError {
|
|
128
165
|
// pre-0.58 error message prefix: R11sSocketError
|
|
129
166
|
const message = `R11s socket error (${handler}): ${socketError.message}`;
|
|
130
|
-
|
|
167
|
+
const error = createR11sNetworkError(
|
|
168
|
+
message,
|
|
169
|
+
socketError.code,
|
|
170
|
+
socketError.retryAfterMs,
|
|
171
|
+
additionalProps,
|
|
172
|
+
socketError.internalErrorCode,
|
|
173
|
+
);
|
|
174
|
+
error.addTelemetryProperties({
|
|
175
|
+
relayServiceError: true,
|
|
176
|
+
scenarioName: handler,
|
|
177
|
+
internalErrorCode: socketError.internalErrorCode,
|
|
178
|
+
});
|
|
179
|
+
return error;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Simulate the pathname for socket connection */
|
|
183
|
+
export const socketIoPath = "socket.io";
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Get a stripped version of a URL safe for r11s telemetry
|
|
187
|
+
* @returns undefined if no appropriate hostName is provided
|
|
188
|
+
*/
|
|
189
|
+
export function getUrlForTelemetry(hostName: string, path: string = ""): string | undefined {
|
|
190
|
+
// Strip off "http://" or "https://"
|
|
191
|
+
const hostNameMatch = hostName.match(/^(?:https?:\/\/)?([^/]+)/);
|
|
192
|
+
if (!hostNameMatch) {
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
const strippedHostName = hostNameMatch[1];
|
|
196
|
+
|
|
197
|
+
let extractedPath = "";
|
|
198
|
+
// Extract portions of the path and explicitly match them to known path names
|
|
199
|
+
for (const portion of path.split("/")) {
|
|
200
|
+
if (portion !== "") {
|
|
201
|
+
// eslint-disable-next-line unicorn/prefer-ternary
|
|
202
|
+
if (
|
|
203
|
+
[
|
|
204
|
+
socketIoPath,
|
|
205
|
+
"repos",
|
|
206
|
+
"deltas",
|
|
207
|
+
"documents",
|
|
208
|
+
"session",
|
|
209
|
+
"git",
|
|
210
|
+
"summaries",
|
|
211
|
+
"latest",
|
|
212
|
+
"document",
|
|
213
|
+
"commits",
|
|
214
|
+
"blobs",
|
|
215
|
+
"refs",
|
|
216
|
+
"revokeToken",
|
|
217
|
+
"accesstoken",
|
|
218
|
+
].includes(portion)
|
|
219
|
+
) {
|
|
220
|
+
extractedPath += `/${portion}`;
|
|
221
|
+
} else {
|
|
222
|
+
extractedPath += "/REDACTED";
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return `${strippedHostName}${extractedPath}`;
|
|
131
228
|
}
|
package/src/packageVersion.ts
CHANGED
package/src/restWrapper.ts
CHANGED
|
@@ -26,7 +26,11 @@ import fetch from "cross-fetch";
|
|
|
26
26
|
import safeStringify from "json-stringify-safe";
|
|
27
27
|
|
|
28
28
|
import type { AxiosRequestConfig, RawAxiosRequestHeaders } from "./axios.cjs";
|
|
29
|
-
import {
|
|
29
|
+
import {
|
|
30
|
+
getUrlForTelemetry,
|
|
31
|
+
RouterliciousErrorTypes,
|
|
32
|
+
throwR11sNetworkError,
|
|
33
|
+
} from "./errorUtils.js";
|
|
30
34
|
import { pkgVersion as driverVersion } from "./packageVersion.js";
|
|
31
35
|
import { addOrUpdateQueryParams, type QueryStringType } from "./queryStringUtils.js";
|
|
32
36
|
import { RestWrapper } from "./restWrapperBase.js";
|
|
@@ -174,6 +178,13 @@ class RouterliciousRestWrapper extends RestWrapper {
|
|
|
174
178
|
// on failure, add the request entry into the retryCounter map to count the subsequent retries, if any
|
|
175
179
|
this.retryCounter.set(requestKey, requestRetryCount ? requestRetryCount + 1 : 1);
|
|
176
180
|
|
|
181
|
+
const telemetryProps = {
|
|
182
|
+
driverVersion,
|
|
183
|
+
retryCount: requestRetryCount,
|
|
184
|
+
url: getUrlForTelemetry(completeRequestUrl.hostname, completeRequestUrl.pathname),
|
|
185
|
+
requestMethod: fetchRequestConfig.method,
|
|
186
|
+
};
|
|
187
|
+
|
|
177
188
|
// Browser Fetch throws a TypeError on network error, `node-fetch` throws a FetchError
|
|
178
189
|
const isNetworkError = ["TypeError", "FetchError"].includes(error?.name);
|
|
179
190
|
const errorMessage = isNetworkError
|
|
@@ -185,14 +196,16 @@ class RouterliciousRestWrapper extends RestWrapper {
|
|
|
185
196
|
// If there exists a self-signed SSL certificates error, throw a NonRetryableError
|
|
186
197
|
// TODO: instead of relying on string matching, filter error based on the error code like we do for websocket connections
|
|
187
198
|
const err = errorMessage.includes("failed, reason: self signed certificate")
|
|
188
|
-
? new NonRetryableError(
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
199
|
+
? new NonRetryableError(
|
|
200
|
+
errorMessage,
|
|
201
|
+
RouterliciousErrorTypes.sslCertError,
|
|
202
|
+
telemetryProps,
|
|
203
|
+
)
|
|
204
|
+
: new GenericNetworkError(
|
|
205
|
+
errorMessage,
|
|
206
|
+
errorMessage.startsWith("NetworkError"),
|
|
207
|
+
telemetryProps,
|
|
208
|
+
);
|
|
196
209
|
throw err;
|
|
197
210
|
},
|
|
198
211
|
);
|
|
@@ -203,6 +216,7 @@ class RouterliciousRestWrapper extends RestWrapper {
|
|
|
203
216
|
});
|
|
204
217
|
|
|
205
218
|
const response = res.response;
|
|
219
|
+
const headers = headersToMap(response.headers);
|
|
206
220
|
|
|
207
221
|
let start = performance.now();
|
|
208
222
|
const text = await response.text();
|
|
@@ -222,7 +236,6 @@ class RouterliciousRestWrapper extends RestWrapper {
|
|
|
222
236
|
// on successful response, remove the entry from the retryCounter map
|
|
223
237
|
this.retryCounter.delete(requestKey);
|
|
224
238
|
const result = responseBody as T;
|
|
225
|
-
const headers = headersToMap(response.headers);
|
|
226
239
|
return {
|
|
227
240
|
content: result,
|
|
228
241
|
headers,
|
|
@@ -268,6 +281,12 @@ class RouterliciousRestWrapper extends RestWrapper {
|
|
|
268
281
|
`R11s fetch error: ${responseSummary}`,
|
|
269
282
|
response.status,
|
|
270
283
|
responseBody?.retryAfter,
|
|
284
|
+
{
|
|
285
|
+
...getPropsToLogFromResponse(headers),
|
|
286
|
+
driverVersion,
|
|
287
|
+
url: getUrlForTelemetry(completeRequestUrl.hostname, completeRequestUrl.pathname),
|
|
288
|
+
requestMethod: fetchRequestConfig.method,
|
|
289
|
+
},
|
|
271
290
|
);
|
|
272
291
|
}
|
|
273
292
|
|