@fluidframework/routerlicious-driver 2.0.0-dev.3.1.0.125672 → 2.0.0-dev.4.2.0.153917
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 +5 -0
- package/README.md +38 -0
- package/dist/cache.js +2 -2
- package/dist/cache.js.map +1 -1
- package/dist/deltaStorageService.d.ts +1 -1
- package/dist/deltaStorageService.d.ts.map +1 -1
- package/dist/deltaStorageService.js +7 -4
- package/dist/deltaStorageService.js.map +1 -1
- package/dist/documentService.d.ts +4 -2
- package/dist/documentService.d.ts.map +1 -1
- package/dist/documentService.js +20 -12
- package/dist/documentService.js.map +1 -1
- package/dist/documentServiceFactory.d.ts +2 -2
- package/dist/documentServiceFactory.d.ts.map +1 -1
- package/dist/documentServiceFactory.js +21 -12
- package/dist/documentServiceFactory.js.map +1 -1
- package/dist/documentStorageService.d.ts +3 -2
- package/dist/documentStorageService.d.ts.map +1 -1
- package/dist/documentStorageService.js +4 -4
- package/dist/documentStorageService.js.map +1 -1
- package/dist/errorUtils.d.ts +9 -2
- package/dist/errorUtils.d.ts.map +1 -1
- package/dist/errorUtils.js +15 -8
- package/dist/errorUtils.js.map +1 -1
- package/dist/gitManager.d.ts +30 -0
- package/dist/gitManager.d.ts.map +1 -0
- package/dist/gitManager.js +89 -0
- package/dist/gitManager.js.map +1 -0
- package/dist/historian.d.ts +33 -0
- package/dist/historian.d.ts.map +1 -0
- package/dist/historian.js +65 -0
- package/dist/historian.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/mapWithExpiration.d.ts +34 -0
- package/dist/mapWithExpiration.d.ts.map +1 -0
- package/dist/mapWithExpiration.js +105 -0
- package/dist/mapWithExpiration.js.map +1 -0
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/restWrapper.d.ts +21 -8
- package/dist/restWrapper.d.ts.map +1 -1
- package/dist/restWrapper.js +93 -25
- package/dist/restWrapper.js.map +1 -1
- package/dist/restWrapperBase.d.ts +26 -0
- package/dist/restWrapperBase.d.ts.map +1 -0
- package/dist/restWrapperBase.js +55 -0
- package/dist/restWrapperBase.js.map +1 -0
- package/dist/retriableGitManager.d.ts +10 -21
- package/dist/retriableGitManager.d.ts.map +1 -1
- package/dist/retriableGitManager.js +0 -36
- package/dist/retriableGitManager.js.map +1 -1
- package/dist/shreddedSummaryDocumentStorageService.d.ts +1 -1
- package/dist/shreddedSummaryDocumentStorageService.d.ts.map +1 -1
- package/dist/shreddedSummaryDocumentStorageService.js +6 -6
- package/dist/shreddedSummaryDocumentStorageService.js.map +1 -1
- package/dist/storageContracts.d.ts +44 -0
- package/dist/storageContracts.d.ts.map +1 -0
- package/dist/storageContracts.js +7 -0
- package/dist/storageContracts.js.map +1 -0
- package/dist/summaryTreeUploadManager.d.ts +23 -0
- package/dist/summaryTreeUploadManager.d.ts.map +1 -0
- package/dist/summaryTreeUploadManager.js +110 -0
- package/dist/summaryTreeUploadManager.js.map +1 -0
- package/dist/tokens.d.ts +24 -7
- package/dist/tokens.d.ts.map +1 -1
- package/dist/tokens.js.map +1 -1
- package/dist/treeUtils.d.ts +58 -0
- package/dist/treeUtils.d.ts.map +1 -0
- package/dist/treeUtils.js +107 -0
- package/dist/treeUtils.js.map +1 -0
- package/dist/wholeSummaryDocumentStorageService.d.ts +5 -4
- package/dist/wholeSummaryDocumentStorageService.d.ts.map +1 -1
- package/dist/wholeSummaryDocumentStorageService.js +70 -38
- package/dist/wholeSummaryDocumentStorageService.js.map +1 -1
- package/dist/wholeSummaryUploadManager.d.ts +16 -0
- package/dist/wholeSummaryUploadManager.d.ts.map +1 -0
- package/dist/wholeSummaryUploadManager.js +38 -0
- package/dist/wholeSummaryUploadManager.js.map +1 -0
- package/lib/cache.js +1 -1
- package/lib/cache.js.map +1 -1
- package/lib/deltaStorageService.d.ts +1 -1
- package/lib/deltaStorageService.d.ts.map +1 -1
- package/lib/deltaStorageService.js +7 -4
- package/lib/deltaStorageService.js.map +1 -1
- package/lib/documentService.d.ts +4 -2
- package/lib/documentService.d.ts.map +1 -1
- package/lib/documentService.js +20 -12
- package/lib/documentService.js.map +1 -1
- package/lib/documentServiceFactory.d.ts +2 -2
- package/lib/documentServiceFactory.d.ts.map +1 -1
- package/lib/documentServiceFactory.js +22 -13
- package/lib/documentServiceFactory.js.map +1 -1
- package/lib/documentStorageService.d.ts +3 -2
- package/lib/documentStorageService.d.ts.map +1 -1
- package/lib/documentStorageService.js +4 -4
- package/lib/documentStorageService.js.map +1 -1
- package/lib/errorUtils.d.ts +9 -2
- package/lib/errorUtils.d.ts.map +1 -1
- package/lib/errorUtils.js +14 -7
- package/lib/errorUtils.js.map +1 -1
- package/lib/gitManager.d.ts +30 -0
- package/lib/gitManager.d.ts.map +1 -0
- package/lib/gitManager.js +85 -0
- package/lib/gitManager.js.map +1 -0
- package/lib/historian.d.ts +33 -0
- package/lib/historian.d.ts.map +1 -0
- package/lib/historian.js +60 -0
- package/lib/historian.js.map +1 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -0
- package/lib/index.js.map +1 -1
- package/lib/mapWithExpiration.d.ts +34 -0
- package/lib/mapWithExpiration.d.ts.map +1 -0
- package/lib/mapWithExpiration.js +101 -0
- package/lib/mapWithExpiration.js.map +1 -0
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/restWrapper.d.ts +21 -8
- package/lib/restWrapper.d.ts.map +1 -1
- package/lib/restWrapper.js +92 -26
- package/lib/restWrapper.js.map +1 -1
- package/lib/restWrapperBase.d.ts +26 -0
- package/lib/restWrapperBase.d.ts.map +1 -0
- package/lib/restWrapperBase.js +50 -0
- package/lib/restWrapperBase.js.map +1 -0
- package/lib/retriableGitManager.d.ts +10 -21
- package/lib/retriableGitManager.d.ts.map +1 -1
- package/lib/retriableGitManager.js +0 -36
- package/lib/retriableGitManager.js.map +1 -1
- package/lib/shreddedSummaryDocumentStorageService.d.ts +1 -1
- package/lib/shreddedSummaryDocumentStorageService.d.ts.map +1 -1
- package/lib/shreddedSummaryDocumentStorageService.js +5 -5
- package/lib/shreddedSummaryDocumentStorageService.js.map +1 -1
- package/lib/storageContracts.d.ts +44 -0
- package/lib/storageContracts.d.ts.map +1 -0
- package/lib/storageContracts.js +6 -0
- package/lib/storageContracts.js.map +1 -0
- package/lib/summaryTreeUploadManager.d.ts +23 -0
- package/lib/summaryTreeUploadManager.d.ts.map +1 -0
- package/lib/summaryTreeUploadManager.js +106 -0
- package/lib/summaryTreeUploadManager.js.map +1 -0
- package/lib/tokens.d.ts +24 -7
- package/lib/tokens.d.ts.map +1 -1
- package/lib/tokens.js.map +1 -1
- package/lib/treeUtils.d.ts +58 -0
- package/lib/treeUtils.d.ts.map +1 -0
- package/lib/treeUtils.js +100 -0
- package/lib/treeUtils.js.map +1 -0
- package/lib/wholeSummaryDocumentStorageService.d.ts +5 -4
- package/lib/wholeSummaryDocumentStorageService.d.ts.map +1 -1
- package/lib/wholeSummaryDocumentStorageService.js +70 -38
- package/lib/wholeSummaryDocumentStorageService.js.map +1 -1
- package/lib/wholeSummaryUploadManager.d.ts +16 -0
- package/lib/wholeSummaryUploadManager.d.ts.map +1 -0
- package/lib/wholeSummaryUploadManager.js +34 -0
- package/lib/wholeSummaryUploadManager.js.map +1 -0
- package/package.json +52 -54
- package/src/cache.ts +1 -1
- package/src/deltaStorageService.ts +11 -3
- package/src/documentService.ts +37 -22
- package/src/documentServiceFactory.ts +40 -21
- package/src/documentStorageService.ts +8 -4
- package/src/errorUtils.ts +16 -4
- package/src/gitManager.ts +116 -0
- package/src/historian.ts +121 -0
- package/src/index.ts +3 -0
- package/src/mapWithExpiration.ts +124 -0
- package/src/packageVersion.ts +1 -1
- package/src/restWrapper.ts +114 -38
- package/src/restWrapperBase.ts +146 -0
- package/src/retriableGitManager.ts +17 -95
- package/src/shreddedSummaryDocumentStorageService.ts +7 -9
- package/src/storageContracts.ts +63 -0
- package/src/summaryTreeUploadManager.ts +160 -0
- package/src/tokens.ts +24 -7
- package/src/treeUtils.ts +137 -0
- package/src/wholeSummaryDocumentStorageService.ts +118 -46
- package/src/wholeSummaryUploadManager.ts +64 -0
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { assert } from "@fluidframework/common-utils";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* An extension of Map that expires (deletes) entries after a period of inactivity.
|
|
10
|
+
* The policy is based on the last time a key was written to.
|
|
11
|
+
*/
|
|
12
|
+
export class MapWithExpiration<TKey = any, TValue = any> extends Map<TKey, TValue> {
|
|
13
|
+
/** Timestamps (as epoch ms numbers) of when each key was last refreshed */
|
|
14
|
+
private readonly lastRefreshedTimes = new Map<TKey, number>();
|
|
15
|
+
|
|
16
|
+
constructor(private readonly expiryMs: number) {
|
|
17
|
+
super();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
private refresh(key: TKey): void {
|
|
21
|
+
this.lastRefreshedTimes.set(key, new Date().valueOf());
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns true if the key is present and expired, false if it's not expired, and undefined if it's not found
|
|
26
|
+
* If cleanUp is passed as true, then delete any expired entry before returning.
|
|
27
|
+
*/
|
|
28
|
+
private checkExpiry(key: TKey, cleanUp: boolean = false): boolean | undefined {
|
|
29
|
+
const refreshTime = this.lastRefreshedTimes.get(key);
|
|
30
|
+
assert(
|
|
31
|
+
(refreshTime !== undefined) === super.has(key),
|
|
32
|
+
0x50c /* freshness map out of sync */,
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
if (refreshTime === undefined) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const expired = new Date().valueOf() - refreshTime >= this.expiryMs;
|
|
39
|
+
if (expired && cleanUp) {
|
|
40
|
+
this.delete(key);
|
|
41
|
+
}
|
|
42
|
+
return expired;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private clearExpiredEntries() {
|
|
46
|
+
// forEach clears out any expired entries
|
|
47
|
+
this.forEach(() => {});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
get size(): number {
|
|
51
|
+
this.clearExpiredEntries();
|
|
52
|
+
return super.size;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
has(key: TKey): boolean {
|
|
56
|
+
this.checkExpiry(key, true /* cleanUp */);
|
|
57
|
+
return super.has(key);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get(key: TKey): TValue | undefined {
|
|
61
|
+
this.checkExpiry(key, true /* cleanUp */);
|
|
62
|
+
return super.get(key);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
set(key: TKey, value: TValue): this {
|
|
66
|
+
// Sliding window expiration policy (on write)
|
|
67
|
+
this.refresh(key);
|
|
68
|
+
return super.set(key, value);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
delete(key: TKey): boolean {
|
|
72
|
+
this.lastRefreshedTimes.delete(key);
|
|
73
|
+
return super.delete(key);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
clear(): void {
|
|
77
|
+
this.lastRefreshedTimes.clear();
|
|
78
|
+
super.clear();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
forEach(
|
|
82
|
+
callbackfn: (value: TValue, key: TKey, map: Map<TKey, TValue>) => void,
|
|
83
|
+
thisArg?: any,
|
|
84
|
+
): void {
|
|
85
|
+
const expiredKeys: TKey[] = [];
|
|
86
|
+
super.forEach(
|
|
87
|
+
(v, k, m) => {
|
|
88
|
+
if (this.checkExpiry(k) === true) {
|
|
89
|
+
expiredKeys.push(k);
|
|
90
|
+
} else {
|
|
91
|
+
callbackfn.bind(thisArg)(v, k, m);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
// Note we don't pass thisArg here, since we bind it directly to callbackFn and don't need it otherwise
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
// Clean up keys we know are expired now that we're done iterating
|
|
98
|
+
expiredKeys.forEach((key: TKey) => {
|
|
99
|
+
this.delete(key);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
entries(): IterableIterator<[TKey, TValue]> {
|
|
104
|
+
this.clearExpiredEntries();
|
|
105
|
+
return super.entries();
|
|
106
|
+
}
|
|
107
|
+
keys(): IterableIterator<TKey> {
|
|
108
|
+
this.clearExpiredEntries();
|
|
109
|
+
return super.keys();
|
|
110
|
+
}
|
|
111
|
+
values(): IterableIterator<TValue> {
|
|
112
|
+
this.clearExpiredEntries();
|
|
113
|
+
return super.values();
|
|
114
|
+
}
|
|
115
|
+
[Symbol.iterator](): IterableIterator<[TKey, TValue]> {
|
|
116
|
+
this.clearExpiredEntries();
|
|
117
|
+
return super[Symbol.iterator]();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
valueOf() {
|
|
121
|
+
this.clearExpiredEntries();
|
|
122
|
+
return super.valueOf();
|
|
123
|
+
}
|
|
124
|
+
}
|
package/src/packageVersion.ts
CHANGED
package/src/restWrapper.ts
CHANGED
|
@@ -3,19 +3,14 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import type { ParsedUrlQueryInput } from "querystring";
|
|
9
|
-
|
|
10
|
-
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
11
|
-
import { fromUtf8ToBase64 } from "@fluidframework/common-utils";
|
|
6
|
+
import { ITelemetryLogger, ITelemetryProperties } from "@fluidframework/common-definitions";
|
|
7
|
+
import { assert, fromUtf8ToBase64, performance } from "@fluidframework/common-utils";
|
|
12
8
|
import { RateLimiter } from "@fluidframework/driver-utils";
|
|
13
9
|
import {
|
|
14
10
|
getAuthorizationTokenFromCredentials,
|
|
15
11
|
RestLessClient,
|
|
16
|
-
RestWrapper,
|
|
17
12
|
} from "@fluidframework/server-services-client";
|
|
18
|
-
import { PerformanceEvent } from "@fluidframework/telemetry-utils";
|
|
13
|
+
import { PerformanceEvent, TelemetryLogger } from "@fluidframework/telemetry-utils";
|
|
19
14
|
import fetch from "cross-fetch";
|
|
20
15
|
import type { AxiosRequestConfig, AxiosRequestHeaders } from "axios";
|
|
21
16
|
import safeStringify from "json-stringify-safe";
|
|
@@ -23,6 +18,7 @@ import { v4 as uuid } from "uuid";
|
|
|
23
18
|
import { throwR11sNetworkError } from "./errorUtils";
|
|
24
19
|
import { ITokenProvider, ITokenResponse } from "./tokens";
|
|
25
20
|
import { pkgVersion as driverVersion } from "./packageVersion";
|
|
21
|
+
import { QueryStringType, RestWrapper } from "./restWrapperBase";
|
|
26
22
|
|
|
27
23
|
type AuthorizationHeaderGetter = (token: ITokenResponse) => string;
|
|
28
24
|
type TokenFetcher = (refresh?: boolean) => Promise<ITokenResponse>;
|
|
@@ -44,18 +40,75 @@ const axiosRequestConfigToFetchRequestConfig = (
|
|
|
44
40
|
return [requestInfo, requestInit];
|
|
45
41
|
};
|
|
46
42
|
|
|
43
|
+
export interface IR11sResponse<T> {
|
|
44
|
+
content: T;
|
|
45
|
+
headers: Map<string, string>;
|
|
46
|
+
propsToLog: ITelemetryProperties;
|
|
47
|
+
requestUrl: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A utility function to create a r11s response without any additional props as we might not have them always.
|
|
52
|
+
* @param content - response which is equivalent to content.
|
|
53
|
+
* @returns - a r11s response without any extra props.
|
|
54
|
+
*/
|
|
55
|
+
export function createR11sResponseFromContent<T>(content: T): IR11sResponse<T> {
|
|
56
|
+
return {
|
|
57
|
+
content,
|
|
58
|
+
headers: new Map(),
|
|
59
|
+
propsToLog: {},
|
|
60
|
+
requestUrl: "",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function headersToMap(headers: Headers) {
|
|
65
|
+
const newHeaders = new Map<string, string>();
|
|
66
|
+
for (const [key, value] of headers.entries()) {
|
|
67
|
+
newHeaders.set(key, value);
|
|
68
|
+
}
|
|
69
|
+
return newHeaders;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function getPropsToLogFromResponse(headers: {
|
|
73
|
+
get: (id: string) => string | undefined | null;
|
|
74
|
+
}) {
|
|
75
|
+
interface LoggingHeader {
|
|
76
|
+
headerName: string;
|
|
77
|
+
logName: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// We rename headers so that otel doesn't scrub them away. Otel doesn't allow
|
|
81
|
+
// certain characters in headers including '-'
|
|
82
|
+
const headersToLog: LoggingHeader[] = [
|
|
83
|
+
{ headerName: "x-correlation-id", logName: "requestCorrelationId" },
|
|
84
|
+
{ headerName: "content-encoding", logName: "contentEncoding" },
|
|
85
|
+
{ headerName: "content-type", logName: "contentType" },
|
|
86
|
+
];
|
|
87
|
+
const additionalProps: ITelemetryProperties = {
|
|
88
|
+
contentsize: TelemetryLogger.numberFromString(headers.get("content-length")),
|
|
89
|
+
};
|
|
90
|
+
headersToLog.forEach((header) => {
|
|
91
|
+
const headerValue = headers.get(header.headerName);
|
|
92
|
+
if (headerValue !== undefined && headerValue !== null) {
|
|
93
|
+
additionalProps[header.logName] = headerValue;
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return additionalProps;
|
|
98
|
+
}
|
|
99
|
+
|
|
47
100
|
export class RouterliciousRestWrapper extends RestWrapper {
|
|
48
101
|
private readonly restLess = new RestLessClient();
|
|
102
|
+
private token: ITokenResponse | undefined;
|
|
49
103
|
|
|
50
104
|
constructor(
|
|
51
105
|
logger: ITelemetryLogger,
|
|
52
106
|
private readonly rateLimiter: RateLimiter,
|
|
53
|
-
private token: ITokenResponse,
|
|
54
107
|
private readonly fetchRefreshedToken: TokenFetcher,
|
|
55
108
|
private readonly getAuthorizationHeader: AuthorizationHeaderGetter,
|
|
56
109
|
private readonly useRestLess: boolean,
|
|
57
110
|
baseurl?: string,
|
|
58
|
-
defaultQueryString:
|
|
111
|
+
defaultQueryString: QueryStringType = {},
|
|
59
112
|
) {
|
|
60
113
|
super(baseurl, defaultQueryString);
|
|
61
114
|
}
|
|
@@ -64,33 +117,59 @@ export class RouterliciousRestWrapper extends RestWrapper {
|
|
|
64
117
|
requestConfig: AxiosRequestConfig,
|
|
65
118
|
statusCode: number,
|
|
66
119
|
canRetry = true,
|
|
67
|
-
): Promise<T
|
|
120
|
+
): Promise<IR11sResponse<T>> {
|
|
68
121
|
const config = {
|
|
69
122
|
...requestConfig,
|
|
70
|
-
headers: this.generateHeaders(requestConfig.headers),
|
|
123
|
+
headers: await this.generateHeaders(requestConfig.headers),
|
|
71
124
|
};
|
|
72
125
|
|
|
73
126
|
const translatedConfig = this.useRestLess ? this.restLess.translate(config) : config;
|
|
74
127
|
const fetchRequestConfig = axiosRequestConfigToFetchRequestConfig(translatedConfig);
|
|
75
128
|
|
|
76
|
-
const
|
|
77
|
-
|
|
129
|
+
const res = await this.rateLimiter.schedule(async () => {
|
|
130
|
+
const perfStart = performance.now();
|
|
131
|
+
const result = await fetch(...fetchRequestConfig).catch(async (error) => {
|
|
78
132
|
// Browser Fetch throws a TypeError on network error, `node-fetch` throws a FetchError
|
|
79
133
|
const isNetworkError = ["TypeError", "FetchError"].includes(error?.name);
|
|
80
134
|
throwR11sNetworkError(
|
|
81
135
|
isNetworkError ? `NetworkError: ${error.message}` : safeStringify(error),
|
|
82
136
|
);
|
|
83
|
-
})
|
|
84
|
-
|
|
137
|
+
});
|
|
138
|
+
return {
|
|
139
|
+
response: result,
|
|
140
|
+
duration: performance.now() - perfStart,
|
|
141
|
+
};
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const response = res.response;
|
|
85
145
|
|
|
146
|
+
let start = performance.now();
|
|
147
|
+
const text = await response.text();
|
|
148
|
+
const receiveContentTime = performance.now() - start;
|
|
149
|
+
|
|
150
|
+
const bodySize = text.length;
|
|
151
|
+
start = performance.now();
|
|
86
152
|
const responseBody: any = response.headers.get("content-type")?.includes("application/json")
|
|
87
|
-
?
|
|
88
|
-
:
|
|
153
|
+
? JSON.parse(text)
|
|
154
|
+
: text;
|
|
155
|
+
const parseTime = performance.now() - start;
|
|
89
156
|
|
|
90
157
|
// Success
|
|
91
158
|
if (response.ok || response.status === statusCode) {
|
|
92
|
-
const result
|
|
93
|
-
|
|
159
|
+
const result = responseBody as T;
|
|
160
|
+
const headers = headersToMap(response.headers);
|
|
161
|
+
return {
|
|
162
|
+
content: result,
|
|
163
|
+
headers,
|
|
164
|
+
requestUrl: fetchRequestConfig[0].toString(),
|
|
165
|
+
propsToLog: {
|
|
166
|
+
...getPropsToLogFromResponse(headers),
|
|
167
|
+
bodySize,
|
|
168
|
+
receiveContentTime,
|
|
169
|
+
parseTime,
|
|
170
|
+
fetchTime: res.duration,
|
|
171
|
+
},
|
|
172
|
+
};
|
|
94
173
|
}
|
|
95
174
|
// Failure
|
|
96
175
|
if (response.status === 401 && canRetry) {
|
|
@@ -100,7 +179,7 @@ export class RouterliciousRestWrapper extends RestWrapper {
|
|
|
100
179
|
}
|
|
101
180
|
if (response.status === 429 && responseBody?.retryAfter > 0) {
|
|
102
181
|
// Retry based on retryAfter[Seconds]
|
|
103
|
-
return new Promise<T
|
|
182
|
+
return new Promise<IR11sResponse<T>>((resolve, reject) =>
|
|
104
183
|
setTimeout(() => {
|
|
105
184
|
this.request<T>(config, statusCode).then(resolve).catch(reject);
|
|
106
185
|
}, responseBody.retryAfter * 1000),
|
|
@@ -120,9 +199,11 @@ export class RouterliciousRestWrapper extends RestWrapper {
|
|
|
120
199
|
);
|
|
121
200
|
}
|
|
122
201
|
|
|
123
|
-
private generateHeaders(
|
|
202
|
+
private async generateHeaders(
|
|
124
203
|
requestHeaders?: AxiosRequestHeaders | undefined,
|
|
125
|
-
): Record<string, string
|
|
204
|
+
): Promise<Record<string, string>> {
|
|
205
|
+
const token = await this.getToken();
|
|
206
|
+
assert(token !== undefined, "token should be present");
|
|
126
207
|
const correlationId = requestHeaders?.["x-correlation-id"] ?? uuid();
|
|
127
208
|
|
|
128
209
|
return {
|
|
@@ -132,12 +213,17 @@ export class RouterliciousRestWrapper extends RestWrapper {
|
|
|
132
213
|
"x-correlation-id": correlationId as string,
|
|
133
214
|
"x-driver-version": driverVersion,
|
|
134
215
|
// NOTE: If this.authorizationHeader is undefined, should "Authorization" be removed entirely?
|
|
135
|
-
"Authorization": this.getAuthorizationHeader(
|
|
216
|
+
"Authorization": this.getAuthorizationHeader(token),
|
|
136
217
|
};
|
|
137
218
|
}
|
|
138
219
|
|
|
139
|
-
public getToken(): ITokenResponse {
|
|
140
|
-
|
|
220
|
+
public async getToken(): Promise<ITokenResponse> {
|
|
221
|
+
if (this.token !== undefined) {
|
|
222
|
+
return this.token;
|
|
223
|
+
}
|
|
224
|
+
const token = await this.fetchRefreshedToken();
|
|
225
|
+
this.setToken(token);
|
|
226
|
+
return token;
|
|
141
227
|
}
|
|
142
228
|
|
|
143
229
|
public setToken(token: ITokenResponse) {
|
|
@@ -149,17 +235,15 @@ export class RouterliciousStorageRestWrapper extends RouterliciousRestWrapper {
|
|
|
149
235
|
private constructor(
|
|
150
236
|
logger: ITelemetryLogger,
|
|
151
237
|
rateLimiter: RateLimiter,
|
|
152
|
-
token: ITokenResponse,
|
|
153
238
|
fetchToken: TokenFetcher,
|
|
154
239
|
getAuthorizationHeader: AuthorizationHeaderGetter,
|
|
155
240
|
useRestLess: boolean,
|
|
156
241
|
baseurl?: string,
|
|
157
|
-
defaultQueryString:
|
|
242
|
+
defaultQueryString: QueryStringType = {},
|
|
158
243
|
) {
|
|
159
244
|
super(
|
|
160
245
|
logger,
|
|
161
246
|
rateLimiter,
|
|
162
|
-
token,
|
|
163
247
|
fetchToken,
|
|
164
248
|
getAuthorizationHeader,
|
|
165
249
|
useRestLess,
|
|
@@ -211,12 +295,9 @@ export class RouterliciousStorageRestWrapper extends RouterliciousRestWrapper {
|
|
|
211
295
|
return getAuthorizationTokenFromCredentials(credentials);
|
|
212
296
|
};
|
|
213
297
|
|
|
214
|
-
const storagetoken = await fetchStorageToken();
|
|
215
|
-
|
|
216
298
|
const restWrapper = new RouterliciousStorageRestWrapper(
|
|
217
299
|
logger,
|
|
218
300
|
rateLimiter,
|
|
219
|
-
storagetoken,
|
|
220
301
|
fetchStorageToken,
|
|
221
302
|
getAuthorizationHeader,
|
|
222
303
|
useRestLess,
|
|
@@ -232,17 +313,15 @@ export class RouterliciousOrdererRestWrapper extends RouterliciousRestWrapper {
|
|
|
232
313
|
private constructor(
|
|
233
314
|
logger: ITelemetryLogger,
|
|
234
315
|
rateLimiter: RateLimiter,
|
|
235
|
-
token: ITokenResponse,
|
|
236
316
|
fetchToken: TokenFetcher,
|
|
237
317
|
getAuthorizationHeader: AuthorizationHeaderGetter,
|
|
238
318
|
useRestLess: boolean,
|
|
239
319
|
baseurl?: string,
|
|
240
|
-
defaultQueryString:
|
|
320
|
+
defaultQueryString: QueryStringType = {},
|
|
241
321
|
) {
|
|
242
322
|
super(
|
|
243
323
|
logger,
|
|
244
324
|
rateLimiter,
|
|
245
|
-
token,
|
|
246
325
|
fetchToken,
|
|
247
326
|
getAuthorizationHeader,
|
|
248
327
|
useRestLess,
|
|
@@ -285,12 +364,9 @@ export class RouterliciousOrdererRestWrapper extends RouterliciousRestWrapper {
|
|
|
285
364
|
);
|
|
286
365
|
};
|
|
287
366
|
|
|
288
|
-
const newtoken = await fetchOrdererToken();
|
|
289
|
-
|
|
290
367
|
const restWrapper = new RouterliciousOrdererRestWrapper(
|
|
291
368
|
logger,
|
|
292
369
|
rateLimiter,
|
|
293
|
-
newtoken,
|
|
294
370
|
fetchOrdererToken,
|
|
295
371
|
getAuthorizationHeader,
|
|
296
372
|
useRestLess,
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { AxiosRequestConfig, AxiosRequestHeaders } from "axios";
|
|
7
|
+
import { IR11sResponse } from "./restWrapper";
|
|
8
|
+
|
|
9
|
+
export abstract class RestWrapper {
|
|
10
|
+
constructor(
|
|
11
|
+
protected readonly baseurl?: string,
|
|
12
|
+
protected defaultQueryString: QueryStringType = {},
|
|
13
|
+
protected readonly maxBodyLength = 1000 * 1024 * 1024,
|
|
14
|
+
protected readonly maxContentLength = 1000 * 1024 * 1024,
|
|
15
|
+
) {}
|
|
16
|
+
|
|
17
|
+
public async get<T>(
|
|
18
|
+
url: string,
|
|
19
|
+
queryString?: QueryStringType,
|
|
20
|
+
headers?: AxiosRequestHeaders,
|
|
21
|
+
additionalOptions?: Partial<
|
|
22
|
+
Omit<
|
|
23
|
+
AxiosRequestConfig,
|
|
24
|
+
"baseURL" | "headers" | "maxBodyLength" | "maxContentLength" | "method" | "url"
|
|
25
|
+
>
|
|
26
|
+
>,
|
|
27
|
+
): Promise<IR11sResponse<T>> {
|
|
28
|
+
const options: AxiosRequestConfig = {
|
|
29
|
+
...additionalOptions,
|
|
30
|
+
baseURL: this.baseurl,
|
|
31
|
+
headers,
|
|
32
|
+
maxBodyLength: this.maxBodyLength,
|
|
33
|
+
maxContentLength: this.maxContentLength,
|
|
34
|
+
method: "GET",
|
|
35
|
+
url: `${url}${this.generateQueryString(queryString)}`,
|
|
36
|
+
};
|
|
37
|
+
return this.request<T>(options, 200);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
public async post<T>(
|
|
41
|
+
url: string,
|
|
42
|
+
requestBody: any,
|
|
43
|
+
queryString?: QueryStringType,
|
|
44
|
+
headers?: AxiosRequestHeaders,
|
|
45
|
+
additionalOptions?: Partial<
|
|
46
|
+
Omit<
|
|
47
|
+
AxiosRequestConfig,
|
|
48
|
+
"baseURL" | "headers" | "maxBodyLength" | "maxContentLength" | "method" | "url"
|
|
49
|
+
>
|
|
50
|
+
>,
|
|
51
|
+
): Promise<IR11sResponse<T>> {
|
|
52
|
+
const options: AxiosRequestConfig = {
|
|
53
|
+
...additionalOptions,
|
|
54
|
+
baseURL: this.baseurl,
|
|
55
|
+
data: requestBody,
|
|
56
|
+
headers,
|
|
57
|
+
maxBodyLength: this.maxBodyLength,
|
|
58
|
+
maxContentLength: this.maxContentLength,
|
|
59
|
+
method: "POST",
|
|
60
|
+
url: `${url}${this.generateQueryString(queryString)}`,
|
|
61
|
+
};
|
|
62
|
+
return this.request<T>(options, 201);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public async delete<T>(
|
|
66
|
+
url: string,
|
|
67
|
+
queryString?: QueryStringType,
|
|
68
|
+
headers?: AxiosRequestHeaders,
|
|
69
|
+
additionalOptions?: Partial<
|
|
70
|
+
Omit<
|
|
71
|
+
AxiosRequestConfig,
|
|
72
|
+
"baseURL" | "headers" | "maxBodyLength" | "maxContentLength" | "method" | "url"
|
|
73
|
+
>
|
|
74
|
+
>,
|
|
75
|
+
): Promise<IR11sResponse<T>> {
|
|
76
|
+
const options: AxiosRequestConfig = {
|
|
77
|
+
...additionalOptions,
|
|
78
|
+
baseURL: this.baseurl,
|
|
79
|
+
headers,
|
|
80
|
+
maxBodyLength: this.maxBodyLength,
|
|
81
|
+
maxContentLength: this.maxContentLength,
|
|
82
|
+
method: "DELETE",
|
|
83
|
+
url: `${url}${this.generateQueryString(queryString)}`,
|
|
84
|
+
};
|
|
85
|
+
return this.request<T>(options, 204);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public async patch<T>(
|
|
89
|
+
url: string,
|
|
90
|
+
requestBody: any,
|
|
91
|
+
queryString?: QueryStringType,
|
|
92
|
+
headers?: AxiosRequestHeaders,
|
|
93
|
+
additionalOptions?: Partial<
|
|
94
|
+
Omit<
|
|
95
|
+
AxiosRequestConfig,
|
|
96
|
+
"baseURL" | "headers" | "maxBodyLength" | "maxContentLength" | "method" | "url"
|
|
97
|
+
>
|
|
98
|
+
>,
|
|
99
|
+
): Promise<IR11sResponse<T>> {
|
|
100
|
+
const options: AxiosRequestConfig = {
|
|
101
|
+
...additionalOptions,
|
|
102
|
+
baseURL: this.baseurl,
|
|
103
|
+
data: requestBody,
|
|
104
|
+
headers,
|
|
105
|
+
maxBodyLength: this.maxBodyLength,
|
|
106
|
+
maxContentLength: this.maxContentLength,
|
|
107
|
+
method: "PATCH",
|
|
108
|
+
url: `${url}${this.generateQueryString(queryString)}`,
|
|
109
|
+
};
|
|
110
|
+
return this.request<T>(options, 200);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
protected abstract request<T>(
|
|
114
|
+
options: AxiosRequestConfig,
|
|
115
|
+
statusCode: number,
|
|
116
|
+
addNetworkCallProps?: boolean,
|
|
117
|
+
): Promise<IR11sResponse<T>>;
|
|
118
|
+
|
|
119
|
+
protected generateQueryString(queryStringValues?: QueryStringType) {
|
|
120
|
+
if (this.defaultQueryString || queryStringValues) {
|
|
121
|
+
const queryStringMap = { ...this.defaultQueryString, ...queryStringValues };
|
|
122
|
+
|
|
123
|
+
return getQueryString(queryStringMap);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return "";
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Generates query string from the given query parameters.
|
|
132
|
+
* @param queryParams - Query parameters from which to create a query.
|
|
133
|
+
*/
|
|
134
|
+
export function getQueryString(queryParams: QueryStringType): string {
|
|
135
|
+
let queryString = "";
|
|
136
|
+
for (const key of Object.keys(queryParams)) {
|
|
137
|
+
if (queryParams[key] !== undefined) {
|
|
138
|
+
const startChar = queryString === "" ? "?" : "&";
|
|
139
|
+
queryString += `${startChar}${key}=${encodeURIComponent(queryParams[key])}`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return queryString;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export type QueryStringType = Record<string, string | number | boolean>;
|