@fluidframework/odsp-driver 0.59.4001 → 1.1.0-75972
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/.eslintrc.js +1 -1
- package/dist/compactSnapshotParser.d.ts +1 -1
- package/dist/compactSnapshotParser.d.ts.map +1 -1
- package/dist/compactSnapshotParser.js +7 -4
- package/dist/compactSnapshotParser.js.map +1 -1
- package/dist/compactSnapshotWriter.d.ts +1 -1
- package/dist/compactSnapshotWriter.d.ts.map +1 -1
- package/dist/compactSnapshotWriter.js +4 -3
- package/dist/compactSnapshotWriter.js.map +1 -1
- package/dist/contracts.d.ts +1 -1
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/createFile.d.ts.map +1 -1
- package/dist/createFile.js.map +1 -1
- package/dist/createNewUtils.d.ts +1 -1
- package/dist/createNewUtils.d.ts.map +1 -1
- package/dist/createNewUtils.js +1 -0
- package/dist/createNewUtils.js.map +1 -1
- package/dist/epochTracker.d.ts +3 -2
- package/dist/epochTracker.d.ts.map +1 -1
- package/dist/epochTracker.js +10 -4
- package/dist/epochTracker.js.map +1 -1
- package/dist/fetchSnapshot.d.ts +7 -6
- package/dist/fetchSnapshot.d.ts.map +1 -1
- package/dist/fetchSnapshot.js +57 -31
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/odspDocumentDeltaConnection.js +18 -17
- package/dist/odspDocumentDeltaConnection.js.map +1 -1
- package/dist/odspDocumentService.d.ts.map +1 -1
- package/dist/odspDocumentService.js +8 -4
- package/dist/odspDocumentService.js.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.js +2 -2
- package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
- package/dist/odspDocumentStorageManager.d.ts +2 -3
- package/dist/odspDocumentStorageManager.d.ts.map +1 -1
- package/dist/odspDocumentStorageManager.js +13 -17
- package/dist/odspDocumentStorageManager.js.map +1 -1
- package/dist/odspError.d.ts +3 -1
- package/dist/odspError.d.ts.map +1 -1
- package/dist/odspError.js +14 -5
- package/dist/odspError.js.map +1 -1
- package/dist/odspPublicUtils.d.ts +14 -0
- package/dist/odspPublicUtils.d.ts.map +1 -1
- package/dist/odspPublicUtils.js.map +1 -1
- package/dist/odspSnapshotParser.d.ts +2 -2
- package/dist/odspSnapshotParser.d.ts.map +1 -1
- package/dist/odspSnapshotParser.js +7 -4
- package/dist/odspSnapshotParser.js.map +1 -1
- package/dist/odspSummaryUploadManager.d.ts.map +1 -1
- package/dist/odspSummaryUploadManager.js +1 -4
- package/dist/odspSummaryUploadManager.js.map +1 -1
- package/dist/odspUtils.d.ts +0 -7
- package/dist/odspUtils.d.ts.map +1 -1
- package/dist/odspUtils.js +4 -3
- package/dist/odspUtils.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/retryErrorsStorageAdapter.d.ts +1 -2
- package/dist/retryErrorsStorageAdapter.d.ts.map +1 -1
- package/dist/retryErrorsStorageAdapter.js +0 -3
- package/dist/retryErrorsStorageAdapter.js.map +1 -1
- package/dist/retryUtils.d.ts.map +1 -1
- package/dist/retryUtils.js +2 -3
- package/dist/retryUtils.js.map +1 -1
- package/lib/compactSnapshotParser.d.ts +1 -1
- package/lib/compactSnapshotParser.d.ts.map +1 -1
- package/lib/compactSnapshotParser.js +7 -4
- package/lib/compactSnapshotParser.js.map +1 -1
- package/lib/compactSnapshotWriter.d.ts +1 -1
- package/lib/compactSnapshotWriter.d.ts.map +1 -1
- package/lib/compactSnapshotWriter.js +4 -3
- package/lib/compactSnapshotWriter.js.map +1 -1
- package/lib/contracts.d.ts +1 -1
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/createFile.d.ts.map +1 -1
- package/lib/createFile.js.map +1 -1
- package/lib/createNewUtils.d.ts +1 -1
- package/lib/createNewUtils.d.ts.map +1 -1
- package/lib/createNewUtils.js +1 -0
- package/lib/createNewUtils.js.map +1 -1
- package/lib/epochTracker.d.ts +3 -2
- package/lib/epochTracker.d.ts.map +1 -1
- package/lib/epochTracker.js +10 -4
- package/lib/epochTracker.js.map +1 -1
- package/lib/fetchSnapshot.d.ts +7 -6
- package/lib/fetchSnapshot.d.ts.map +1 -1
- package/lib/fetchSnapshot.js +58 -33
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -1
- package/lib/odspDocumentDeltaConnection.js +18 -17
- package/lib/odspDocumentDeltaConnection.js.map +1 -1
- package/lib/odspDocumentService.d.ts.map +1 -1
- package/lib/odspDocumentService.js +9 -5
- package/lib/odspDocumentService.js.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.js +2 -2
- package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
- package/lib/odspDocumentStorageManager.d.ts +2 -3
- package/lib/odspDocumentStorageManager.d.ts.map +1 -1
- package/lib/odspDocumentStorageManager.js +13 -17
- package/lib/odspDocumentStorageManager.js.map +1 -1
- package/lib/odspError.d.ts +3 -1
- package/lib/odspError.d.ts.map +1 -1
- package/lib/odspError.js +14 -5
- package/lib/odspError.js.map +1 -1
- package/lib/odspPublicUtils.d.ts +14 -0
- package/lib/odspPublicUtils.d.ts.map +1 -1
- package/lib/odspPublicUtils.js.map +1 -1
- package/lib/odspSnapshotParser.d.ts +2 -2
- package/lib/odspSnapshotParser.d.ts.map +1 -1
- package/lib/odspSnapshotParser.js +5 -2
- package/lib/odspSnapshotParser.js.map +1 -1
- package/lib/odspSummaryUploadManager.d.ts.map +1 -1
- package/lib/odspSummaryUploadManager.js +1 -4
- package/lib/odspSummaryUploadManager.js.map +1 -1
- package/lib/odspUtils.d.ts +0 -7
- package/lib/odspUtils.d.ts.map +1 -1
- package/lib/odspUtils.js +4 -3
- package/lib/odspUtils.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/retryErrorsStorageAdapter.d.ts +1 -2
- package/lib/retryErrorsStorageAdapter.d.ts.map +1 -1
- package/lib/retryErrorsStorageAdapter.js +0 -3
- package/lib/retryErrorsStorageAdapter.js.map +1 -1
- package/lib/retryUtils.d.ts.map +1 -1
- package/lib/retryUtils.js +3 -4
- package/lib/retryUtils.js.map +1 -1
- package/package.json +15 -28
- package/src/compactSnapshotParser.ts +10 -4
- package/src/compactSnapshotWriter.ts +7 -4
- package/src/contracts.ts +1 -1
- package/src/createFile.ts +2 -2
- package/src/createNewUtils.ts +2 -1
- package/src/epochTracker.ts +10 -3
- package/src/fetchSnapshot.ts +113 -67
- package/src/index.ts +4 -0
- package/src/odspDocumentDeltaConnection.ts +18 -18
- package/src/odspDocumentService.ts +7 -3
- package/src/odspDocumentServiceFactoryCore.ts +4 -2
- package/src/odspDocumentStorageManager.ts +18 -21
- package/src/odspError.ts +23 -10
- package/src/odspPublicUtils.ts +17 -0
- package/src/odspSnapshotParser.ts +8 -3
- package/src/odspSummaryUploadManager.ts +1 -4
- package/src/odspUtils.ts +4 -11
- package/src/packageVersion.ts +1 -1
- package/src/retryErrorsStorageAdapter.ts +0 -8
- package/src/retryUtils.ts +3 -4
package/src/epochTracker.ts
CHANGED
|
@@ -55,6 +55,7 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
55
55
|
protected readonly cache: IPersistedCache,
|
|
56
56
|
protected readonly fileEntry: IFileEntry,
|
|
57
57
|
protected readonly logger: ITelemetryLogger,
|
|
58
|
+
protected readonly clientIsSummarizer?: boolean,
|
|
58
59
|
) {
|
|
59
60
|
// Limits the max number of concurrent requests to 24.
|
|
60
61
|
this.rateLimiter = new RateLimiter(24);
|
|
@@ -300,7 +301,12 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
300
301
|
}
|
|
301
302
|
|
|
302
303
|
private formatClientCorrelationId(fetchReason?: string) {
|
|
303
|
-
const items: string[] = [
|
|
304
|
+
const items: string[] = [
|
|
305
|
+
`driverId=${this.driverId}`,
|
|
306
|
+
`RequestNumber=${this.networkCallNumber++}`,
|
|
307
|
+
`driverVersion=${driverVersion}`,
|
|
308
|
+
`isSummarizer=${this.clientIsSummarizer}`,
|
|
309
|
+
];
|
|
304
310
|
if (fetchReason !== undefined) {
|
|
305
311
|
items.push(`fetchReason=${fetchReason}`);
|
|
306
312
|
}
|
|
@@ -475,8 +481,9 @@ export function createOdspCacheAndTracker(
|
|
|
475
481
|
persistedCacheArg: IPersistedCache,
|
|
476
482
|
nonpersistentCache: INonPersistentCache,
|
|
477
483
|
fileEntry: IFileEntry,
|
|
478
|
-
logger: ITelemetryLogger
|
|
479
|
-
|
|
484
|
+
logger: ITelemetryLogger,
|
|
485
|
+
clientIsSummarizer?: boolean): ICacheAndTracker {
|
|
486
|
+
const epochTracker = new EpochTrackerWithRedemption(persistedCacheArg, fileEntry, logger, clientIsSummarizer);
|
|
480
487
|
return {
|
|
481
488
|
cache: {
|
|
482
489
|
...nonpersistentCache,
|
package/src/fetchSnapshot.ts
CHANGED
|
@@ -8,7 +8,7 @@ import { v4 as uuid } from "uuid";
|
|
|
8
8
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
9
9
|
import { assert, fromUtf8ToBase64, performance } from "@fluidframework/common-utils";
|
|
10
10
|
import { DriverErrorType } from "@fluidframework/driver-definitions";
|
|
11
|
-
import { PerformanceEvent } from "@fluidframework/telemetry-utils";
|
|
11
|
+
import { isFluidError, PerformanceEvent, wrapError } from "@fluidframework/telemetry-utils";
|
|
12
12
|
import {
|
|
13
13
|
IOdspResolvedUrl,
|
|
14
14
|
ISnapshotOptions,
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
InstrumentedStorageTokenFetcher,
|
|
17
17
|
} from "@fluidframework/odsp-driver-definitions";
|
|
18
18
|
import { ISnapshotTree } from "@fluidframework/protocol-definitions";
|
|
19
|
-
import { isRuntimeMessage } from "@fluidframework/driver-utils";
|
|
19
|
+
import { isRuntimeMessage, NonRetryableError } from "@fluidframework/driver-utils";
|
|
20
20
|
import { IOdspSnapshot, ISnapshotCachedEntry, IVersionedValueWithEpoch, persistedCacheValueVersion } from "./contracts";
|
|
21
21
|
import { getQueryString } from "./getQueryString";
|
|
22
22
|
import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth";
|
|
@@ -26,12 +26,13 @@ import {
|
|
|
26
26
|
getWithRetryForTokenRefresh,
|
|
27
27
|
getWithRetryForTokenRefreshRepeat,
|
|
28
28
|
IOdspResponse,
|
|
29
|
-
ISnapshotContents,
|
|
30
29
|
} from "./odspUtils";
|
|
31
|
-
import {
|
|
30
|
+
import { ISnapshotContents } from "./odspPublicUtils";
|
|
31
|
+
import { convertOdspSnapshotToSnapshotTreeAndBlobs } from "./odspSnapshotParser";
|
|
32
32
|
import { currentReadVersion, parseCompactSnapshotResponse } from "./compactSnapshotParser";
|
|
33
33
|
import { ReadBuffer } from "./ReadBufferUtils";
|
|
34
34
|
import { EpochTracker } from "./epochTracker";
|
|
35
|
+
import { pkgVersion } from "./packageVersion";
|
|
35
36
|
|
|
36
37
|
/**
|
|
37
38
|
* Enum to support different types of snapshot formats.
|
|
@@ -83,7 +84,7 @@ export async function fetchSnapshot(
|
|
|
83
84
|
},
|
|
84
85
|
async () => snapshotDownloader(url, { headers }),
|
|
85
86
|
) as IOdspResponse<IOdspSnapshot>;
|
|
86
|
-
return
|
|
87
|
+
return convertOdspSnapshotToSnapshotTreeAndBlobs(response.content);
|
|
87
88
|
}
|
|
88
89
|
|
|
89
90
|
export async function fetchSnapshotWithRedeem(
|
|
@@ -93,11 +94,11 @@ export async function fetchSnapshotWithRedeem(
|
|
|
93
94
|
forceAccessTokenViaAuthorizationHeader: boolean,
|
|
94
95
|
logger: ITelemetryLogger,
|
|
95
96
|
snapshotDownloader: (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
97
|
+
finalOdspResolvedUrl: IOdspResolvedUrl,
|
|
98
|
+
storageToken: string,
|
|
99
|
+
snapshotOptions: ISnapshotOptions | undefined,
|
|
100
|
+
controller?: AbortController,
|
|
101
|
+
) => Promise<ISnapshotRequestAndResponseOptions>,
|
|
101
102
|
putInCache: (valueWithEpoch: IVersionedValueWithEpoch) => Promise<void>,
|
|
102
103
|
removeEntries: () => Promise<void>,
|
|
103
104
|
enableRedeemFallback?: boolean,
|
|
@@ -126,12 +127,13 @@ export async function fetchSnapshotWithRedeem(
|
|
|
126
127
|
await redeemSharingLink(
|
|
127
128
|
odspResolvedUrl, storageTokenFetcher, logger, forceAccessTokenViaAuthorizationHeader);
|
|
128
129
|
const odspResolvedUrlWithoutShareLink: IOdspResolvedUrl =
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
130
|
+
{
|
|
131
|
+
...odspResolvedUrl,
|
|
132
|
+
shareLinkInfo: {
|
|
133
|
+
...odspResolvedUrl.shareLinkInfo,
|
|
134
|
+
sharingLinkToRedeem: undefined,
|
|
135
|
+
},
|
|
136
|
+
};
|
|
135
137
|
|
|
136
138
|
return fetchLatestSnapshotCore(
|
|
137
139
|
odspResolvedUrlWithoutShareLink,
|
|
@@ -168,15 +170,15 @@ async function redeemSharingLink(
|
|
|
168
170
|
eventName: "RedeemShareLink",
|
|
169
171
|
},
|
|
170
172
|
async () => getWithRetryForTokenRefresh(async (tokenFetchOptions) => {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
173
|
+
assert(!!odspResolvedUrl.shareLinkInfo?.sharingLinkToRedeem,
|
|
174
|
+
0x1ed /* "Share link should be present" */);
|
|
175
|
+
const storageToken = await storageTokenFetcher(tokenFetchOptions, "RedeemShareLink");
|
|
176
|
+
const encodedShareUrl = getEncodedShareUrl(odspResolvedUrl.shareLinkInfo?.sharingLinkToRedeem);
|
|
177
|
+
const redeemUrl = `${odspResolvedUrl.siteUrl}/_api/v2.0/shares/${encodedShareUrl}`;
|
|
178
|
+
const { url, headers } = getUrlAndHeadersWithAuth(
|
|
179
|
+
redeemUrl, storageToken, forceAccessTokenViaAuthorizationHeader);
|
|
180
|
+
headers.prefer = "redeemSharingLink";
|
|
181
|
+
return fetchAndParseAsJSONHelper(url, { headers });
|
|
180
182
|
}),
|
|
181
183
|
);
|
|
182
184
|
}
|
|
@@ -187,11 +189,11 @@ async function fetchLatestSnapshotCore(
|
|
|
187
189
|
snapshotOptions: ISnapshotOptions | undefined,
|
|
188
190
|
logger: ITelemetryLogger,
|
|
189
191
|
snapshotDownloader: (
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
192
|
+
finalOdspResolvedUrl: IOdspResolvedUrl,
|
|
193
|
+
storageToken: string,
|
|
194
|
+
snapshotOptions: ISnapshotOptions | undefined,
|
|
195
|
+
controller?: AbortController,
|
|
196
|
+
) => Promise<ISnapshotRequestAndResponseOptions>,
|
|
195
197
|
putInCache: (valueWithEpoch: IVersionedValueWithEpoch) => Promise<void>,
|
|
196
198
|
enableRedeemFallback?: boolean,
|
|
197
199
|
): Promise<ISnapshotContents> {
|
|
@@ -232,7 +234,64 @@ async function fetchLatestSnapshotCore(
|
|
|
232
234
|
snapshotOptions,
|
|
233
235
|
controller,
|
|
234
236
|
);
|
|
235
|
-
const
|
|
237
|
+
const odspResponse = response.odspResponse;
|
|
238
|
+
const contentType = odspResponse.headers.get("content-type");
|
|
239
|
+
odspResponse.propsToLog = {
|
|
240
|
+
...odspResponse.propsToLog,
|
|
241
|
+
contentType,
|
|
242
|
+
accept: response.requestHeaders.accept,
|
|
243
|
+
};
|
|
244
|
+
let parsedSnapshotContents: IOdspResponse<ISnapshotContents> | undefined;
|
|
245
|
+
try {
|
|
246
|
+
switch (contentType) {
|
|
247
|
+
case "application/json": {
|
|
248
|
+
const text = await odspResponse.content.text();
|
|
249
|
+
odspResponse.propsToLog.bodySize = text.length;
|
|
250
|
+
const content: IOdspSnapshot = JSON.parse(text);
|
|
251
|
+
validateBlobsAndTrees(content);
|
|
252
|
+
const snapshotContents: ISnapshotContents =
|
|
253
|
+
convertOdspSnapshotToSnapshotTreeAndBlobs(content);
|
|
254
|
+
parsedSnapshotContents = { ...odspResponse, content: snapshotContents };
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
case "application/ms-fluid": {
|
|
258
|
+
const content = await odspResponse.content.arrayBuffer();
|
|
259
|
+
odspResponse.propsToLog.bodySize = content.byteLength;
|
|
260
|
+
const snapshotContents: ISnapshotContents = parseCompactSnapshotResponse(
|
|
261
|
+
new ReadBuffer(new Uint8Array(content)));
|
|
262
|
+
if (snapshotContents.snapshotTree.trees === undefined ||
|
|
263
|
+
snapshotContents.snapshotTree.blobs === undefined) {
|
|
264
|
+
throw new NonRetryableError(
|
|
265
|
+
"Returned odsp snapshot is malformed. No trees or blobs!",
|
|
266
|
+
DriverErrorType.incorrectServerResponse,
|
|
267
|
+
{ driverVersion: pkgVersion, ...odspResponse.propsToLog },
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
parsedSnapshotContents = { ...odspResponse, content: snapshotContents };
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
default:
|
|
274
|
+
throw new NonRetryableError(
|
|
275
|
+
"Unknown snapshot content type",
|
|
276
|
+
DriverErrorType.incorrectServerResponse,
|
|
277
|
+
{ driverVersion: pkgVersion, ...odspResponse.propsToLog },
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
} catch (error) {
|
|
281
|
+
if (isFluidError(error)) {
|
|
282
|
+
error.addTelemetryProperties({ driverVersion: pkgVersion, ...odspResponse.propsToLog });
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
const enhancedError = wrapError(
|
|
286
|
+
error,
|
|
287
|
+
(errorMessage) => new NonRetryableError(
|
|
288
|
+
`Error parsing snapshot response: ${errorMessage}`,
|
|
289
|
+
DriverErrorType.genericError,
|
|
290
|
+
{ driverVersion: pkgVersion, ...odspResponse.propsToLog }));
|
|
291
|
+
throw enhancedError;
|
|
292
|
+
}
|
|
293
|
+
assert(parsedSnapshotContents !== undefined, 0x312 /* snapshot should be parsed */);
|
|
294
|
+
const snapshot = parsedSnapshotContents.content;
|
|
236
295
|
// From: https://developer.mozilla.org/en-US/docs/Web/API/PerformanceResourceTiming
|
|
237
296
|
// fetchStart: immediately before the browser starts to fetch the resource.
|
|
238
297
|
// requestStart: immediately before the browser starts requesting the resource from the server
|
|
@@ -254,7 +313,7 @@ async function fetchLatestSnapshotCore(
|
|
|
254
313
|
let fetchStartToResponseEndTime: number | undefined; // responseEnd - fetchStart
|
|
255
314
|
let reqStartToResponseEndTime: number | undefined; // responseEnd - requestStart
|
|
256
315
|
let networkTime: number | undefined; // responseEnd - startTime
|
|
257
|
-
const spReqDuration =
|
|
316
|
+
const spReqDuration = odspResponse.headers.get("sprequestduration");
|
|
258
317
|
|
|
259
318
|
// getEntriesByType is only available in browser performance object
|
|
260
319
|
const resources1 = performance.getEntriesByType?.("resource") ?? [];
|
|
@@ -286,12 +345,12 @@ async function fetchLatestSnapshotCore(
|
|
|
286
345
|
}
|
|
287
346
|
|
|
288
347
|
const { numTrees, numBlobs, encodedBlobsSize } =
|
|
289
|
-
|
|
348
|
+
evalBlobsAndTrees(parsedSnapshotContents.content);
|
|
290
349
|
|
|
291
350
|
// There are some scenarios in ODSP where we cannot cache, trees/latest will explicitly tell us when we
|
|
292
351
|
// cannot cache using an HTTP response header.
|
|
293
352
|
const canCache =
|
|
294
|
-
|
|
353
|
+
odspResponse.headers.get("disablebrowsercachingofusercontent") !== "true";
|
|
295
354
|
const sequenceNumber: number = snapshot.sequenceNumber ?? 0;
|
|
296
355
|
const seqNumberFromOps = snapshot.ops && snapshot.ops.length > 0 ?
|
|
297
356
|
snapshot.ops[0].sequenceNumber - 1 :
|
|
@@ -302,7 +361,7 @@ async function fetchLatestSnapshotCore(
|
|
|
302
361
|
logger.sendErrorEvent({ eventName: "fetchSnapshotError", sequenceNumber, seqNumberFromOps });
|
|
303
362
|
snapshot.sequenceNumber = undefined;
|
|
304
363
|
} else if (canCache) {
|
|
305
|
-
const fluidEpoch =
|
|
364
|
+
const fluidEpoch = odspResponse.headers.get("x-fluid-epoch");
|
|
306
365
|
assert(fluidEpoch !== undefined, 0x1e6 /* "Epoch should be present in response" */);
|
|
307
366
|
const value: ISnapshotCachedEntry = {
|
|
308
367
|
...snapshot,
|
|
@@ -346,8 +405,8 @@ async function fetchLatestSnapshotCore(
|
|
|
346
405
|
// Azure Fluid Relay service; desc=S, FRP; desc=False. Here, FRL is the duration taken for redeem,
|
|
347
406
|
// Azure Fluid Relay service is the redeem status (S means success), and FRP is a flag to indicate
|
|
348
407
|
// if the permission has changed.
|
|
349
|
-
sltelemetry:
|
|
350
|
-
...
|
|
408
|
+
sltelemetry: odspResponse.headers.get("x-fluid-sltelemetry"),
|
|
409
|
+
...odspResponse.propsToLog,
|
|
351
410
|
});
|
|
352
411
|
return snapshot;
|
|
353
412
|
},
|
|
@@ -363,8 +422,8 @@ async function fetchLatestSnapshotCore(
|
|
|
363
422
|
});
|
|
364
423
|
}
|
|
365
424
|
|
|
366
|
-
interface ISnapshotRequestAndResponseOptions {
|
|
367
|
-
|
|
425
|
+
export interface ISnapshotRequestAndResponseOptions {
|
|
426
|
+
odspResponse: IOdspResponse<Response>;
|
|
368
427
|
requestUrl: string;
|
|
369
428
|
requestHeaders: { [index: string]: any; };
|
|
370
429
|
}
|
|
@@ -406,11 +465,7 @@ function getFormBodyAndHeaders(
|
|
|
406
465
|
return { body: postBody, headers: header };
|
|
407
466
|
}
|
|
408
467
|
|
|
409
|
-
function
|
|
410
|
-
assert(snapshot.snapshotTree !== undefined,
|
|
411
|
-
0x200 /* "Returned odsp snapshot is malformed. No trees!" */);
|
|
412
|
-
assert(snapshot.blobs !== undefined,
|
|
413
|
-
0x201 /* "Returned odsp snapshot is malformed. No blobs!" */);
|
|
468
|
+
function evalBlobsAndTrees(snapshot: ISnapshotContents) {
|
|
414
469
|
const numTrees = countTreesInSnapshotTree(snapshot.snapshotTree);
|
|
415
470
|
const numBlobs = snapshot.blobs.size;
|
|
416
471
|
let encodedBlobsSize = 0;
|
|
@@ -420,6 +475,13 @@ function validateAndEvalBlobsAndTrees(snapshot: ISnapshotContents) {
|
|
|
420
475
|
return { numTrees, numBlobs, encodedBlobsSize };
|
|
421
476
|
}
|
|
422
477
|
|
|
478
|
+
export function validateBlobsAndTrees(snapshot: IOdspSnapshot) {
|
|
479
|
+
assert(snapshot.trees !== undefined,
|
|
480
|
+
0x200 /* "Returned odsp snapshot is malformed. No trees!" */);
|
|
481
|
+
assert(snapshot.blobs !== undefined,
|
|
482
|
+
0x201 /* "Returned odsp snapshot is malformed. No blobs!" */);
|
|
483
|
+
}
|
|
484
|
+
|
|
423
485
|
function countTreesInSnapshotTree(snapshotTree: ISnapshotTree): number {
|
|
424
486
|
let numTrees = 0;
|
|
425
487
|
for (const [_, tree] of Object.entries(snapshotTree.trees)) {
|
|
@@ -448,6 +510,7 @@ export async function downloadSnapshot(
|
|
|
448
510
|
snapshotFormatFetchType?: SnapshotFormatSupportType,
|
|
449
511
|
controller?: AbortController,
|
|
450
512
|
epochTracker?: EpochTracker,
|
|
513
|
+
scenarioName?: string,
|
|
451
514
|
): Promise<ISnapshotRequestAndResponseOptions> {
|
|
452
515
|
// back-compat: This block to be removed with #8784 when we only consume/consider odsp resolvers that are >= 0.51
|
|
453
516
|
const sharingLinkToRedeem = (odspResolvedUrl as any).sharingLinkToRedeem;
|
|
@@ -482,28 +545,11 @@ export async function downloadSnapshot(
|
|
|
482
545
|
headers.accept = "application/json";
|
|
483
546
|
}
|
|
484
547
|
|
|
485
|
-
const
|
|
548
|
+
const odspResponse = await (epochTracker?.fetch(url, fetchOptions, "treesLatest", true, scenarioName) ??
|
|
486
549
|
fetchHelper(url, fetchOptions));
|
|
487
550
|
|
|
488
|
-
let finalSnapshotContents: IOdspResponse<ISnapshotContents>;
|
|
489
|
-
const contentType = response.headers.get("content-type");
|
|
490
|
-
if (contentType === "application/json") {
|
|
491
|
-
const text = await response.content.text();
|
|
492
|
-
const content: IOdspSnapshot = JSON.parse(text);
|
|
493
|
-
response.propsToLog.bodySize = text.length;
|
|
494
|
-
const snapshotContents: ISnapshotContents = convertOdspSnapshotToSnapsohtTreeAndBlobs(content);
|
|
495
|
-
finalSnapshotContents = { ...response, content: snapshotContents };
|
|
496
|
-
} else {
|
|
497
|
-
assert(contentType === "application/ms-fluid", 0x2c3 /* "Content type should be application/ms-fluid" */);
|
|
498
|
-
const content = await response.content.arrayBuffer();
|
|
499
|
-
response.propsToLog.bodySize = content.byteLength;
|
|
500
|
-
const snapshotContents: ISnapshotContents = parseCompactSnapshotResponse(
|
|
501
|
-
new ReadBuffer(new Uint8Array(content)));
|
|
502
|
-
finalSnapshotContents = { ...response, content: snapshotContents };
|
|
503
|
-
}
|
|
504
|
-
response.propsToLog.contentType = contentType;
|
|
505
551
|
return {
|
|
506
|
-
|
|
552
|
+
odspResponse,
|
|
507
553
|
requestHeaders: headers,
|
|
508
554
|
requestUrl: url,
|
|
509
555
|
};
|
|
@@ -513,7 +559,7 @@ function isRedeemSharingLinkError(odspResolvedUrl: IOdspResolvedUrl, error: any)
|
|
|
513
559
|
if (odspResolvedUrl.shareLinkInfo?.sharingLinkToRedeem !== undefined
|
|
514
560
|
&& (typeof error === "object" && error !== null)
|
|
515
561
|
&& (error.errorType === DriverErrorType.authorizationError
|
|
516
|
-
|
|
562
|
+
|| error.errorType === DriverErrorType.fileNotFoundOrAccessDeniedError)) {
|
|
517
563
|
return true;
|
|
518
564
|
}
|
|
519
565
|
return false;
|
|
@@ -526,9 +572,9 @@ function getEncodedShareUrl(url: string): string {
|
|
|
526
572
|
*/
|
|
527
573
|
let encodedUrl = fromUtf8ToBase64(encodeURI(url));
|
|
528
574
|
encodedUrl = encodedUrl
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
575
|
+
.replace(/=+$/g, "")
|
|
576
|
+
.replace(/\//g, "_")
|
|
577
|
+
.replace(/\+/g, "-");
|
|
532
578
|
encodedUrl = "u!".concat(encodedUrl);
|
|
533
579
|
return encodedUrl;
|
|
534
580
|
}
|
package/src/index.ts
CHANGED
|
@@ -31,3 +31,7 @@ export * from "./odspDriverUrlResolver";
|
|
|
31
31
|
|
|
32
32
|
// It's used by URL resolve code, but also has some public functions
|
|
33
33
|
export * from "./odspFluidFileLink";
|
|
34
|
+
|
|
35
|
+
// Wire format related
|
|
36
|
+
export * from "./compactSnapshotParser";
|
|
37
|
+
export * from "./ReadBufferUtils";
|
|
@@ -128,7 +128,7 @@ class SocketReference extends TypedEventEmitter<ISocketEvents> {
|
|
|
128
128
|
this.isPendingInitialConnection = false;
|
|
129
129
|
|
|
130
130
|
// Explicitly cast error to the specified event args type to ensure type compatibility
|
|
131
|
-
this.emit("server_disconnect", error
|
|
131
|
+
this.emit("server_disconnect", error);
|
|
132
132
|
this.closeSocket();
|
|
133
133
|
});
|
|
134
134
|
}
|
|
@@ -440,7 +440,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
440
440
|
}
|
|
441
441
|
|
|
442
442
|
protected serverDisconnectHandler = (error: IFluidErrorBase & OdspError) => {
|
|
443
|
-
this.logger.sendTelemetryEvent({ eventName: "ServerDisconnect" }, error);
|
|
443
|
+
this.logger.sendTelemetryEvent({ eventName: "ServerDisconnect", clientId: this.clientId }, error);
|
|
444
444
|
this.disposeCore(true, error);
|
|
445
445
|
};
|
|
446
446
|
|
|
@@ -460,7 +460,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
460
460
|
};
|
|
461
461
|
}
|
|
462
462
|
|
|
463
|
-
this.socketReference!.
|
|
463
|
+
this.socketReference!.on("server_disconnect", this.serverDisconnectHandler);
|
|
464
464
|
|
|
465
465
|
this.socket.on("get_ops_response", (result: IGetOpsResponse) => {
|
|
466
466
|
const messages = result.messages;
|
|
@@ -553,20 +553,20 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
553
553
|
case "nack":
|
|
554
554
|
// per client / document nack handling
|
|
555
555
|
super.addTrackedListener(event, (clientIdOrDocumentId: string, nacks: INack[]) => {
|
|
556
|
-
|
|
556
|
+
const handle = clientIdOrDocumentId.length === 0 ||
|
|
557
557
|
clientIdOrDocumentId === this.documentId ||
|
|
558
|
-
(
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
558
|
+
(clientIdOrDocumentId === this.clientId);
|
|
559
|
+
const { code, type, message, retryAfter } = nacks[0]?.content ?? {};
|
|
560
|
+
this.logger.sendTelemetryEvent({
|
|
561
|
+
eventName: "ServerNack",
|
|
562
|
+
code,
|
|
563
|
+
type,
|
|
564
|
+
message,
|
|
565
|
+
retryAfterSeconds: retryAfter,
|
|
566
|
+
clientId: this.clientId,
|
|
567
|
+
handle,
|
|
568
|
+
});
|
|
569
|
+
if (handle) {
|
|
570
570
|
this.emit("nack", clientIdOrDocumentId, nacks);
|
|
571
571
|
}
|
|
572
572
|
});
|
|
@@ -584,9 +584,9 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
584
584
|
protected disconnect(socketProtocolError: boolean, reason: IAnyDriverError) {
|
|
585
585
|
const socket = this.socketReference;
|
|
586
586
|
assert(socket !== undefined, 0x0a2 /* "reentrancy not supported!" */);
|
|
587
|
-
this.socketReference = undefined;
|
|
588
587
|
|
|
589
|
-
this.
|
|
588
|
+
this.socketReference?.off("server_disconnect", this.serverDisconnectHandler);
|
|
589
|
+
this.socketReference = undefined;
|
|
590
590
|
|
|
591
591
|
if (!socketProtocolError && this.hasDetails) {
|
|
592
592
|
// tell the server we are disconnecting this client from the document
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
IDocumentServicePolicies,
|
|
22
22
|
DriverErrorType,
|
|
23
23
|
} from "@fluidframework/driver-definitions";
|
|
24
|
-
import { DeltaStreamConnectionForbiddenError, NonRetryableError } from "@fluidframework/driver-utils";
|
|
24
|
+
import { canRetryOnError, DeltaStreamConnectionForbiddenError, NonRetryableError } from "@fluidframework/driver-utils";
|
|
25
25
|
import { IFacetCodes } from "@fluidframework/odsp-doclib-utils";
|
|
26
26
|
import {
|
|
27
27
|
IClient,
|
|
@@ -415,12 +415,16 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
415
415
|
if (response.refreshAfterDeltaMs > 0) {
|
|
416
416
|
this.scheduleJoinSessionRefresh(response.refreshAfterDeltaMs)
|
|
417
417
|
.catch((error) => {
|
|
418
|
-
|
|
418
|
+
const canRetry = canRetryOnError(error);
|
|
419
|
+
// Only record error event in case it is non retriable.
|
|
420
|
+
if (!canRetry) {
|
|
421
|
+
this.mc.logger.sendErrorEvent({
|
|
419
422
|
eventName: "JoinSessionRefreshError",
|
|
420
423
|
details: JSON.stringify(props),
|
|
421
424
|
},
|
|
422
425
|
error,
|
|
423
|
-
|
|
426
|
+
);
|
|
427
|
+
}
|
|
424
428
|
});
|
|
425
429
|
} else {
|
|
426
430
|
// Logging just for informational purposes to help with debugging as this is a new feature.
|
|
@@ -102,7 +102,8 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
|
|
|
102
102
|
this.persistedCache,
|
|
103
103
|
this.nonPersistentCache,
|
|
104
104
|
fileEntry,
|
|
105
|
-
odspLogger
|
|
105
|
+
odspLogger,
|
|
106
|
+
clientIsSummarizer);
|
|
106
107
|
|
|
107
108
|
return PerformanceEvent.timedExecAsync(
|
|
108
109
|
odspLogger,
|
|
@@ -189,7 +190,8 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
|
|
|
189
190
|
this.persistedCache,
|
|
190
191
|
this.nonPersistentCache,
|
|
191
192
|
{ resolvedUrl: odspResolvedUrl, docId: odspResolvedUrl.hashedDocumentId },
|
|
192
|
-
odspLogger
|
|
193
|
+
odspLogger,
|
|
194
|
+
clientIsSummarizer);
|
|
193
195
|
|
|
194
196
|
const storageTokenFetcher = toInstrumentedOdspTokenFetcher(
|
|
195
197
|
odspLogger,
|
|
@@ -38,8 +38,8 @@ import { IOdspCache } from "./odspCache";
|
|
|
38
38
|
import {
|
|
39
39
|
createCacheSnapshotKey,
|
|
40
40
|
getWithRetryForTokenRefresh,
|
|
41
|
-
ISnapshotContents,
|
|
42
41
|
} from "./odspUtils";
|
|
42
|
+
import { ISnapshotContents } from "./odspPublicUtils";
|
|
43
43
|
import { defaultCacheExpiryTimeoutMs, EpochTracker } from "./epochTracker";
|
|
44
44
|
import { OdspSummaryUploadManager } from "./odspSummaryUploadManager";
|
|
45
45
|
import { FlushResult } from "./odspDocumentDeltaConnection";
|
|
@@ -275,7 +275,7 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
275
275
|
method: "POST",
|
|
276
276
|
},
|
|
277
277
|
"createBlob",
|
|
278
|
-
|
|
278
|
+
));
|
|
279
279
|
event.end({
|
|
280
280
|
blobId: res.content.id,
|
|
281
281
|
...res.propsToLog,
|
|
@@ -343,14 +343,14 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
343
343
|
return this.readBlobCore(blobId);
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
-
public async getSnapshotTree(version?: api.IVersion): Promise<api.ISnapshotTree | null> {
|
|
346
|
+
public async getSnapshotTree(version?: api.IVersion, scenarioName?: string): Promise<api.ISnapshotTree | null> {
|
|
347
347
|
if (!this.snapshotUrl) {
|
|
348
348
|
return null;
|
|
349
349
|
}
|
|
350
350
|
|
|
351
351
|
let id: string;
|
|
352
352
|
if (!version || !version.id) {
|
|
353
|
-
const versions = await this.getVersions(null, 1);
|
|
353
|
+
const versions = await this.getVersions(null, 1, scenarioName);
|
|
354
354
|
if (!versions || versions.length === 0) {
|
|
355
355
|
return null;
|
|
356
356
|
}
|
|
@@ -359,7 +359,7 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
359
359
|
id = version.id;
|
|
360
360
|
}
|
|
361
361
|
|
|
362
|
-
const snapshotTree = await this.readTree(id);
|
|
362
|
+
const snapshotTree = await this.readTree(id, scenarioName);
|
|
363
363
|
if (!snapshotTree) {
|
|
364
364
|
return null;
|
|
365
365
|
}
|
|
@@ -379,7 +379,7 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
379
379
|
return this.combineProtocolAndAppSnapshotTree(appTree, protocolTree);
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
-
public async getVersions(blobid: string | null, count: number): Promise<api.IVersion[]> {
|
|
382
|
+
public async getVersions(blobid: string | null, count: number, scenarioName?: string): Promise<api.IVersion[]> {
|
|
383
383
|
// Regular load workflow uses blobId === documentID to indicate "latest".
|
|
384
384
|
if (blobid !== this.documentId && blobid) {
|
|
385
385
|
// FluidFetch & FluidDebugger tools use empty sting to query for versions
|
|
@@ -437,14 +437,14 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
437
437
|
}
|
|
438
438
|
|
|
439
439
|
return snapshotCachedEntry;
|
|
440
|
-
|
|
440
|
+
});
|
|
441
441
|
|
|
442
442
|
// Based on the concurrentSnapshotFetch policy:
|
|
443
443
|
// Either retrieve both the network and cache snapshots concurrently and pick the first to return,
|
|
444
444
|
// or grab the cache value and then the network value if the cache value returns undefined.
|
|
445
445
|
let method: string;
|
|
446
446
|
if (this.hostPolicy.concurrentSnapshotFetch && !this.hostPolicy.summarizerClient) {
|
|
447
|
-
const networkSnapshotP = this.fetchSnapshot(hostSnapshotOptions);
|
|
447
|
+
const networkSnapshotP = this.fetchSnapshot(hostSnapshotOptions, scenarioName);
|
|
448
448
|
|
|
449
449
|
// Ensure that failures on both paths are ignored initially.
|
|
450
450
|
// I.e. if cache fails for some reason, we will proceed with network result.
|
|
@@ -478,7 +478,7 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
478
478
|
method = retrievedSnapshot !== undefined ? "cache" : "network";
|
|
479
479
|
|
|
480
480
|
if (retrievedSnapshot === undefined) {
|
|
481
|
-
retrievedSnapshot = await this.fetchSnapshot(hostSnapshotOptions);
|
|
481
|
+
retrievedSnapshot = await this.fetchSnapshot(hostSnapshotOptions, scenarioName);
|
|
482
482
|
}
|
|
483
483
|
}
|
|
484
484
|
if (method === "network") {
|
|
@@ -524,7 +524,7 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
524
524
|
eventName: "getVersions",
|
|
525
525
|
headers: Object.keys(headers).length !== 0 ? true : undefined,
|
|
526
526
|
},
|
|
527
|
-
async () => this.epochTracker.fetchAndParseAsJSON<IDocumentStorageGetVersionsResponse>(url, { headers }, "versions"),
|
|
527
|
+
async () => this.epochTracker.fetchAndParseAsJSON<IDocumentStorageGetVersionsResponse>(url, { headers }, "versions", undefined, scenarioName),
|
|
528
528
|
);
|
|
529
529
|
const versionsResponse = response.content;
|
|
530
530
|
if (!versionsResponse) {
|
|
@@ -548,8 +548,8 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
548
548
|
});
|
|
549
549
|
}
|
|
550
550
|
|
|
551
|
-
private async fetchSnapshot(hostSnapshotOptions: ISnapshotOptions | undefined) {
|
|
552
|
-
return this.fetchSnapshotCore(hostSnapshotOptions).catch((error) => {
|
|
551
|
+
private async fetchSnapshot(hostSnapshotOptions: ISnapshotOptions | undefined, scenarioName?: string) {
|
|
552
|
+
return this.fetchSnapshotCore(hostSnapshotOptions, scenarioName).catch((error) => {
|
|
553
553
|
// Issue #5895:
|
|
554
554
|
// If we are offline, this error is retryable. But that means that RetriableDocumentStorageService
|
|
555
555
|
// will run in circles calling getSnapshotTree, which would result in OdspDocumentStorageService class
|
|
@@ -562,7 +562,7 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
562
562
|
});
|
|
563
563
|
}
|
|
564
564
|
|
|
565
|
-
private async fetchSnapshotCore(hostSnapshotOptions: ISnapshotOptions | undefined) {
|
|
565
|
+
private async fetchSnapshotCore(hostSnapshotOptions: ISnapshotOptions | undefined, scenarioName?: string) {
|
|
566
566
|
const snapshotOptions: ISnapshotOptions = {
|
|
567
567
|
mds: this.maxSnapshotSizeLimit,
|
|
568
568
|
...hostSnapshotOptions,
|
|
@@ -589,6 +589,7 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
589
589
|
this.snapshotFormatFetchType,
|
|
590
590
|
controller,
|
|
591
591
|
this.epochTracker,
|
|
592
|
+
scenarioName,
|
|
592
593
|
);
|
|
593
594
|
};
|
|
594
595
|
const putInCache = async (valueWithEpoch: IVersionedValueWithEpoch) => {
|
|
@@ -642,19 +643,13 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
642
643
|
}
|
|
643
644
|
}
|
|
644
645
|
|
|
645
|
-
public async write(tree: api.ITree, parents: string[], message: string): Promise<api.IVersion> {
|
|
646
|
-
this.checkSnapshotUrl();
|
|
647
|
-
|
|
648
|
-
throw new Error("Not supported");
|
|
649
|
-
}
|
|
650
|
-
|
|
651
646
|
public async uploadSummaryWithContext(summary: api.ISummaryTree, context: ISummaryContext): Promise<string> {
|
|
652
647
|
this.checkSnapshotUrl();
|
|
653
648
|
|
|
654
649
|
// Enable flushing only if we have single commit summary and this is not the initial summary for an empty file
|
|
655
650
|
if (".protocol" in summary.tree && context.ackHandle !== undefined) {
|
|
656
651
|
let retry = 0;
|
|
657
|
-
for (
|
|
652
|
+
for (; ;) {
|
|
658
653
|
const result = await this.flushCallback();
|
|
659
654
|
const seq = result.lastPersistedSequenceNumber;
|
|
660
655
|
if (seq !== undefined && seq >= context.referenceSequenceNumber) {
|
|
@@ -726,7 +721,7 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
726
721
|
}
|
|
727
722
|
}
|
|
728
723
|
|
|
729
|
-
private async readTree(id: string): Promise<api.ISnapshotTree | null> {
|
|
724
|
+
private async readTree(id: string, scenarioName?: string): Promise<api.ISnapshotTree | null> {
|
|
730
725
|
if (!this.snapshotUrl) {
|
|
731
726
|
return null;
|
|
732
727
|
}
|
|
@@ -739,6 +734,8 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
739
734
|
url,
|
|
740
735
|
fetchOptions,
|
|
741
736
|
"snapshotTree",
|
|
737
|
+
undefined,
|
|
738
|
+
scenarioName,
|
|
742
739
|
);
|
|
743
740
|
};
|
|
744
741
|
const snapshot = await fetchSnapshot(
|