@fluidframework/odsp-driver 0.54.3 → 0.55.2
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 -2
- package/dist/epochTracker.d.ts.map +1 -1
- package/dist/epochTracker.js +3 -2
- package/dist/epochTracker.js.map +1 -1
- package/dist/fetchSnapshot.d.ts.map +1 -1
- package/dist/fetchSnapshot.js +36 -34
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/odspDocumentDeltaConnection.js +1 -1
- package/dist/odspDocumentDeltaConnection.js.map +1 -1
- package/dist/odspDocumentService.d.ts.map +1 -1
- package/dist/odspDocumentService.js +9 -0
- package/dist/odspDocumentService.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/vroom.d.ts.map +1 -1
- package/dist/vroom.js +19 -20
- package/dist/vroom.js.map +1 -1
- package/lib/epochTracker.d.ts.map +1 -1
- package/lib/epochTracker.js +3 -2
- package/lib/epochTracker.js.map +1 -1
- package/lib/fetchSnapshot.d.ts.map +1 -1
- package/lib/fetchSnapshot.js +36 -34
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/odspDocumentDeltaConnection.js +1 -1
- package/lib/odspDocumentDeltaConnection.js.map +1 -1
- package/lib/odspDocumentService.d.ts.map +1 -1
- package/lib/odspDocumentService.js +9 -0
- package/lib/odspDocumentService.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/vroom.d.ts.map +1 -1
- package/lib/vroom.js +19 -20
- package/lib/vroom.js.map +1 -1
- package/package.json +17 -16
- package/src/epochTracker.ts +3 -2
- package/src/fetchSnapshot.ts +44 -34
- package/src/odspDocumentDeltaConnection.ts +1 -1
- package/src/odspDocumentService.ts +9 -0
- package/src/packageVersion.ts +1 -1
- package/src/vroom.ts +23 -23
package/src/fetchSnapshot.ts
CHANGED
|
@@ -95,6 +95,7 @@ export async function fetchSnapshotWithRedeem(
|
|
|
95
95
|
logger,
|
|
96
96
|
snapshotDownloader,
|
|
97
97
|
putInCache,
|
|
98
|
+
enableRedeemFallback,
|
|
98
99
|
).catch(async (error) => {
|
|
99
100
|
if (enableRedeemFallback && isRedeemSharingLinkError(odspResolvedUrl, error)) {
|
|
100
101
|
// Execute the redeem fallback
|
|
@@ -171,6 +172,7 @@ async function fetchLatestSnapshotCore(
|
|
|
171
172
|
controller?: AbortController,
|
|
172
173
|
) => Promise<ISnapshotRequestAndResponseOptions>,
|
|
173
174
|
putInCache: (valueWithEpoch: IVersionedValueWithEpoch) => Promise<void>,
|
|
175
|
+
enableRedeemFallback?: boolean,
|
|
174
176
|
): Promise<ISnapshotContents> {
|
|
175
177
|
return getWithRetryForTokenRefresh(async (tokenFetchOptions) => {
|
|
176
178
|
const storageToken = await storageTokenFetcher(tokenFetchOptions, "TreesLatest", true);
|
|
@@ -187,6 +189,8 @@ async function fetchLatestSnapshotCore(
|
|
|
187
189
|
const perfEvent = {
|
|
188
190
|
eventName: "TreesLatest",
|
|
189
191
|
attempts: tokenFetchOptions.refresh ? 2 : 1,
|
|
192
|
+
shareLinkPresent: odspResolvedUrl.sharingLinkToRedeem !== undefined,
|
|
193
|
+
redeemFallbackEnabled: enableRedeemFallback,
|
|
190
194
|
};
|
|
191
195
|
if (snapshotOptions !== undefined) {
|
|
192
196
|
Object.entries(snapshotOptions).forEach(([key, value]) => {
|
|
@@ -344,30 +348,9 @@ async function fetchSnapshotContentsCoreV1(
|
|
|
344
348
|
): Promise<ISnapshotRequestAndResponseOptions> {
|
|
345
349
|
const snapshotUrl = odspResolvedUrl.endpoints.snapshotStorageUrl;
|
|
346
350
|
const url = `${snapshotUrl}/trees/latest?ump=1`;
|
|
347
|
-
const
|
|
348
|
-
const formParams: string[] = [];
|
|
349
|
-
formParams.push(`--${formBoundary}`);
|
|
350
|
-
formParams.push(`Authorization: Bearer ${storageToken}`);
|
|
351
|
-
formParams.push(`X-HTTP-Method-Override: GET`);
|
|
352
|
-
if (snapshotOptions !== undefined) {
|
|
353
|
-
Object.entries(snapshotOptions).forEach(([key, value]) => {
|
|
354
|
-
if (value !== undefined) {
|
|
355
|
-
formParams.push(`${key}: ${value}`);
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
}
|
|
359
|
-
if (odspResolvedUrl.sharingLinkToRedeem) {
|
|
360
|
-
formParams.push(`sl: ${odspResolvedUrl.sharingLinkToRedeem}`);
|
|
361
|
-
}
|
|
362
|
-
formParams.push(`_post: 1`);
|
|
363
|
-
formParams.push(`\r\n--${formBoundary}--`);
|
|
364
|
-
const postBody = formParams.join("\r\n");
|
|
365
|
-
const headers: {[index: string]: any} = {
|
|
366
|
-
"Content-Type": `multipart/form-data;boundary=${formBoundary}`,
|
|
367
|
-
};
|
|
368
|
-
|
|
351
|
+
const { body, headers } = getFormBodyAndHeaders(odspResolvedUrl, storageToken, snapshotOptions);
|
|
369
352
|
const fetchOptions = {
|
|
370
|
-
body
|
|
353
|
+
body,
|
|
371
354
|
headers,
|
|
372
355
|
signal: controller?.signal,
|
|
373
356
|
method: "POST",
|
|
@@ -401,28 +384,55 @@ async function fetchSnapshotContentsCoreV2(
|
|
|
401
384
|
epochTracker?: EpochTracker,
|
|
402
385
|
): Promise<ISnapshotRequestAndResponseOptions> {
|
|
403
386
|
const fullUrl = `${odspResolvedUrl.siteUrl}/_api/v2.1/drives/${odspResolvedUrl.driveId}/items/${
|
|
404
|
-
odspResolvedUrl.itemId}/opStream/attachments/latest/content`;
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
408
|
-
queryParams["sl"] = odspResolvedUrl.sharingLinkToRedeem;
|
|
409
|
-
}
|
|
410
|
-
const queryString = getQueryString(queryParams);
|
|
411
|
-
const { url, headers } = getUrlAndHeadersWithAuth(`${fullUrl}${queryString}`, storageToken);
|
|
387
|
+
odspResolvedUrl.itemId}/opStream/attachments/latest/content?ump=1`;
|
|
388
|
+
|
|
389
|
+
const { body, headers } = getFormBodyAndHeaders(odspResolvedUrl, storageToken, snapshotOptions);
|
|
412
390
|
const fetchOptions = {
|
|
391
|
+
body,
|
|
413
392
|
headers,
|
|
414
393
|
signal: controller?.signal,
|
|
394
|
+
method: "POST",
|
|
415
395
|
};
|
|
416
|
-
|
|
417
|
-
|
|
396
|
+
|
|
397
|
+
const response = await (epochTracker?.fetchArray(fullUrl, fetchOptions, "treesLatest", true) ??
|
|
398
|
+
fetchArray(fullUrl, fetchOptions));
|
|
418
399
|
const snapshotContents: ISnapshotContents = parseCompactSnapshotResponse(
|
|
419
400
|
new ReadBuffer(new Uint8Array(response.content)));
|
|
420
401
|
const finalSnapshotContents: IOdspResponse<ISnapshotContents> = { ...response, content: snapshotContents };
|
|
421
402
|
return {
|
|
422
403
|
odspSnapshotResponse: finalSnapshotContents,
|
|
423
404
|
requestHeaders: headers,
|
|
424
|
-
requestUrl:
|
|
405
|
+
requestUrl: fullUrl,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function getFormBodyAndHeaders(
|
|
410
|
+
odspResolvedUrl: IOdspResolvedUrl,
|
|
411
|
+
storageToken: string,
|
|
412
|
+
snapshotOptions: ISnapshotOptions | undefined,
|
|
413
|
+
) {
|
|
414
|
+
const formBoundary = uuid();
|
|
415
|
+
const formParams: string[] = [];
|
|
416
|
+
formParams.push(`--${formBoundary}`);
|
|
417
|
+
formParams.push(`Authorization: Bearer ${storageToken}`);
|
|
418
|
+
formParams.push(`X-HTTP-Method-Override: GET`);
|
|
419
|
+
if (snapshotOptions !== undefined) {
|
|
420
|
+
Object.entries(snapshotOptions).forEach(([key, value]) => {
|
|
421
|
+
if (value !== undefined) {
|
|
422
|
+
formParams.push(`${key}: ${value}`);
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
if (odspResolvedUrl.sharingLinkToRedeem) {
|
|
427
|
+
formParams.push(`sl: ${odspResolvedUrl.sharingLinkToRedeem}`);
|
|
428
|
+
}
|
|
429
|
+
formParams.push(`_post: 1`);
|
|
430
|
+
formParams.push(`\r\n--${formBoundary}--`);
|
|
431
|
+
const postBody = formParams.join("\r\n");
|
|
432
|
+
const headers: {[index: string]: any} = {
|
|
433
|
+
"Content-Type": `multipart/form-data;boundary=${formBoundary}`,
|
|
425
434
|
};
|
|
435
|
+
return { body: postBody, headers };
|
|
426
436
|
}
|
|
427
437
|
|
|
428
438
|
function validateAndEvalBlobsAndTrees(snapshot: ISnapshotContents) {
|
|
@@ -234,7 +234,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
234
234
|
|
|
235
235
|
// Reference to this client supporting get_ops flow.
|
|
236
236
|
connectMessage.supportedFeatures = { };
|
|
237
|
-
if (mc.config.getBoolean("Fluid.Driver.Odsp.GetOpsEnabled")
|
|
237
|
+
if (mc.config.getBoolean("Fluid.Driver.Odsp.GetOpsEnabled") !== false) {
|
|
238
238
|
connectMessage.supportedFeatures[feature_get_ops] = true;
|
|
239
239
|
}
|
|
240
240
|
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
IResolvedUrl,
|
|
18
18
|
IDocumentStorageService,
|
|
19
19
|
IDocumentServicePolicies,
|
|
20
|
+
DriverErrorType,
|
|
20
21
|
} from "@fluidframework/driver-definitions";
|
|
21
22
|
import { DeltaStreamConnectionForbiddenError, NonRetryableError } from "@fluidframework/driver-utils";
|
|
22
23
|
import { IFacetCodes } from "@fluidframework/odsp-doclib-utils";
|
|
@@ -281,6 +282,14 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
281
282
|
connection.on("op", (documentId, ops: ISequencedDocumentMessage[]) => {
|
|
282
283
|
this.opsReceived(ops);
|
|
283
284
|
});
|
|
285
|
+
// On disconnect with 401/403 error code, we can just clear the joinSession cache as we will again
|
|
286
|
+
// get the auth error on reconnecting and face latency.
|
|
287
|
+
connection.on("disconnect", (error: any) => {
|
|
288
|
+
if (typeof error === "object" && error !== null
|
|
289
|
+
&& error.errorType === DriverErrorType.authorizationError) {
|
|
290
|
+
this.cache.sessionJoinCache.remove(this.joinSessionKey);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
284
293
|
this.currentConnection = connection;
|
|
285
294
|
return connection;
|
|
286
295
|
} catch (error) {
|
package/src/packageVersion.ts
CHANGED
package/src/vroom.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { v4 as uuid } from "uuid";
|
|
6
7
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
8
|
import { PerformanceEvent } from "@fluidframework/telemetry-utils";
|
|
8
9
|
import { InstrumentedStorageTokenFetcher, IOdspUrlParts } from "@fluidframework/odsp-driver-definitions";
|
|
@@ -13,8 +14,8 @@ import { EpochTracker } from "./epochTracker";
|
|
|
13
14
|
import { runWithRetry } from "./retryUtils";
|
|
14
15
|
|
|
15
16
|
interface IJoinSessionBody {
|
|
16
|
-
requestSocketToken
|
|
17
|
-
guestDisplayName
|
|
17
|
+
requestSocketToken: boolean;
|
|
18
|
+
guestDisplayName: string;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -54,35 +55,34 @@ export async function fetchJoinSession(
|
|
|
54
55
|
...extraProps,
|
|
55
56
|
},
|
|
56
57
|
async (event) => {
|
|
57
|
-
// TODO Extract the auth header-vs-query logic out
|
|
58
58
|
const siteOrigin = getOrigin(urlParts.siteUrl);
|
|
59
|
-
|
|
60
|
-
let
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (requestSocketToken
|
|
67
|
-
body = {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
body.guestDisplayName = guestDisplayName;
|
|
73
|
-
}
|
|
74
|
-
// IMPORTANT: Must set content-type header explicitly to application/json when request has body.
|
|
75
|
-
// By default, request will use text/plain as content-type and will be rejected by backend.
|
|
76
|
-
headers["Content-Type"] = "application/json";
|
|
59
|
+
const formBoundary = uuid();
|
|
60
|
+
let postBody = `--${formBoundary}\r\n`;
|
|
61
|
+
postBody += `Authorization: Bearer ${token}\r\n`;
|
|
62
|
+
postBody += `X-HTTP-Method-Override: POST\r\n`;
|
|
63
|
+
postBody += `Content-Type: application/json\r\n`;
|
|
64
|
+
postBody += `_post: 1\r\n`;
|
|
65
|
+
// Name should be there when socket token is requested and vice-versa.
|
|
66
|
+
if (requestSocketToken && guestDisplayName !== undefined) {
|
|
67
|
+
const body: IJoinSessionBody = {
|
|
68
|
+
requestSocketToken: true,
|
|
69
|
+
guestDisplayName,
|
|
70
|
+
};
|
|
71
|
+
postBody += `\r\n${JSON.stringify(body)}\r\n`;
|
|
77
72
|
}
|
|
73
|
+
postBody += `\r\n--${formBoundary}--`;
|
|
74
|
+
const headers: {[index: string]: string} = {
|
|
75
|
+
"Content-Type": `multipart/form-data;boundary=${formBoundary}`,
|
|
76
|
+
};
|
|
78
77
|
|
|
79
78
|
const response = await runWithRetry(
|
|
80
79
|
async () => epochTracker.fetchAndParseAsJSON<ISocketStorageDiscovery>(
|
|
81
80
|
`${getApiRoot(siteOrigin)}/drives/${
|
|
82
81
|
urlParts.driveId
|
|
83
|
-
}/items/${urlParts.itemId}/${path}
|
|
84
|
-
{ method, headers, body:
|
|
82
|
+
}/items/${urlParts.itemId}/${path}?ump=1`,
|
|
83
|
+
{ method, headers, body: postBody },
|
|
85
84
|
"joinSession",
|
|
85
|
+
true,
|
|
86
86
|
),
|
|
87
87
|
"joinSession",
|
|
88
88
|
logger,
|