@fluidframework/odsp-driver 3.0.2 → 3.1.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 +10 -0
- package/README.md +30 -34
- package/api-report/odsp-driver.legacy.alpha.api.md +5 -1
- package/api-report/odsp-driver.legacy.beta.api.md +5 -1
- package/api-report/odsp-driver.point-in-time.legacy.beta.api.md +2 -1
- package/dist/epochTracker.d.ts +4 -3
- package/dist/epochTracker.d.ts.map +1 -1
- package/dist/epochTracker.js +9 -5
- package/dist/epochTracker.js.map +1 -1
- package/dist/fetchSnapshot.d.ts +2 -2
- package/dist/fetchSnapshot.d.ts.map +1 -1
- package/dist/fetchSnapshot.js +12 -7
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/getFileLink.d.ts +1 -1
- package/dist/getFileLink.d.ts.map +1 -1
- package/dist/getFileLink.js +16 -11
- package/dist/getFileLink.js.map +1 -1
- package/dist/odspDelayLoadedDeltaStream.d.ts.map +1 -1
- package/dist/odspDelayLoadedDeltaStream.js +1 -1
- package/dist/odspDelayLoadedDeltaStream.js.map +1 -1
- package/dist/odspDocumentDeltaConnection.d.ts +1 -1
- package/dist/odspDocumentDeltaConnection.d.ts.map +1 -1
- package/dist/odspDocumentDeltaConnection.js +6 -3
- package/dist/odspDocumentDeltaConnection.js.map +1 -1
- package/dist/odspDocumentServiceFactory.d.ts.map +1 -1
- package/dist/odspDocumentServiceFactory.js +1 -0
- package/dist/odspDocumentServiceFactory.js.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.d.ts +4 -1
- package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.js +11 -5
- package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
- package/dist/odspDocumentStorageManager.d.ts.map +1 -1
- package/dist/odspDocumentStorageManager.js +11 -11
- package/dist/odspDocumentStorageManager.js.map +1 -1
- package/dist/odspDriverUrlResolverForShareLink.d.ts +5 -0
- package/dist/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
- package/dist/odspDriverUrlResolverForShareLink.js +3 -1
- package/dist/odspDriverUrlResolverForShareLink.js.map +1 -1
- package/dist/odspVersionManager/odspFileVersionFetcher.d.ts +1 -0
- package/dist/odspVersionManager/odspFileVersionFetcher.d.ts.map +1 -1
- package/dist/odspVersionManager/odspFileVersionFetcher.js +6 -2
- package/dist/odspVersionManager/odspFileVersionFetcher.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/pointInTimeDriver/createPointInTimeDocumentService.d.ts +1 -1
- package/dist/pointInTimeDriver/createPointInTimeDocumentService.d.ts.map +1 -1
- package/dist/pointInTimeDriver/createPointInTimeDocumentService.js +5 -4
- package/dist/pointInTimeDriver/createPointInTimeDocumentService.js.map +1 -1
- package/dist/pointInTimeDriver/odspPointInTimeDocumentService.d.ts +1 -1
- package/dist/pointInTimeDriver/odspPointInTimeDocumentService.d.ts.map +1 -1
- package/dist/pointInTimeDriver/odspPointInTimeDocumentService.js +21 -1
- package/dist/pointInTimeDriver/odspPointInTimeDocumentService.js.map +1 -1
- package/dist/prefetchLatestSnapshot.d.ts.map +1 -1
- package/dist/prefetchLatestSnapshot.js +2 -2
- package/dist/prefetchLatestSnapshot.js.map +1 -1
- package/dist/requestHeaders.d.ts +19 -0
- package/dist/requestHeaders.d.ts.map +1 -0
- package/dist/requestHeaders.js +48 -0
- package/dist/requestHeaders.js.map +1 -0
- package/lib/epochTracker.d.ts +4 -3
- package/lib/epochTracker.d.ts.map +1 -1
- package/lib/epochTracker.js +9 -5
- package/lib/epochTracker.js.map +1 -1
- package/lib/fetchSnapshot.d.ts +2 -2
- package/lib/fetchSnapshot.d.ts.map +1 -1
- package/lib/fetchSnapshot.js +12 -7
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/getFileLink.d.ts +1 -1
- package/lib/getFileLink.d.ts.map +1 -1
- package/lib/getFileLink.js +16 -11
- package/lib/getFileLink.js.map +1 -1
- package/lib/odspDelayLoadedDeltaStream.d.ts.map +1 -1
- package/lib/odspDelayLoadedDeltaStream.js +1 -1
- package/lib/odspDelayLoadedDeltaStream.js.map +1 -1
- package/lib/odspDocumentDeltaConnection.d.ts +1 -1
- package/lib/odspDocumentDeltaConnection.d.ts.map +1 -1
- package/lib/odspDocumentDeltaConnection.js +6 -3
- package/lib/odspDocumentDeltaConnection.js.map +1 -1
- package/lib/odspDocumentServiceFactory.d.ts.map +1 -1
- package/lib/odspDocumentServiceFactory.js +1 -0
- package/lib/odspDocumentServiceFactory.js.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.d.ts +4 -1
- package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.js +11 -5
- package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
- package/lib/odspDocumentStorageManager.d.ts.map +1 -1
- package/lib/odspDocumentStorageManager.js +11 -11
- package/lib/odspDocumentStorageManager.js.map +1 -1
- package/lib/odspDriverUrlResolverForShareLink.d.ts +5 -0
- package/lib/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
- package/lib/odspDriverUrlResolverForShareLink.js +3 -1
- package/lib/odspDriverUrlResolverForShareLink.js.map +1 -1
- package/lib/odspVersionManager/odspFileVersionFetcher.d.ts +1 -0
- package/lib/odspVersionManager/odspFileVersionFetcher.d.ts.map +1 -1
- package/lib/odspVersionManager/odspFileVersionFetcher.js +6 -2
- package/lib/odspVersionManager/odspFileVersionFetcher.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/pointInTimeDriver/createPointInTimeDocumentService.d.ts +1 -1
- package/lib/pointInTimeDriver/createPointInTimeDocumentService.d.ts.map +1 -1
- package/lib/pointInTimeDriver/createPointInTimeDocumentService.js +5 -4
- package/lib/pointInTimeDriver/createPointInTimeDocumentService.js.map +1 -1
- package/lib/pointInTimeDriver/odspPointInTimeDocumentService.d.ts +1 -1
- package/lib/pointInTimeDriver/odspPointInTimeDocumentService.d.ts.map +1 -1
- package/lib/pointInTimeDriver/odspPointInTimeDocumentService.js +21 -1
- package/lib/pointInTimeDriver/odspPointInTimeDocumentService.js.map +1 -1
- package/lib/prefetchLatestSnapshot.d.ts.map +1 -1
- package/lib/prefetchLatestSnapshot.js +2 -2
- package/lib/prefetchLatestSnapshot.js.map +1 -1
- package/lib/requestHeaders.d.ts +19 -0
- package/lib/requestHeaders.d.ts.map +1 -0
- package/lib/requestHeaders.js +44 -0
- package/lib/requestHeaders.js.map +1 -0
- package/package.json +11 -11
- package/src/epochTracker.ts +9 -1
- package/src/fetchSnapshot.ts +17 -4
- package/src/getFileLink.ts +21 -7
- package/src/odspDelayLoadedDeltaStream.ts +1 -0
- package/src/odspDocumentDeltaConnection.ts +6 -0
- package/src/odspDocumentServiceFactory.ts +1 -0
- package/src/odspDocumentServiceFactoryCore.ts +14 -2
- package/src/odspDocumentStorageManager.ts +13 -3
- package/src/odspDriverUrlResolverForShareLink.ts +8 -0
- package/src/odspVersionManager/DEV.md +5 -1
- package/src/odspVersionManager/odspFileVersionFetcher.ts +7 -2
- package/src/packageVersion.ts +1 -1
- package/src/pointInTimeDriver/createPointInTimeDocumentService.ts +5 -1
- package/src/pointInTimeDriver/odspPointInTimeDocumentService.ts +40 -10
- package/src/prefetchLatestSnapshot.ts +4 -0
- package/src/requestHeaders.ts +56 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"requestHeaders.js","sourceRoot":"","sources":["../src/requestHeaders.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC;IACtC,eAAe;IACf,cAAc;IACd,eAAe;IACf,wBAAwB;IACxB,gBAAgB;CAChB,CAAC,CAAC;AAEH;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CACjC,cAA4D;IAE5D,IAAI,cAAc,KAAK,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9E,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CACvC,MAAM,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC,MAAM,CACpC,CAAC,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAC3D,CACD,CAAC;IACF,OAAO,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;AAC3F,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAClC,WAAyD,EACzD,cAAuC;IAEvC,MAAM,eAAe,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;IACxD,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;QACnC,OAAO,cAAc,CAAC;IACvB,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,OAAO,CAAC,eAAe,CAAC,CAAC;IAC5C,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QAClC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,OAAO,CAAC,cAAc,CAAC,EAAE,CAAC;YACxD,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QACxB,CAAC;IACF,CAAC;IACD,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;AACnC,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nconst driverOwnedHeaderNames = new Set([\n\t\"authorization\",\n\t\"content-type\",\n\t\"x-fluid-epoch\",\n\t\"x-http-method-override\",\n\t\"x-requeststats\",\n]);\n\n/**\n * Creates an immutable snapshot of host-provided per-session headers after removing names owned by\n * the ODSP driver. The factory, URL resolver, epoch tracker, and Socket.IO connection use the\n * returned value so later host mutations cannot change an active document session or override\n * authentication and protocol headers.\n */\nexport function copyRequestHeaders(\n\trequestHeaders: Readonly<Record<string, string>> | undefined,\n): Readonly<Record<string, string>> | undefined {\n\tif (requestHeaders === undefined || Object.keys(requestHeaders).length === 0) {\n\t\treturn undefined;\n\t}\n\tconst copiedHeaders = Object.fromEntries(\n\t\tObject.entries(requestHeaders).filter(\n\t\t\t([name]) => !driverOwnedHeaderNames.has(name.toLowerCase()),\n\t\t),\n\t);\n\treturn Object.keys(copiedHeaders).length === 0 ? undefined : Object.freeze(copiedHeaders);\n}\n\n/**\n * Adds sanitized host headers to one ODSP request. Request-specific headers are applied last, so\n * driver-generated values win case-insensitive collisions. ODSP fetch paths use this helper for\n * snapshot, delta, blob, share-link, file-link, and version requests; Socket.IO applies the copied\n * headers separately as Node.js `extraHeaders`.\n */\nexport function mergeRequestHeaders(\n\thostHeaders: Readonly<Record<string, string>> | undefined,\n\trequestHeaders: HeadersInit | undefined,\n): HeadersInit | undefined {\n\tconst safeHostHeaders = copyRequestHeaders(hostHeaders);\n\tif (safeHostHeaders === undefined) {\n\t\treturn requestHeaders;\n\t}\n\n\tconst merged = new Headers(safeHostHeaders);\n\tif (requestHeaders !== undefined) {\n\t\tfor (const [key, value] of new Headers(requestHeaders)) {\n\t\t\tmerged.set(key, value);\n\t\t}\n\t}\n\treturn Object.fromEntries(merged);\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/odsp-driver",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Socket storage implementation for SPO and ODC",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -66,22 +66,22 @@
|
|
|
66
66
|
"temp-directory": "nyc/.nyc_output"
|
|
67
67
|
},
|
|
68
68
|
"dependencies": {
|
|
69
|
-
"@fluid-internal/client-utils": "~3.0
|
|
70
|
-
"@fluidframework/core-interfaces": "~3.0
|
|
71
|
-
"@fluidframework/core-utils": "~3.0
|
|
72
|
-
"@fluidframework/driver-base": "~3.0
|
|
73
|
-
"@fluidframework/driver-definitions": "~3.0
|
|
74
|
-
"@fluidframework/driver-utils": "~3.0
|
|
75
|
-
"@fluidframework/odsp-doclib-utils": "~3.0
|
|
76
|
-
"@fluidframework/odsp-driver-definitions": "~3.0
|
|
77
|
-
"@fluidframework/telemetry-utils": "~3.0
|
|
69
|
+
"@fluid-internal/client-utils": "~3.1.0",
|
|
70
|
+
"@fluidframework/core-interfaces": "~3.1.0",
|
|
71
|
+
"@fluidframework/core-utils": "~3.1.0",
|
|
72
|
+
"@fluidframework/driver-base": "~3.1.0",
|
|
73
|
+
"@fluidframework/driver-definitions": "~3.1.0",
|
|
74
|
+
"@fluidframework/driver-utils": "~3.1.0",
|
|
75
|
+
"@fluidframework/odsp-doclib-utils": "~3.1.0",
|
|
76
|
+
"@fluidframework/odsp-driver-definitions": "~3.1.0",
|
|
77
|
+
"@fluidframework/telemetry-utils": "~3.1.0",
|
|
78
78
|
"socket.io-client": "^4.8.3",
|
|
79
79
|
"uuid": "^11.1.0"
|
|
80
80
|
},
|
|
81
81
|
"devDependencies": {
|
|
82
82
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
83
83
|
"@biomejs/biome": "~2.4.5",
|
|
84
|
-
"@fluid-internal/mocha-test-setup": "~3.0
|
|
84
|
+
"@fluid-internal/mocha-test-setup": "~3.1.0",
|
|
85
85
|
"@fluid-tools/build-cli": "^0.67.0",
|
|
86
86
|
"@fluidframework/build-common": "^2.0.3",
|
|
87
87
|
"@fluidframework/build-tools": "^0.67.0",
|
package/src/epochTracker.ts
CHANGED
|
@@ -51,6 +51,7 @@ import {
|
|
|
51
51
|
getOdspResolvedUrl,
|
|
52
52
|
} from "./odspUtils.js";
|
|
53
53
|
import { pkgVersion as driverVersion } from "./packageVersion.js";
|
|
54
|
+
import { copyRequestHeaders, mergeRequestHeaders } from "./requestHeaders.js";
|
|
54
55
|
|
|
55
56
|
/**
|
|
56
57
|
* @legacy
|
|
@@ -100,6 +101,7 @@ export const Odsp409Error = "Odsp409Error";
|
|
|
100
101
|
*/
|
|
101
102
|
export class EpochTracker implements IPersistedFileCache {
|
|
102
103
|
private _fluidEpoch: string | undefined;
|
|
104
|
+
private readonly requestHeaders: Readonly<Record<string, string>> | undefined;
|
|
103
105
|
|
|
104
106
|
private readonly snapshotCacheExpiryTimeoutMs: number;
|
|
105
107
|
public readonly rateLimiter: RateLimiter;
|
|
@@ -112,8 +114,10 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
112
114
|
protected readonly fileEntry: IFileEntry,
|
|
113
115
|
protected readonly logger: ITelemetryLoggerExt,
|
|
114
116
|
protected readonly clientIsSummarizer?: boolean,
|
|
117
|
+
requestHeaders?: Readonly<Record<string, string>>,
|
|
115
118
|
) {
|
|
116
119
|
this.loggerInternal = extractTelemetryLoggerExt(logger);
|
|
120
|
+
this.requestHeaders = copyRequestHeaders(requestHeaders);
|
|
117
121
|
|
|
118
122
|
// Limits the max number of concurrent requests to 24.
|
|
119
123
|
this.rateLimiter = new RateLimiter(24);
|
|
@@ -298,6 +302,7 @@ export class EpochTracker implements IPersistedFileCache {
|
|
|
298
302
|
const clientCorrelationId = this.formatClientCorrelationId(fetchReason);
|
|
299
303
|
// Add epoch in fetch request.
|
|
300
304
|
this.addEpochInRequest(fetchOptions, addInBody, clientCorrelationId);
|
|
305
|
+
fetchOptions.headers = mergeRequestHeaders(this.requestHeaders, fetchOptions.headers);
|
|
301
306
|
let epochFromResponse: string | undefined;
|
|
302
307
|
return this.rateLimiter
|
|
303
308
|
.schedule(async () => fetcher(url, fetchOptions))
|
|
@@ -523,8 +528,9 @@ export class EpochTrackerWithRedemption extends EpochTracker {
|
|
|
523
528
|
protected readonly fileEntry: IFileEntry,
|
|
524
529
|
logger: TelemetryLoggerExt,
|
|
525
530
|
protected readonly clientIsSummarizer?: boolean,
|
|
531
|
+
requestHeaders?: Readonly<Record<string, string>>,
|
|
526
532
|
) {
|
|
527
|
-
super(cache, fileEntry, toITelemetryLoggerExt(logger), clientIsSummarizer);
|
|
533
|
+
super(cache, fileEntry, toITelemetryLoggerExt(logger), clientIsSummarizer, requestHeaders);
|
|
528
534
|
// Handles the rejected promise within treesLatestDeferral.
|
|
529
535
|
this.treesLatestDeferral.promise.catch(() => {});
|
|
530
536
|
}
|
|
@@ -656,12 +662,14 @@ export function createOdspCacheAndTracker(
|
|
|
656
662
|
fileEntry: IFileEntry,
|
|
657
663
|
logger: TelemetryLoggerExt,
|
|
658
664
|
clientIsSummarizer?: boolean,
|
|
665
|
+
requestHeaders?: Readonly<Record<string, string>>,
|
|
659
666
|
): ICacheAndTracker {
|
|
660
667
|
const epochTracker = new EpochTrackerWithRedemption(
|
|
661
668
|
persistedCacheArg,
|
|
662
669
|
fileEntry,
|
|
663
670
|
logger,
|
|
664
671
|
clientIsSummarizer,
|
|
672
|
+
requestHeaders,
|
|
665
673
|
);
|
|
666
674
|
return {
|
|
667
675
|
cache: {
|
package/src/fetchSnapshot.ts
CHANGED
|
@@ -68,6 +68,7 @@ import {
|
|
|
68
68
|
type TokenFetchOptionsEx,
|
|
69
69
|
} from "./odspUtils.js";
|
|
70
70
|
import { pkgVersion } from "./packageVersion.js";
|
|
71
|
+
import { mergeRequestHeaders } from "./requestHeaders.js";
|
|
71
72
|
|
|
72
73
|
/**
|
|
73
74
|
* Enum to support different types of snapshot formats.
|
|
@@ -131,6 +132,7 @@ export async function fetchSnapshotWithRedeem(
|
|
|
131
132
|
removeEntries: () => Promise<void>,
|
|
132
133
|
loadingGroupIds: string[] | undefined,
|
|
133
134
|
enableRedeemFallback?: boolean,
|
|
135
|
+
requestHeaders?: Readonly<Record<string, string>>,
|
|
134
136
|
): Promise<ISnapshot> {
|
|
135
137
|
// back-compat: This block to be removed with #8784 when we only consume/consider odsp resolvers that are >= 0.51
|
|
136
138
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
@@ -154,7 +156,7 @@ export async function fetchSnapshotWithRedeem(
|
|
|
154
156
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
155
157
|
if (enableRedeemFallback && isRedeemSharingLinkError(odspResolvedUrl, error)) {
|
|
156
158
|
// Execute the redeem fallback
|
|
157
|
-
await redeemSharingLink(odspResolvedUrl, storageTokenFetcher, logger);
|
|
159
|
+
await redeemSharingLink(odspResolvedUrl, storageTokenFetcher, logger, requestHeaders);
|
|
158
160
|
|
|
159
161
|
const shareLinkInfo = { ...odspResolvedUrl.shareLinkInfo };
|
|
160
162
|
delete shareLinkInfo.sharingLinkToRedeem;
|
|
@@ -205,7 +207,12 @@ export async function fetchSnapshotWithRedeem(
|
|
|
205
207
|
...getOdspResolvedUrl(error.redirectUrl),
|
|
206
208
|
shareLinkInfo: odspResolvedUrl.shareLinkInfo,
|
|
207
209
|
};
|
|
208
|
-
await redeemSharingLink(
|
|
210
|
+
await redeemSharingLink(
|
|
211
|
+
redirectedResolvedUrl,
|
|
212
|
+
storageTokenFetcher,
|
|
213
|
+
logger,
|
|
214
|
+
requestHeaders,
|
|
215
|
+
);
|
|
209
216
|
} catch (redeemError) {
|
|
210
217
|
logger.sendErrorEvent({ eventName: "RedirectRedeemFallbackError" }, redeemError);
|
|
211
218
|
}
|
|
@@ -236,6 +243,7 @@ async function redeemSharingLink(
|
|
|
236
243
|
odspResolvedUrl: IOdspResolvedUrl,
|
|
237
244
|
getAuthHeader: InstrumentedStorageTokenFetcher,
|
|
238
245
|
logger: TelemetryLoggerExt,
|
|
246
|
+
requestHeaders?: Readonly<Record<string, string>>,
|
|
239
247
|
): Promise<void> {
|
|
240
248
|
await PerformanceEvent.timedExecAsync(
|
|
241
249
|
logger,
|
|
@@ -272,7 +280,10 @@ async function redeemSharingLink(
|
|
|
272
280
|
);
|
|
273
281
|
const headers = getHeadersWithAuth(authHeader);
|
|
274
282
|
headers.prefer = isRedemptionNonDurable ? "nonDurableRedeem" : "redeemSharingLink";
|
|
275
|
-
await fetchAndParseAsJSONHelper(url, {
|
|
283
|
+
await fetchAndParseAsJSONHelper(url, {
|
|
284
|
+
headers: mergeRequestHeaders(requestHeaders, headers),
|
|
285
|
+
method,
|
|
286
|
+
});
|
|
276
287
|
});
|
|
277
288
|
}
|
|
278
289
|
|
|
@@ -735,6 +746,7 @@ export const downloadSnapshot = mockify(
|
|
|
735
746
|
controller?: AbortController,
|
|
736
747
|
epochTracker?: EpochTracker,
|
|
737
748
|
scenarioName?: string,
|
|
749
|
+
requestHeaders?: Readonly<Record<string, string>>,
|
|
738
750
|
): Promise<ISnapshotRequestAndResponseOptions> => {
|
|
739
751
|
// back-compat: This block to be removed with #8784 when we only consume/consider odsp resolvers that are >= 0.51
|
|
740
752
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
@@ -791,7 +803,7 @@ export const downloadSnapshot = mockify(
|
|
|
791
803
|
const { body, headers } = getFormBodyAndHeaders(odspResolvedUrl, authHeader, header);
|
|
792
804
|
const fetchOptions = {
|
|
793
805
|
body,
|
|
794
|
-
headers,
|
|
806
|
+
headers: mergeRequestHeaders(requestHeaders, headers),
|
|
795
807
|
signal: controller?.signal,
|
|
796
808
|
method,
|
|
797
809
|
};
|
|
@@ -806,6 +818,7 @@ export const downloadSnapshot = mockify(
|
|
|
806
818
|
headers.accept = `application/json, application/ms-fluid; v=${currentReadVersion}`;
|
|
807
819
|
}
|
|
808
820
|
}
|
|
821
|
+
fetchOptions.headers = mergeRequestHeaders(requestHeaders, headers);
|
|
809
822
|
|
|
810
823
|
const odspResponse = await (epochTracker?.fetch(
|
|
811
824
|
url,
|
package/src/getFileLink.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
toInstrumentedOdspStorageTokenFetcher,
|
|
28
28
|
} from "./odspUtils.js";
|
|
29
29
|
import { pkgVersion as driverVersion } from "./packageVersion.js";
|
|
30
|
+
import { copyRequestHeaders, mergeRequestHeaders } from "./requestHeaders.js";
|
|
30
31
|
import { runWithRetry as runWithRetryForCoherencyAndServiceReadOnlyErrors } from "./retryUtils.js";
|
|
31
32
|
|
|
32
33
|
// Store cached responses for the lifetime of web session as file link remains the same for given file item
|
|
@@ -48,7 +49,9 @@ export const getFileLink = mockify(
|
|
|
48
49
|
getToken: TokenFetcher<OdspResourceTokenFetchOptions>,
|
|
49
50
|
resolvedUrl: IOdspResolvedUrl,
|
|
50
51
|
logger: TelemetryLoggerExt,
|
|
52
|
+
requestHeaders?: Readonly<Record<string, string>>,
|
|
51
53
|
): Promise<string> => {
|
|
54
|
+
const immutableRequestHeaders = copyRequestHeaders(requestHeaders);
|
|
52
55
|
const cacheKey = `${resolvedUrl.siteUrl}_${resolvedUrl.driveId}_${resolvedUrl.itemId}`;
|
|
53
56
|
const maybeFileLinkCacheEntry = fileLinkCache.get(cacheKey);
|
|
54
57
|
if (maybeFileLinkCacheEntry !== undefined) {
|
|
@@ -63,7 +66,12 @@ export const getFileLink = mockify(
|
|
|
63
66
|
async () =>
|
|
64
67
|
runWithRetryForCoherencyAndServiceReadOnlyErrors(
|
|
65
68
|
async () =>
|
|
66
|
-
getFileLinkWithLocationRedirectionHandling(
|
|
69
|
+
getFileLinkWithLocationRedirectionHandling(
|
|
70
|
+
getToken,
|
|
71
|
+
resolvedUrl,
|
|
72
|
+
logger,
|
|
73
|
+
immutableRequestHeaders,
|
|
74
|
+
),
|
|
67
75
|
"getFileLinkCore",
|
|
68
76
|
logger,
|
|
69
77
|
),
|
|
@@ -121,6 +129,7 @@ async function getFileLinkWithLocationRedirectionHandling(
|
|
|
121
129
|
getToken: TokenFetcher<OdspResourceTokenFetchOptions>,
|
|
122
130
|
resolvedUrl: IOdspResolvedUrl,
|
|
123
131
|
logger: TelemetryLoggerExt,
|
|
132
|
+
requestHeaders?: Readonly<Record<string, string>>,
|
|
124
133
|
): Promise<string> {
|
|
125
134
|
// We can have chains of location redirection one after the other, so have a for loop
|
|
126
135
|
// so that we can keep handling the same type of error. Set max number of redirection to 5.
|
|
@@ -128,7 +137,7 @@ async function getFileLinkWithLocationRedirectionHandling(
|
|
|
128
137
|
let locationRedirected = false;
|
|
129
138
|
for (let count = 1; count <= 5; count++) {
|
|
130
139
|
try {
|
|
131
|
-
const fileItem = await getFileItemLite(getToken, resolvedUrl, logger);
|
|
140
|
+
const fileItem = await getFileItemLite(getToken, resolvedUrl, logger, requestHeaders);
|
|
132
141
|
// Sometimes the siteUrl in the actual file is different from the siteUrl in the resolvedUrl due to location
|
|
133
142
|
// redirection. This creates issues in the getSharingInformation call. So we need to update the siteUrl in the
|
|
134
143
|
// resolvedUrl to the siteUrl in the fileItem which is the updated siteUrl.
|
|
@@ -142,7 +151,7 @@ async function getFileLinkWithLocationRedirectionHandling(
|
|
|
142
151
|
});
|
|
143
152
|
renameTenantInOdspResolvedUrl(resolvedUrl, newSiteDomain);
|
|
144
153
|
}
|
|
145
|
-
return await getFileLinkCore(getToken, resolvedUrl, logger, fileItem);
|
|
154
|
+
return await getFileLinkCore(getToken, resolvedUrl, logger, fileItem, requestHeaders);
|
|
146
155
|
} catch (error: unknown) {
|
|
147
156
|
lastError = error;
|
|
148
157
|
// If the getSharingLink call fails with the 401/403/404 error, then it could be due to that the file has moved
|
|
@@ -167,6 +176,7 @@ async function getFileLinkCore(
|
|
|
167
176
|
odspUrlParts: IOdspUrlParts,
|
|
168
177
|
logger: TelemetryLoggerExt,
|
|
169
178
|
fileItem: FileItemLite,
|
|
179
|
+
requestHeaders?: Readonly<Record<string, string>>,
|
|
170
180
|
): Promise<string> {
|
|
171
181
|
// ODSP link requires extra call to return link that is resistant to file being renamed or moved to different folder
|
|
172
182
|
return PerformanceEvent.timedExecAsync(
|
|
@@ -202,11 +212,11 @@ async function getFileLinkCore(
|
|
|
202
212
|
const headers = getHeadersWithAuth(authHeader);
|
|
203
213
|
const requestInit = {
|
|
204
214
|
method,
|
|
205
|
-
headers: {
|
|
215
|
+
headers: mergeRequestHeaders(requestHeaders, {
|
|
206
216
|
"Content-Type": "application/json;odata=verbose",
|
|
207
|
-
|
|
217
|
+
Accept: "application/json;odata=verbose",
|
|
208
218
|
...headers,
|
|
209
|
-
},
|
|
219
|
+
}),
|
|
210
220
|
};
|
|
211
221
|
const response = await fetchHelper(url, requestInit);
|
|
212
222
|
additionalProps = response.propsToLog;
|
|
@@ -263,6 +273,7 @@ async function getFileItemLite(
|
|
|
263
273
|
getToken: TokenFetcher<OdspResourceTokenFetchOptions>,
|
|
264
274
|
odspUrlParts: IOdspUrlParts,
|
|
265
275
|
logger: TelemetryLoggerExt,
|
|
276
|
+
requestHeaders?: Readonly<Record<string, string>>,
|
|
266
277
|
): Promise<FileItemLite> {
|
|
267
278
|
return PerformanceEvent.timedExecAsync(
|
|
268
279
|
logger,
|
|
@@ -290,7 +301,10 @@ async function getFileItemLite(
|
|
|
290
301
|
);
|
|
291
302
|
|
|
292
303
|
const headers = getHeadersWithAuth(authHeader);
|
|
293
|
-
const requestInit = {
|
|
304
|
+
const requestInit = {
|
|
305
|
+
method,
|
|
306
|
+
headers: mergeRequestHeaders(requestHeaders, headers),
|
|
307
|
+
};
|
|
294
308
|
const response = await fetchHelper(url, requestInit);
|
|
295
309
|
additionalProps = response.propsToLog;
|
|
296
310
|
|
|
@@ -559,6 +559,7 @@ export class OdspDelayLoadedDeltaStream {
|
|
|
559
559
|
this.epochTracker,
|
|
560
560
|
this.socketReferenceKeyPrefix,
|
|
561
561
|
connectionId,
|
|
562
|
+
this.hostPolicy.hostProvidedRequestHeaders,
|
|
562
563
|
);
|
|
563
564
|
const duration = performanceNow() - startTime;
|
|
564
565
|
// This event happens rather often, so it adds up to cost of telemetry.
|
|
@@ -32,6 +32,7 @@ import type { EpochTracker } from "./epochTracker.js";
|
|
|
32
32
|
import { errorObjectFromSocketError } from "./odspError.js";
|
|
33
33
|
import { pkgVersion } from "./packageVersion.js";
|
|
34
34
|
import { SocketIOClientStatic } from "./socketModule.js";
|
|
35
|
+
import { copyRequestHeaders } from "./requestHeaders.js";
|
|
35
36
|
|
|
36
37
|
const protocolVersions = ["^0.4.0", "^0.3.0", "^0.2.0", "^0.1.0"];
|
|
37
38
|
const feature_get_ops = "api_get_ops";
|
|
@@ -259,6 +260,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
259
260
|
epochTracker: EpochTracker,
|
|
260
261
|
socketReferenceKeyPrefix: string | undefined,
|
|
261
262
|
connectionId: string = uuid(),
|
|
263
|
+
requestHeaders?: Readonly<Record<string, string>>,
|
|
262
264
|
): Promise<OdspDocumentDeltaConnection> {
|
|
263
265
|
const mc = loggerToMonitoringContext(telemetryLogger);
|
|
264
266
|
|
|
@@ -269,6 +271,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
269
271
|
|
|
270
272
|
// do not include the specific tenant/doc id in the ref key when multiplexing
|
|
271
273
|
// this will allow multiple documents to share the same websocket connection
|
|
274
|
+
const immutableRequestHeaders = copyRequestHeaders(requestHeaders);
|
|
272
275
|
const key = socketReferenceKeyPrefix ? `${socketReferenceKeyPrefix},${url}` : url;
|
|
273
276
|
const socketReferenceKey = enableMultiplexing ? key : `${key},${tenantId},${documentId}`;
|
|
274
277
|
|
|
@@ -279,6 +282,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
279
282
|
enableMultiplexing,
|
|
280
283
|
tenantId,
|
|
281
284
|
documentId,
|
|
285
|
+
immutableRequestHeaders,
|
|
282
286
|
);
|
|
283
287
|
|
|
284
288
|
const socket = socketReference.socket;
|
|
@@ -388,6 +392,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
388
392
|
enableMultiplexing: boolean,
|
|
389
393
|
tenantId: string,
|
|
390
394
|
documentId: string,
|
|
395
|
+
requestHeaders?: Readonly<Record<string, string>>,
|
|
391
396
|
): SocketReference {
|
|
392
397
|
// eslint-disable-next-line unicorn/no-array-method-this-argument
|
|
393
398
|
const existingSocketReference = SocketReference.find(key);
|
|
@@ -403,6 +408,7 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
403
408
|
reconnection: false,
|
|
404
409
|
transports: ["websocket"],
|
|
405
410
|
timeout: timeoutMs,
|
|
411
|
+
...(requestHeaders === undefined ? {} : { extraHeaders: requestHeaders }),
|
|
406
412
|
});
|
|
407
413
|
|
|
408
414
|
return new SocketReference(key, socket);
|
|
@@ -90,6 +90,7 @@ export function createOdspDocumentServiceFactory(
|
|
|
90
90
|
clientIsSummarizer,
|
|
91
91
|
persistedCache,
|
|
92
92
|
getStorageToken: options.getStorageToken,
|
|
93
|
+
requestHeaders: this.requestHeaders,
|
|
93
94
|
createDocumentService: async (url, odspLogger, cacheAndTracker, isSummarizer) =>
|
|
94
95
|
this.createDocumentServiceCore(url, odspLogger, cacheAndTracker, isSummarizer),
|
|
95
96
|
});
|
|
@@ -55,6 +55,7 @@ import {
|
|
|
55
55
|
toInstrumentedOdspStorageTokenFetcher,
|
|
56
56
|
toInstrumentedOdspTokenFetcher,
|
|
57
57
|
} from "./odspUtils.js";
|
|
58
|
+
import { copyRequestHeaders } from "./requestHeaders.js";
|
|
58
59
|
|
|
59
60
|
/**
|
|
60
61
|
* An ODSP document service factory that supports point-in-time (sequence-number-based) loading.
|
|
@@ -104,6 +105,8 @@ export interface IOdspPointInTimeDocumentServiceImplementationProps {
|
|
|
104
105
|
readonly persistedCache: IPersistedCache;
|
|
105
106
|
/** Fetches storage access tokens for ODSP requests. */
|
|
106
107
|
readonly getStorageToken: TokenFetcher<OdspResourceTokenFetchOptions>;
|
|
108
|
+
/** Host-owned attribution metadata for ODSP requests. */
|
|
109
|
+
readonly requestHeaders?: Readonly<Record<string, string>>;
|
|
107
110
|
/**
|
|
108
111
|
* Creates an ODSP document service for a resolved URL.
|
|
109
112
|
*
|
|
@@ -141,6 +144,8 @@ export class OdspDocumentServiceFactoryCore
|
|
|
141
144
|
{
|
|
142
145
|
private readonly nonPersistentCache: INonPersistentCache = new NonPersistentCache();
|
|
143
146
|
private readonly socketReferenceKeyPrefix?: string;
|
|
147
|
+
private readonly hostPolicy: HostStoragePolicy;
|
|
148
|
+
public readonly requestHeaders?: Readonly<Record<string, string>>;
|
|
144
149
|
|
|
145
150
|
public get snapshotPrefetchResultCache(): PromiseCache<string, IPrefetchSnapshotContents> {
|
|
146
151
|
return this.nonPersistentCache.snapshotPrefetchResultCache;
|
|
@@ -231,6 +236,7 @@ export class OdspDocumentServiceFactoryCore
|
|
|
231
236
|
fileEntry,
|
|
232
237
|
odspLogger,
|
|
233
238
|
clientIsSummarizer,
|
|
239
|
+
this.requestHeaders,
|
|
234
240
|
);
|
|
235
241
|
|
|
236
242
|
return PerformanceEvent.timedExecAsync(
|
|
@@ -307,9 +313,14 @@ export class OdspDocumentServiceFactoryCore
|
|
|
307
313
|
| TokenFetcher<OdspResourceTokenFetchOptions>
|
|
308
314
|
| undefined,
|
|
309
315
|
protected persistedCache: IPersistedCache = new LocalPersistentCache(),
|
|
310
|
-
|
|
316
|
+
hostPolicy: HostStoragePolicy = {},
|
|
311
317
|
) {
|
|
312
|
-
|
|
318
|
+
this.requestHeaders = copyRequestHeaders(hostPolicy.hostProvidedRequestHeaders);
|
|
319
|
+
this.hostPolicy =
|
|
320
|
+
this.requestHeaders === undefined
|
|
321
|
+
? hostPolicy
|
|
322
|
+
: { ...hostPolicy, hostProvidedRequestHeaders: this.requestHeaders };
|
|
323
|
+
if (this.hostPolicy.isolateSocketCache === true || this.requestHeaders !== undefined) {
|
|
313
324
|
// create the key to separate the socket reuse cache
|
|
314
325
|
this.socketReferenceKeyPrefix = uuid();
|
|
315
326
|
}
|
|
@@ -396,6 +407,7 @@ export class OdspDocumentServiceFactoryCore
|
|
|
396
407
|
},
|
|
397
408
|
extLogger,
|
|
398
409
|
clientIsSummarizer,
|
|
410
|
+
this.requestHeaders,
|
|
399
411
|
);
|
|
400
412
|
|
|
401
413
|
const storageTokenFetcher = toInstrumentedOdspStorageTokenFetcher(
|
|
@@ -284,6 +284,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
284
284
|
hostSnapshotOptions,
|
|
285
285
|
snapshotFetchOptions.loadingGroupIds,
|
|
286
286
|
snapshotFetchOptions.scenarioName,
|
|
287
|
+
true /* avoidPrefetchSnapshotCache */,
|
|
287
288
|
);
|
|
288
289
|
method = "networkOnly";
|
|
289
290
|
} else {
|
|
@@ -355,6 +356,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
355
356
|
hostSnapshotOptions,
|
|
356
357
|
snapshotFetchOptions.loadingGroupIds,
|
|
357
358
|
snapshotFetchOptions.scenarioName,
|
|
359
|
+
false /* avoidPrefetchSnapshotCache */,
|
|
358
360
|
);
|
|
359
361
|
|
|
360
362
|
// Ensure that failures on both paths are ignored initially.
|
|
@@ -412,6 +414,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
412
414
|
hostSnapshotOptions,
|
|
413
415
|
snapshotFetchOptions.loadingGroupIds,
|
|
414
416
|
snapshotFetchOptions.scenarioName,
|
|
417
|
+
false /* avoidPrefetchSnapshotCache */,
|
|
415
418
|
);
|
|
416
419
|
}
|
|
417
420
|
}
|
|
@@ -553,11 +556,13 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
553
556
|
hostSnapshotOptions: ISnapshotOptions | undefined,
|
|
554
557
|
loadingGroupIds: string[] | undefined,
|
|
555
558
|
scenarioName?: string,
|
|
559
|
+
avoidPrefetchSnapshotCache: boolean = false,
|
|
556
560
|
): Promise<ISnapshot | IPrefetchSnapshotContents> {
|
|
557
561
|
return this.fetchSnapshotFromNetworkCore(
|
|
558
562
|
hostSnapshotOptions,
|
|
559
563
|
loadingGroupIds,
|
|
560
564
|
scenarioName,
|
|
565
|
+
avoidPrefetchSnapshotCache,
|
|
561
566
|
).catch((error) => {
|
|
562
567
|
// Issue #5895:
|
|
563
568
|
// If we are offline, this error is retryable. But that means that RetriableDocumentStorageService
|
|
@@ -576,10 +581,13 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
576
581
|
hostSnapshotOptions: ISnapshotOptions | undefined,
|
|
577
582
|
loadingGroupIds: string[] | undefined,
|
|
578
583
|
scenarioName?: string,
|
|
584
|
+
avoidPrefetchSnapshotCache: boolean = false,
|
|
579
585
|
): Promise<ISnapshot | IPrefetchSnapshotContents> {
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
586
|
+
if (
|
|
587
|
+
!avoidPrefetchSnapshotCache &&
|
|
588
|
+
!this.hostPolicy.avoidPrefetchSnapshotCache &&
|
|
589
|
+
this.firstSnapshotFetchCall
|
|
590
|
+
) {
|
|
583
591
|
const prefetchCacheKey = getKeyForCacheEntry(
|
|
584
592
|
createCacheSnapshotKey(
|
|
585
593
|
this.odspResolvedUrl,
|
|
@@ -668,6 +676,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
668
676
|
removeEntries,
|
|
669
677
|
loadingGroupIds,
|
|
670
678
|
this.hostPolicy.enableRedeemFallback,
|
|
679
|
+
this.hostPolicy.hostProvidedRequestHeaders,
|
|
671
680
|
);
|
|
672
681
|
return odspSnapshot;
|
|
673
682
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -709,6 +718,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
709
718
|
removeEntries,
|
|
710
719
|
loadingGroupIds,
|
|
711
720
|
this.hostPolicy.enableRedeemFallback,
|
|
721
|
+
this.hostPolicy.hostProvidedRequestHeaders,
|
|
712
722
|
);
|
|
713
723
|
return odspSnapshot;
|
|
714
724
|
}
|
|
@@ -21,6 +21,7 @@ import type { TelemetryLoggerExt } from "@fluidframework/telemetry-utils/interna
|
|
|
21
21
|
import { type OdspFluidDataStoreLocator, SharingLinkHeader } from "./contractsPublic.js";
|
|
22
22
|
import { createOdspUrl } from "./createOdspUrl.js";
|
|
23
23
|
import { getFileLink } from "./getFileLink.js";
|
|
24
|
+
import { copyRequestHeaders } from "./requestHeaders.js";
|
|
24
25
|
import { OdspDriverUrlResolver } from "./odspDriverUrlResolver.js";
|
|
25
26
|
import {
|
|
26
27
|
getLocatorFromOdspUrl,
|
|
@@ -48,6 +49,11 @@ export interface ShareLinkFetcherProps {
|
|
|
48
49
|
* Identity type determining the shape of share link as it differs for Enterprise and Consumer users.
|
|
49
50
|
*/
|
|
50
51
|
identityType: IdentityType;
|
|
52
|
+
/**
|
|
53
|
+
* Host-owned attribution metadata for ODSP requests made while resolving share links.
|
|
54
|
+
* Driver-owned headers take precedence on case-insensitive collisions.
|
|
55
|
+
*/
|
|
56
|
+
requestHeaders?: Readonly<Record<string, string>>;
|
|
51
57
|
}
|
|
52
58
|
|
|
53
59
|
/**
|
|
@@ -90,6 +96,7 @@ export class OdspDriverUrlResolverForShareLink implements IUrlResolver {
|
|
|
90
96
|
this.shareLinkFetcherProps = {
|
|
91
97
|
...shareLinkFetcherProps,
|
|
92
98
|
tokenFetcher: shareLinkFetcherProps.tokenFetcher,
|
|
99
|
+
requestHeaders: copyRequestHeaders(shareLinkFetcherProps.requestHeaders),
|
|
93
100
|
};
|
|
94
101
|
}
|
|
95
102
|
}
|
|
@@ -209,6 +216,7 @@ export class OdspDriverUrlResolverForShareLink implements IUrlResolver {
|
|
|
209
216
|
this.shareLinkFetcherProps.tokenFetcher,
|
|
210
217
|
resolvedUrl,
|
|
211
218
|
this.logger,
|
|
219
|
+
this.shareLinkFetcherProps.requestHeaders,
|
|
212
220
|
).catch((error) => {
|
|
213
221
|
// This should imply that error is a non-retriable error.
|
|
214
222
|
this.logger.sendErrorEvent({ eventName: "FluidFileUrlError" }, error);
|
|
@@ -571,7 +571,11 @@ The injected implementation:
|
|
|
571
571
|
document services: a **recoverable** one bound to that base version (its storage is the base
|
|
572
572
|
snapshot) and a **live** one (its delta storage supplies the ops to replay). Both are created via
|
|
573
573
|
`createDocumentServiceCore` with a **single shared** `EpochTracker` (the same one the version
|
|
574
|
-
manager reads through) — this is the structural lineage guard; see the next question.
|
|
574
|
+
manager reads through) — this is the structural lineage guard; see the next question. The
|
|
575
|
+
point-in-time storage wrapper forces the recoverable service's initial snapshot read to bypass
|
|
576
|
+
both the persistent latest-snapshot cache and the in-memory snapshot-prefetch cache so the
|
|
577
|
+
selected base cannot be replaced by a newer cached live snapshot. Both loader snapshot paths
|
|
578
|
+
(`getSnapshot` and the legacy `getVersions` + `getSnapshotTree` path) apply this rule.
|
|
575
579
|
3. Return an `OdspPointInTimeDocumentService` composing the two.
|
|
576
580
|
|
|
577
581
|
It lives in this package rather than a generic wrapping driver (e.g. `@fluidframework/replay-driver`)
|
|
@@ -28,6 +28,7 @@ import { convertOdspSnapshotToSnapshotTreeAndBlobs } from "../odspSnapshotParser
|
|
|
28
28
|
import { getApiRoot } from "../odspUrlHelper.js";
|
|
29
29
|
import { fetchArray, getWithRetryForTokenRefresh } from "../odspUtils.js";
|
|
30
30
|
import { pkgVersion as driverVersion } from "../packageVersion.js";
|
|
31
|
+
import { mergeRequestHeaders } from "../requestHeaders.js";
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* A single ODSP file version, as listed by the file's version history.
|
|
@@ -96,6 +97,7 @@ export interface OdspFileVersionFetcherProps {
|
|
|
96
97
|
readonly getAuthHeader: InstrumentedStorageTokenFetcher;
|
|
97
98
|
readonly epochTracker: EpochTracker;
|
|
98
99
|
readonly logger: TelemetryLoggerExt;
|
|
100
|
+
readonly requestHeaders?: Readonly<Record<string, string>>;
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
/**
|
|
@@ -104,7 +106,7 @@ export interface OdspFileVersionFetcherProps {
|
|
|
104
106
|
export function createOdspFileVersionFetcher(
|
|
105
107
|
props: OdspFileVersionFetcherProps,
|
|
106
108
|
): IOdspFileVersionFetcher {
|
|
107
|
-
const { urlParts, getAuthHeader, epochTracker, logger } = props;
|
|
109
|
+
const { urlParts, getAuthHeader, epochTracker, logger, requestHeaders } = props;
|
|
108
110
|
const { siteUrl, driveId, itemId } = urlParts;
|
|
109
111
|
|
|
110
112
|
const listFileVersions = async (): Promise<OdspFileVersionRef[]> =>
|
|
@@ -209,7 +211,10 @@ export function createOdspFileVersionFetcher(
|
|
|
209
211
|
scenarioName,
|
|
210
212
|
);
|
|
211
213
|
const headers = getHeadersWithAuth(token);
|
|
212
|
-
const response = await fetchArray(url, {
|
|
214
|
+
const response = await fetchArray(url, {
|
|
215
|
+
method,
|
|
216
|
+
headers: mergeRequestHeaders(requestHeaders, headers),
|
|
217
|
+
});
|
|
213
218
|
return response.headers.get("x-fluid-epoch") ?? undefined;
|
|
214
219
|
});
|
|
215
220
|
|
package/src/packageVersion.ts
CHANGED
|
@@ -51,6 +51,7 @@ async function createVersionManager(
|
|
|
51
51
|
logger: TelemetryLoggerExt,
|
|
52
52
|
epochTracker: EpochTracker,
|
|
53
53
|
getStorageToken: IOdspPointInTimeDocumentServiceImplementationProps["getStorageToken"],
|
|
54
|
+
requestHeaders?: Readonly<Record<string, string>>,
|
|
54
55
|
): Promise<IOdspVersionManager> {
|
|
55
56
|
const urlParts: IOdspUrlParts = {
|
|
56
57
|
siteUrl: odspResolvedUrl.siteUrl,
|
|
@@ -67,6 +68,7 @@ async function createVersionManager(
|
|
|
67
68
|
getAuthHeader,
|
|
68
69
|
epochTracker,
|
|
69
70
|
logger,
|
|
71
|
+
requestHeaders,
|
|
70
72
|
});
|
|
71
73
|
}
|
|
72
74
|
|
|
@@ -120,6 +122,7 @@ export async function createPointInTimeDocumentServiceCore(
|
|
|
120
122
|
clientIsSummarizer,
|
|
121
123
|
persistedCache,
|
|
122
124
|
getStorageToken,
|
|
125
|
+
requestHeaders,
|
|
123
126
|
createDocumentService,
|
|
124
127
|
}: IOdspPointInTimeDocumentServiceImplementationProps,
|
|
125
128
|
dependencies: IPointInTimeDocumentServiceDependencies = {},
|
|
@@ -137,12 +140,13 @@ export async function createPointInTimeDocumentServiceCore(
|
|
|
137
140
|
},
|
|
138
141
|
extLogger,
|
|
139
142
|
clientIsSummarizer,
|
|
143
|
+
requestHeaders,
|
|
140
144
|
);
|
|
141
145
|
|
|
142
146
|
const versionManager = await (
|
|
143
147
|
dependencies.createVersionManager ??
|
|
144
148
|
(async (url, versionLogger, epochTracker) =>
|
|
145
|
-
createVersionManager(url, versionLogger, epochTracker, getStorageToken))
|
|
149
|
+
createVersionManager(url, versionLogger, epochTracker, getStorageToken, requestHeaders))
|
|
146
150
|
)(odspResolvedUrl, extLogger, cacheAndTracker.epochTracker);
|
|
147
151
|
const baseResult = await versionManager.findBaseForSeq(targetSequenceNumber);
|
|
148
152
|
if (baseResult.kind === "noBaseVersion") {
|