@fluidframework/odsp-driver 2.0.0 → 2.0.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.
Files changed (138) hide show
  1. package/api-report/odsp-driver.alpha.api.md +1 -1
  2. package/dist/createFile.d.ts +3 -3
  3. package/dist/createFile.d.ts.map +1 -1
  4. package/dist/createFile.js +11 -9
  5. package/dist/createFile.js.map +1 -1
  6. package/dist/createNewContainerOnExistingFile.d.ts +1 -1
  7. package/dist/createNewContainerOnExistingFile.d.ts.map +1 -1
  8. package/dist/createNewContainerOnExistingFile.js +2 -2
  9. package/dist/createNewContainerOnExistingFile.js.map +1 -1
  10. package/dist/createNewUtils.d.ts +1 -1
  11. package/dist/createNewUtils.d.ts.map +1 -1
  12. package/dist/createNewUtils.js +22 -17
  13. package/dist/createNewUtils.js.map +1 -1
  14. package/dist/fetchSnapshot.d.ts +6 -9
  15. package/dist/fetchSnapshot.d.ts.map +1 -1
  16. package/dist/fetchSnapshot.js +22 -21
  17. package/dist/fetchSnapshot.js.map +1 -1
  18. package/dist/getFileLink.js +13 -8
  19. package/dist/getFileLink.js.map +1 -1
  20. package/dist/getUrlAndHeadersWithAuth.d.ts +2 -5
  21. package/dist/getUrlAndHeadersWithAuth.d.ts.map +1 -1
  22. package/dist/getUrlAndHeadersWithAuth.js +7 -28
  23. package/dist/getUrlAndHeadersWithAuth.js.map +1 -1
  24. package/dist/odspDelayLoadedDeltaStream.d.ts +3 -4
  25. package/dist/odspDelayLoadedDeltaStream.d.ts.map +1 -1
  26. package/dist/odspDelayLoadedDeltaStream.js +4 -5
  27. package/dist/odspDelayLoadedDeltaStream.js.map +1 -1
  28. package/dist/odspDeltaStorageService.d.ts +2 -2
  29. package/dist/odspDeltaStorageService.d.ts.map +1 -1
  30. package/dist/odspDeltaStorageService.js +9 -8
  31. package/dist/odspDeltaStorageService.js.map +1 -1
  32. package/dist/odspDocumentService.d.ts +4 -4
  33. package/dist/odspDocumentService.d.ts.map +1 -1
  34. package/dist/odspDocumentService.js +9 -9
  35. package/dist/odspDocumentService.js.map +1 -1
  36. package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  37. package/dist/odspDocumentServiceFactoryCore.js +6 -4
  38. package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
  39. package/dist/odspDocumentStorageManager.d.ts +2 -2
  40. package/dist/odspDocumentStorageManager.d.ts.map +1 -1
  41. package/dist/odspDocumentStorageManager.js +25 -19
  42. package/dist/odspDocumentStorageManager.js.map +1 -1
  43. package/dist/odspSummaryUploadManager.d.ts +2 -3
  44. package/dist/odspSummaryUploadManager.d.ts.map +1 -1
  45. package/dist/odspSummaryUploadManager.js +6 -5
  46. package/dist/odspSummaryUploadManager.js.map +1 -1
  47. package/dist/odspUtils.d.ts +11 -3
  48. package/dist/odspUtils.d.ts.map +1 -1
  49. package/dist/odspUtils.js +10 -6
  50. package/dist/odspUtils.js.map +1 -1
  51. package/dist/packageVersion.d.ts +1 -1
  52. package/dist/packageVersion.js +1 -1
  53. package/dist/packageVersion.js.map +1 -1
  54. package/dist/prefetchLatestSnapshot.d.ts +1 -1
  55. package/dist/prefetchLatestSnapshot.d.ts.map +1 -1
  56. package/dist/prefetchLatestSnapshot.js +5 -5
  57. package/dist/prefetchLatestSnapshot.js.map +1 -1
  58. package/dist/vroom.d.ts +2 -2
  59. package/dist/vroom.d.ts.map +1 -1
  60. package/dist/vroom.js +7 -9
  61. package/dist/vroom.js.map +1 -1
  62. package/lib/createFile.d.ts +3 -3
  63. package/lib/createFile.d.ts.map +1 -1
  64. package/lib/createFile.js +12 -10
  65. package/lib/createFile.js.map +1 -1
  66. package/lib/createNewContainerOnExistingFile.d.ts +1 -1
  67. package/lib/createNewContainerOnExistingFile.d.ts.map +1 -1
  68. package/lib/createNewContainerOnExistingFile.js +2 -2
  69. package/lib/createNewContainerOnExistingFile.js.map +1 -1
  70. package/lib/createNewUtils.d.ts +1 -1
  71. package/lib/createNewUtils.d.ts.map +1 -1
  72. package/lib/createNewUtils.js +23 -18
  73. package/lib/createNewUtils.js.map +1 -1
  74. package/lib/fetchSnapshot.d.ts +6 -9
  75. package/lib/fetchSnapshot.d.ts.map +1 -1
  76. package/lib/fetchSnapshot.js +23 -22
  77. package/lib/fetchSnapshot.js.map +1 -1
  78. package/lib/getFileLink.js +14 -9
  79. package/lib/getFileLink.js.map +1 -1
  80. package/lib/getUrlAndHeadersWithAuth.d.ts +2 -5
  81. package/lib/getUrlAndHeadersWithAuth.d.ts.map +1 -1
  82. package/lib/getUrlAndHeadersWithAuth.js +5 -26
  83. package/lib/getUrlAndHeadersWithAuth.js.map +1 -1
  84. package/lib/odspDelayLoadedDeltaStream.d.ts +3 -4
  85. package/lib/odspDelayLoadedDeltaStream.d.ts.map +1 -1
  86. package/lib/odspDelayLoadedDeltaStream.js +4 -5
  87. package/lib/odspDelayLoadedDeltaStream.js.map +1 -1
  88. package/lib/odspDeltaStorageService.d.ts +2 -2
  89. package/lib/odspDeltaStorageService.d.ts.map +1 -1
  90. package/lib/odspDeltaStorageService.js +9 -8
  91. package/lib/odspDeltaStorageService.js.map +1 -1
  92. package/lib/odspDocumentService.d.ts +4 -4
  93. package/lib/odspDocumentService.d.ts.map +1 -1
  94. package/lib/odspDocumentService.js +9 -9
  95. package/lib/odspDocumentService.js.map +1 -1
  96. package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  97. package/lib/odspDocumentServiceFactoryCore.js +6 -4
  98. package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
  99. package/lib/odspDocumentStorageManager.d.ts +2 -2
  100. package/lib/odspDocumentStorageManager.d.ts.map +1 -1
  101. package/lib/odspDocumentStorageManager.js +26 -20
  102. package/lib/odspDocumentStorageManager.js.map +1 -1
  103. package/lib/odspSummaryUploadManager.d.ts +2 -3
  104. package/lib/odspSummaryUploadManager.d.ts.map +1 -1
  105. package/lib/odspSummaryUploadManager.js +7 -6
  106. package/lib/odspSummaryUploadManager.js.map +1 -1
  107. package/lib/odspUtils.d.ts +11 -3
  108. package/lib/odspUtils.d.ts.map +1 -1
  109. package/lib/odspUtils.js +11 -7
  110. package/lib/odspUtils.js.map +1 -1
  111. package/lib/packageVersion.d.ts +1 -1
  112. package/lib/packageVersion.js +1 -1
  113. package/lib/packageVersion.js.map +1 -1
  114. package/lib/prefetchLatestSnapshot.d.ts +1 -1
  115. package/lib/prefetchLatestSnapshot.d.ts.map +1 -1
  116. package/lib/prefetchLatestSnapshot.js +5 -5
  117. package/lib/prefetchLatestSnapshot.js.map +1 -1
  118. package/lib/vroom.d.ts +2 -2
  119. package/lib/vroom.d.ts.map +1 -1
  120. package/lib/vroom.js +7 -9
  121. package/lib/vroom.js.map +1 -1
  122. package/package.json +11 -11
  123. package/src/createFile.ts +15 -21
  124. package/src/createNewContainerOnExistingFile.ts +2 -2
  125. package/src/createNewUtils.ts +32 -24
  126. package/src/fetchSnapshot.ts +35 -34
  127. package/src/getFileLink.ts +26 -20
  128. package/src/getUrlAndHeadersWithAuth.ts +6 -31
  129. package/src/odspDelayLoadedDeltaStream.ts +4 -5
  130. package/src/odspDeltaStorageService.ts +11 -7
  131. package/src/odspDocumentService.ts +8 -8
  132. package/src/odspDocumentServiceFactoryCore.ts +5 -3
  133. package/src/odspDocumentStorageManager.ts +36 -33
  134. package/src/odspSummaryUploadManager.ts +9 -9
  135. package/src/odspUtils.ts +19 -6
  136. package/src/packageVersion.ts +1 -1
  137. package/src/prefetchLatestSnapshot.ts +9 -5
  138. package/src/vroom.ts +11 -11
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/odsp-driver",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Socket storage implementation for SPO and ODC",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -77,15 +77,15 @@
77
77
  "temp-directory": "nyc/.nyc_output"
78
78
  },
79
79
  "dependencies": {
80
- "@fluid-internal/client-utils": "~2.0.0",
81
- "@fluidframework/core-interfaces": "~2.0.0",
82
- "@fluidframework/core-utils": "~2.0.0",
83
- "@fluidframework/driver-base": "~2.0.0",
84
- "@fluidframework/driver-definitions": "~2.0.0",
85
- "@fluidframework/driver-utils": "~2.0.0",
86
- "@fluidframework/odsp-doclib-utils": "~2.0.0",
87
- "@fluidframework/odsp-driver-definitions": "~2.0.0",
88
- "@fluidframework/telemetry-utils": "~2.0.0",
80
+ "@fluid-internal/client-utils": "~2.0.2",
81
+ "@fluidframework/core-interfaces": "~2.0.2",
82
+ "@fluidframework/core-utils": "~2.0.2",
83
+ "@fluidframework/driver-base": "~2.0.2",
84
+ "@fluidframework/driver-definitions": "~2.0.2",
85
+ "@fluidframework/driver-utils": "~2.0.2",
86
+ "@fluidframework/odsp-doclib-utils": "~2.0.2",
87
+ "@fluidframework/odsp-driver-definitions": "~2.0.2",
88
+ "@fluidframework/telemetry-utils": "~2.0.2",
89
89
  "node-fetch": "^2.6.9",
90
90
  "socket.io-client": "^4.7.3",
91
91
  "uuid": "^9.0.0"
@@ -93,7 +93,7 @@
93
93
  "devDependencies": {
94
94
  "@arethetypeswrong/cli": "^0.15.2",
95
95
  "@biomejs/biome": "^1.7.3",
96
- "@fluid-internal/mocha-test-setup": "~2.0.0",
96
+ "@fluid-internal/mocha-test-setup": "~2.0.2",
97
97
  "@fluid-tools/build-cli": "^0.39.0",
98
98
  "@fluidframework/build-common": "^2.0.3",
99
99
  "@fluidframework/build-tools": "^0.39.0",
package/src/createFile.ts CHANGED
@@ -28,7 +28,7 @@ import {
28
28
  } from "./createNewUtils.js";
29
29
  import { createOdspUrl } from "./createOdspUrl.js";
30
30
  import { EpochTracker } from "./epochTracker.js";
31
- import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
31
+ import { getHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
32
32
  import { OdspDriverUrlResolver } from "./odspDriverUrlResolver.js";
33
33
  import { getApiRoot } from "./odspUrlHelper.js";
34
34
  import {
@@ -50,7 +50,7 @@ const isInvalidFileName = (fileName: string): boolean => {
50
50
  * Returns resolved url
51
51
  */
52
52
  export async function createNewFluidFile(
53
- getStorageToken: InstrumentedStorageTokenFetcher,
53
+ getAuthHeader: InstrumentedStorageTokenFetcher,
54
54
  newFileInfo: INewFileInfo,
55
55
  logger: ITelemetryLoggerExt,
56
56
  createNewSummary: ISummaryTree | undefined,
@@ -75,16 +75,10 @@ export async function createNewFluidFile(
75
75
  let summaryHandle: string = "";
76
76
  let shareLinkInfo: ShareLinkInfoType | undefined;
77
77
  if (createNewSummary === undefined) {
78
- itemId = await createNewEmptyFluidFile(
79
- getStorageToken,
80
- newFileInfo,
81
- logger,
82
- epochTracker,
83
- forceAccessTokenViaAuthorizationHeader,
84
- );
78
+ itemId = await createNewEmptyFluidFile(getAuthHeader, newFileInfo, logger, epochTracker);
85
79
  } else {
86
80
  const content = await createNewFluidFileFromSummary(
87
- getStorageToken,
81
+ getAuthHeader,
88
82
  newFileInfo,
89
83
  logger,
90
84
  createNewSummary,
@@ -163,11 +157,10 @@ function extractShareLinkData(
163
157
  }
164
158
 
165
159
  export async function createNewEmptyFluidFile(
166
- getStorageToken: InstrumentedStorageTokenFetcher,
160
+ getAuthHeader: InstrumentedStorageTokenFetcher,
167
161
  newFileInfo: INewFileInfo,
168
162
  logger: ITelemetryLoggerExt,
169
163
  epochTracker: EpochTracker,
170
- forceAccessTokenViaAuthorizationHeader: boolean,
171
164
  ): Promise<string> {
172
165
  const filePath = newFileInfo.filePath ? encodeURIComponent(`/${newFileInfo.filePath}`) : "";
173
166
  // add .tmp extension to empty file (host is expected to rename)
@@ -177,17 +170,18 @@ export async function createNewEmptyFluidFile(
177
170
  }/items/root:/${filePath}/${encodedFilename}:/content?@name.conflictBehavior=rename&select=id,name,parentReference`;
178
171
 
179
172
  return getWithRetryForTokenRefresh(async (options) => {
180
- const storageToken = await getStorageToken(options, "CreateNewFile");
173
+ const url = initialUrl;
174
+ const method = "PUT";
175
+ const authHeader = await getAuthHeader(
176
+ { ...options, request: { url, method } },
177
+ "CreateNewFile",
178
+ );
181
179
 
182
180
  return PerformanceEvent.timedExecAsync(
183
181
  logger,
184
182
  { eventName: "createNewEmptyFile" },
185
183
  async (event) => {
186
- const { url, headers } = getUrlAndHeadersWithAuth(
187
- initialUrl,
188
- storageToken,
189
- forceAccessTokenViaAuthorizationHeader,
190
- );
184
+ const headers = getHeadersWithAuth(authHeader);
191
185
  headers["Content-Type"] = "application/json";
192
186
 
193
187
  const fetchResponse = await runWithRetry(
@@ -197,7 +191,7 @@ export async function createNewEmptyFluidFile(
197
191
  {
198
192
  body: undefined,
199
193
  headers,
200
- method: "PUT",
194
+ method,
201
195
  },
202
196
  "createFile",
203
197
  ),
@@ -225,7 +219,7 @@ export async function createNewEmptyFluidFile(
225
219
  }
226
220
 
227
221
  export async function createNewFluidFileFromSummary(
228
- getStorageToken: InstrumentedStorageTokenFetcher,
222
+ getAuthHeader: InstrumentedStorageTokenFetcher,
229
223
  newFileInfo: INewFileInfo,
230
224
  logger: ITelemetryLoggerExt,
231
225
  createNewSummary: ISummaryTree,
@@ -249,7 +243,7 @@ export async function createNewFluidFileFromSummary(
249
243
 
250
244
  return createNewFluidContainerCore<ICreateFileResponse>({
251
245
  containerSnapshot,
252
- getStorageToken,
246
+ getAuthHeader,
253
247
  logger,
254
248
  initialUrl,
255
249
  forceAccessTokenViaAuthorizationHeader,
@@ -38,7 +38,7 @@ import { IExistingFileInfo, createCacheSnapshotKey } from "./odspUtils.js";
38
38
  * "alternative file partition" where the main File stub is an ASPX page.
39
39
  */
40
40
  export async function createNewContainerOnExistingFile(
41
- getStorageToken: InstrumentedStorageTokenFetcher,
41
+ getAuthHeader: InstrumentedStorageTokenFetcher,
42
42
  fileInfo: IExistingFileInfo,
43
43
  logger: ITelemetryLoggerExt,
44
44
  createNewSummary: ISummaryTree | undefined,
@@ -62,7 +62,7 @@ export async function createNewContainerOnExistingFile(
62
62
 
63
63
  const { id: summaryHandle } = await createNewFluidContainerCore<IWriteSummaryResponse>({
64
64
  containerSnapshot,
65
- getStorageToken,
65
+ getAuthHeader,
66
66
  logger,
67
67
  initialUrl,
68
68
  forceAccessTokenViaAuthorizationHeader,
@@ -31,7 +31,7 @@ import {
31
31
  OdspSummaryTreeValue,
32
32
  } from "./contracts.js";
33
33
  import { EpochTracker, FetchType } from "./epochTracker.js";
34
- import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
34
+ import { getHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
35
35
  import { getWithRetryForTokenRefresh, maxUmpPostBodySize } from "./odspUtils.js";
36
36
  import { runWithRetry } from "./retryUtils.js";
37
37
 
@@ -202,7 +202,7 @@ function convertSummaryToSnapshotTreeForCreateNew(summary: ISummaryTree): IOdspS
202
202
 
203
203
  export async function createNewFluidContainerCore<T>(args: {
204
204
  containerSnapshot: IOdspSummaryPayload;
205
- getStorageToken: InstrumentedStorageTokenFetcher;
205
+ getAuthHeader: InstrumentedStorageTokenFetcher;
206
206
  logger: ITelemetryLoggerExt;
207
207
  initialUrl: string;
208
208
  forceAccessTokenViaAuthorizationHeader: boolean;
@@ -213,10 +213,9 @@ export async function createNewFluidContainerCore<T>(args: {
213
213
  }): Promise<T> {
214
214
  const {
215
215
  containerSnapshot,
216
- getStorageToken,
216
+ getAuthHeader,
217
217
  logger,
218
218
  initialUrl,
219
- forceAccessTokenViaAuthorizationHeader,
220
219
  epochTracker,
221
220
  telemetryName,
222
221
  fetchType,
@@ -224,8 +223,6 @@ export async function createNewFluidContainerCore<T>(args: {
224
223
  } = args;
225
224
 
226
225
  return getWithRetryForTokenRefresh(async (options) => {
227
- const storageToken = await getStorageToken(options, telemetryName);
228
-
229
226
  return PerformanceEvent.timedExecAsync(
230
227
  logger,
231
228
  { eventName: telemetryName },
@@ -235,31 +232,42 @@ export async function createNewFluidContainerCore<T>(args: {
235
232
  let headers: { [index: string]: string };
236
233
  let addInBody = false;
237
234
  const formBoundary = uuid();
238
- let postBody = `--${formBoundary}\r\n`;
239
- postBody += `Authorization: Bearer ${storageToken}\r\n`;
240
- postBody += `X-HTTP-Method-Override: POST\r\n`;
241
- postBody += `Content-Type: application/json\r\n`;
242
- postBody += `_post: 1\r\n`;
243
- postBody += `\r\n${snapshotBody}\r\n`;
244
- postBody += `\r\n--${formBoundary}--`;
235
+ const urlObj = new URL(initialUrl);
236
+ urlObj.searchParams.set("ump", "1");
237
+ const authInBodyUrl = urlObj.href;
238
+ const method = "POST";
239
+ const authHeader = await getAuthHeader(
240
+ { ...options, request: { url: authInBodyUrl, method } },
241
+ telemetryName,
242
+ );
243
+ const postBodyWithAuth =
244
+ `--${formBoundary}\r\n` +
245
+ `Authorization: ${authHeader}\r\n` +
246
+ `X-HTTP-Method-Override: POST\r\n` +
247
+ `Content-Type: application/json\r\n` +
248
+ `_post: 1\r\n` +
249
+ `\r\n${snapshotBody}\r\n` +
250
+ `\r\n--${formBoundary}--`;
245
251
 
246
- if (postBody.length <= maxUmpPostBodySize) {
247
- const urlObj = new URL(initialUrl);
248
- urlObj.searchParams.set("ump", "1");
249
- url = urlObj.href;
252
+ let postBody = snapshotBody;
253
+ if (
254
+ postBodyWithAuth.length <= maxUmpPostBodySize &&
255
+ authHeader?.startsWith("Bearer")
256
+ ) {
257
+ url = authInBodyUrl;
250
258
  headers = {
251
259
  "Content-Type": `multipart/form-data;boundary=${formBoundary}`,
252
260
  };
253
261
  addInBody = true;
262
+ postBody = postBodyWithAuth;
254
263
  } else {
255
- const parts = getUrlAndHeadersWithAuth(
256
- initialUrl,
257
- storageToken,
258
- forceAccessTokenViaAuthorizationHeader,
264
+ url = initialUrl;
265
+ const authHeaderNoUmp = await getAuthHeader(
266
+ { ...options, request: { url, method } },
267
+ telemetryName,
259
268
  );
260
- url = parts.url;
261
269
  headers = {
262
- ...parts.headers,
270
+ ...getHeadersWithAuth(authHeaderNoUmp),
263
271
  "Content-Type": "application/json",
264
272
  };
265
273
  postBody = snapshotBody;
@@ -272,7 +280,7 @@ export async function createNewFluidContainerCore<T>(args: {
272
280
  {
273
281
  body: postBody,
274
282
  headers,
275
- method: "POST",
283
+ method,
276
284
  },
277
285
  fetchType,
278
286
  addInBody,
@@ -45,7 +45,7 @@ import {
45
45
  } from "./contracts.js";
46
46
  import { EpochTracker } from "./epochTracker.js";
47
47
  import { getQueryString } from "./getQueryString.js";
48
- import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
48
+ import { getHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
49
49
  import { convertOdspSnapshotToSnapshotTreeAndBlobs } from "./odspSnapshotParser.js";
50
50
  import {
51
51
  IOdspResponse,
@@ -57,6 +57,7 @@ import {
57
57
  measure,
58
58
  measureP,
59
59
  useLegacyFlowWithoutGroupsForSnapshotFetch,
60
+ type TokenFetchOptionsEx,
60
61
  } from "./odspUtils.js";
61
62
  import { pkgVersion } from "./packageVersion.js";
62
63
 
@@ -73,25 +74,19 @@ export enum SnapshotFormatSupportType {
73
74
  /**
74
75
  * Fetches a snapshot from the server with a given version id.
75
76
  * @param snapshotUrl - snapshot url from where the odsp snapshot will be fetched
76
- * @param token - token used for authorization in the request
77
- * @param storageFetchWrapper - Implementation of the get/post methods used to fetch the snapshot
78
77
  * @param versionId - id of specific snapshot to be fetched
79
78
  * @param fetchFullSnapshot - whether we want to fetch full snapshot(with blobs)
80
- * @param forceAccessTokenViaAuthorizationHeader - whether to force passing given token via authorization header
79
+ * @param forceAccessTokenViaAuthorizationHeader - @deprecated Not used, true value always used instead. Whether to force passing given token via authorization header
80
+ * @param snapshotDownloader - Implementation of the get/post methods used to fetch the snapshot. snapshotDownloader is responsible for generating the appropriate headers (including Authorization header) as well as handling any token refreshes before retrying.
81
81
  * @returns A promise of the snapshot and the status code of the response
82
82
  */
83
83
  export async function fetchSnapshot(
84
84
  snapshotUrl: string,
85
- // eslint-disable-next-line @rushstack/no-new-null
86
- token: string,
87
85
  versionId: string,
88
86
  fetchFullSnapshot: boolean,
89
87
  forceAccessTokenViaAuthorizationHeader: boolean,
90
88
  logger: ITelemetryLoggerExt,
91
- snapshotDownloader: (
92
- url: string,
93
- fetchOptions: { [index: string]: RequestInit },
94
- ) => Promise<IOdspResponse<unknown>>,
89
+ snapshotDownloader: (url: string) => Promise<IOdspResponse<unknown>>,
95
90
  ): Promise<ISnapshot> {
96
91
  const path = `/trees/${versionId}`;
97
92
  let queryParams: ISnapshotOptions = {};
@@ -101,17 +96,13 @@ export async function fetchSnapshot(
101
96
  }
102
97
 
103
98
  const queryString = getQueryString(queryParams);
104
- const { url, headers } = getUrlAndHeadersWithAuth(
105
- `${snapshotUrl}${path}${queryString}`,
106
- token,
107
- forceAccessTokenViaAuthorizationHeader,
108
- );
99
+ const url = `${snapshotUrl}${path}${queryString}`;
109
100
  const response = (await PerformanceEvent.timedExecAsync(
110
101
  logger,
111
102
  {
112
103
  eventName: "fetchSnapshot",
113
104
  },
114
- async () => snapshotDownloader(url, { headers }),
105
+ async () => snapshotDownloader(url),
115
106
  )) as IOdspResponse<IOdspSnapshot>;
116
107
  return convertOdspSnapshotToSnapshotTreeAndBlobs(response.content);
117
108
  }
@@ -124,7 +115,8 @@ export async function fetchSnapshotWithRedeem(
124
115
  logger: ITelemetryLoggerExt,
125
116
  snapshotDownloader: (
126
117
  finalOdspResolvedUrl: IOdspResolvedUrl,
127
- storageToken: string,
118
+ getAuthHeader: InstrumentedStorageTokenFetcher,
119
+ tokenFetchOptions: TokenFetchOptionsEx,
128
120
  loadingGroupIds: string[] | undefined,
129
121
  snapshotOptions: ISnapshotOptions | undefined,
130
122
  controller?: AbortController,
@@ -217,7 +209,7 @@ export async function fetchSnapshotWithRedeem(
217
209
 
218
210
  async function redeemSharingLink(
219
211
  odspResolvedUrl: IOdspResolvedUrl,
220
- storageTokenFetcher: InstrumentedStorageTokenFetcher,
212
+ getAuthHeader: InstrumentedStorageTokenFetcher,
221
213
  logger: ITelemetryLoggerExt,
222
214
  forceAccessTokenViaAuthorizationHeader: boolean,
223
215
  ): Promise<void> {
@@ -239,15 +231,16 @@ async function redeemSharingLink(
239
231
  let redeemUrl: string | undefined;
240
232
  async function callSharesAPI(baseUrl: string): Promise<void> {
241
233
  await getWithRetryForTokenRefresh(async (tokenFetchOptions) => {
242
- const storageToken = await storageTokenFetcher(tokenFetchOptions, "RedeemShareLink");
243
234
  redeemUrl = `${baseUrl}/_api/v2.0/shares/${encodedShareUrl}`;
244
- const { url, headers } = getUrlAndHeadersWithAuth(
245
- redeemUrl,
246
- storageToken,
247
- forceAccessTokenViaAuthorizationHeader,
235
+ const url = redeemUrl;
236
+ const method = "GET";
237
+ const authHeader = await getAuthHeader(
238
+ { ...tokenFetchOptions, request: { url, method } },
239
+ "RedeemShareLink",
248
240
  );
241
+ const headers = getHeadersWithAuth(authHeader);
249
242
  headers.prefer = "redeemSharingLink";
250
- await fetchAndParseAsJSONHelper(url, { headers });
243
+ await fetchAndParseAsJSONHelper(url, { headers, method });
251
244
  });
252
245
  }
253
246
 
@@ -271,7 +264,7 @@ async function redeemSharingLink(
271
264
  shareLinkUrlLength: odspResolvedUrl.shareLinkInfo?.sharingLinkToRedeem.length,
272
265
  queryParamsLength: new URL(odspResolvedUrl.shareLinkInfo?.sharingLinkToRedeem)
273
266
  .search.length,
274
- useHeaders: forceAccessTokenViaAuthorizationHeader,
267
+ useHeaders: true,
275
268
  }),
276
269
  },
277
270
  error,
@@ -285,12 +278,13 @@ async function redeemSharingLink(
285
278
 
286
279
  async function fetchLatestSnapshotCore(
287
280
  odspResolvedUrl: IOdspResolvedUrl,
288
- storageTokenFetcher: InstrumentedStorageTokenFetcher,
281
+ getAuthHeader: InstrumentedStorageTokenFetcher,
289
282
  snapshotOptions: ISnapshotOptions | undefined,
290
283
  logger: ITelemetryLoggerExt,
291
284
  snapshotDownloader: (
292
285
  finalOdspResolvedUrl: IOdspResolvedUrl,
293
- storageToken: string,
286
+ getAuthHeader: InstrumentedStorageTokenFetcher,
287
+ tokenFetchOptions: TokenFetchOptionsEx,
294
288
  loadingGroupIds: string[] | undefined,
295
289
  snapshotOptions: ISnapshotOptions | undefined,
296
290
  controller?: AbortController,
@@ -302,7 +296,6 @@ async function fetchLatestSnapshotCore(
302
296
  return getWithRetryForTokenRefresh(async (tokenFetchOptions) => {
303
297
  const fetchSnapshotForLoadingGroup = isSnapshotFetchForLoadingGroup(loadingGroupIds);
304
298
  const eventName = fetchSnapshotForLoadingGroup ? "TreesLatestForGroup" : "TreesLatest";
305
- const storageToken = await storageTokenFetcher(tokenFetchOptions, eventName, true);
306
299
 
307
300
  const perfEvent = {
308
301
  eventName,
@@ -331,7 +324,8 @@ async function fetchLatestSnapshotCore(
331
324
  const [response, fetchTime] = await measureP(async () =>
332
325
  snapshotDownloader(
333
326
  odspResolvedUrl,
334
- storageToken,
327
+ getAuthHeader,
328
+ tokenFetchOptions,
335
329
  loadingGroupIds,
336
330
  snapshotOptions,
337
331
  controller,
@@ -594,7 +588,7 @@ export interface ISnapshotRequestAndResponseOptions {
594
588
 
595
589
  function getFormBodyAndHeaders(
596
590
  odspResolvedUrl: IOdspResolvedUrl,
597
- storageToken: string,
591
+ authHeader: string,
598
592
  headers?: { [index: string]: string },
599
593
  ): {
600
594
  body: string;
@@ -606,7 +600,7 @@ function getFormBodyAndHeaders(
606
600
  const formParams: string[] = [];
607
601
  formParams.push(
608
602
  `--${formBoundary}`,
609
- `Authorization: Bearer ${storageToken}`,
603
+ `Authorization: ${authHeader}`,
610
604
  `X-HTTP-Method-Override: GET`,
611
605
  );
612
606
 
@@ -690,7 +684,8 @@ function getTreeStatsCore(snapshotTree: ISnapshotTree, stats: ITreeStats): void
690
684
  */
691
685
  export async function downloadSnapshot(
692
686
  odspResolvedUrl: IOdspResolvedUrl,
693
- storageToken: string,
687
+ getAuthHeader: InstrumentedStorageTokenFetcher,
688
+ tokenFetchOptions: TokenFetchOptionsEx,
694
689
  loadingGroupIds: string[] | undefined,
695
690
  snapshotOptions: ISnapshotOptions | undefined,
696
691
  snapshotFormatFetchType?: SnapshotFormatSupportType,
@@ -724,17 +719,23 @@ export async function downloadSnapshot(
724
719
 
725
720
  const queryString = getQueryString(queryParams);
726
721
  const url = `${snapshotUrl}/trees/latest${queryString}`;
722
+ const method = "POST";
727
723
  // The location of file can move on Spo in which case server returns 308(Permanent Redirect) error.
728
724
  // Adding below header will make VROOM API return 404 instead of 308 and browser can intercept it.
729
725
  // This error thrown by server will contain the new redirect location. Look at the 404 error parsing
730
726
  // for further reference here: \packages\utils\odsp-doclib-utils\src\odspErrorUtils.ts
731
727
  const header = { prefer: "manualredirect" };
732
- const { body, headers } = getFormBodyAndHeaders(odspResolvedUrl, storageToken, header);
728
+ const authHeader = await getAuthHeader(
729
+ { ...tokenFetchOptions, request: { url, method } },
730
+ "downloadSnapshot",
731
+ );
732
+ assert(authHeader !== null, 0x1e5 /* "Storage token should not be null" */);
733
+ const { body, headers } = getFormBodyAndHeaders(odspResolvedUrl, authHeader, header);
733
734
  const fetchOptions = {
734
735
  body,
735
736
  headers,
736
737
  signal: controller?.signal,
737
- method: "POST",
738
+ method,
738
739
  };
739
740
  // Decide what snapshot format to fetch as per the feature gate.
740
741
  switch (snapshotFormatFetchType) {
@@ -19,7 +19,7 @@ import {
19
19
  isFluidError,
20
20
  } from "@fluidframework/telemetry-utils/internal";
21
21
 
22
- import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
22
+ import { getHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
23
23
  import {
24
24
  fetchHelper,
25
25
  getWithRetryForTokenRefresh,
@@ -165,12 +165,11 @@ async function getFileLinkCore(
165
165
  let additionalProps;
166
166
  const fileLink = await getWithRetryForTokenRefresh(async (options) => {
167
167
  attempts++;
168
- const storageTokenFetcher = toInstrumentedOdspStorageTokenFetcher(
168
+ const getAuthHeader = toInstrumentedOdspStorageTokenFetcher(
169
169
  logger,
170
170
  odspUrlParts,
171
171
  getToken,
172
172
  );
173
- const storageToken = await storageTokenFetcher(options, "GetFileLinkCore");
174
173
 
175
174
  // IMPORTANT: In past we were using GetFileByUrl() API to get to the list item that was corresponding
176
175
  // to the file. This was intentionally replaced with GetFileById() to solve the following issue:
@@ -178,17 +177,19 @@ async function getFileLinkCore(
178
177
  // where webDavUrl is constructed using legacy ODC format for backward compatibility reasons.
179
178
  // GetFileByUrl() does not understand that format and thus fails. GetFileById() relies on file item
180
179
  // unique guid (sharepointIds.listItemUniqueId) and it works uniformly across Consumer and Commercial.
181
- const { url, headers } = getUrlAndHeadersWithAuth(
182
- `${
183
- odspUrlParts.siteUrl
184
- }/_api/web/GetFileById(@a1)/ListItemAllFields/GetSharingInformation?@a1=guid${encodeURIComponent(
185
- `'${fileItem.sharepointIds.listItemUniqueId}'`,
186
- )}`,
187
- storageToken,
188
- true,
180
+ const url = `${
181
+ odspUrlParts.siteUrl
182
+ }/_api/web/GetFileById(@a1)/ListItemAllFields/GetSharingInformation?@a1=guid${encodeURIComponent(
183
+ `'${fileItem.sharepointIds.listItemUniqueId}'`,
184
+ )}`;
185
+ const method = "POST";
186
+ const authHeader = await getAuthHeader(
187
+ { ...options, request: { url, method } },
188
+ "GetFileLinkCore",
189
189
  );
190
+ const headers = getHeadersWithAuth(authHeader);
190
191
  const requestInit = {
191
- method: "POST",
192
+ method,
192
193
  headers: {
193
194
  "Content-Type": "application/json;odata=verbose",
194
195
  "Accept": "application/json;odata=verbose",
@@ -262,20 +263,25 @@ async function getFileItemLite(
262
263
  const fileItem = await getWithRetryForTokenRefresh(async (options) => {
263
264
  attempts++;
264
265
  const { siteUrl, driveId, itemId } = odspUrlParts;
265
- const storageTokenFetcher = toInstrumentedOdspStorageTokenFetcher(
266
+ const getAuthHeader = toInstrumentedOdspStorageTokenFetcher(
266
267
  logger,
267
268
  odspUrlParts,
268
269
  getToken,
269
270
  );
270
- const storageToken = await storageTokenFetcher(options, "GetFileItemLite");
271
-
272
- const { url, headers } = getUrlAndHeadersWithAuth(
273
- `${siteUrl}/_api/v2.0/drives/${driveId}/items/${itemId}?select=webUrl,webDavUrl,sharepointIds`,
274
- storageToken,
275
- forceAccessTokenViaAuthorizationHeader,
271
+ const url = `${siteUrl}/_api/v2.0/drives/${driveId}/items/${itemId}?select=webUrl,webDavUrl,sharepointIds`;
272
+ const method = "GET";
273
+ const authHeader = await getAuthHeader(
274
+ { ...options, request: { url, method } },
275
+ "GetFileItemLite",
276
+ );
277
+ assert(
278
+ authHeader !== null,
279
+ 0x2bc /* "Instrumented token fetcher with throwOnNullToken =true should never return null" */,
276
280
  );
281
+
282
+ const headers = getHeadersWithAuth(authHeader);
277
283
  headers.redirect = "manual";
278
- const requestInit = { method: "GET", headers };
284
+ const requestInit = { method, headers };
279
285
  const response = await fetchHelper(url, requestInit);
280
286
  additionalProps = response.propsToLog;
281
287
 
@@ -5,38 +5,13 @@
5
5
 
6
6
  import { assert } from "@fluidframework/core-utils/internal";
7
7
 
8
- export function getUrlAndHeadersWithAuth(
9
- url: string,
10
- token: string,
11
- forceAccessTokenViaAuthorizationHeader: boolean,
12
- ): { url: string; headers: { [index: string]: string } } {
13
- assert(token.length > 0, 0x936 /* should be token */);
14
-
15
- if (!forceAccessTokenViaAuthorizationHeader) {
16
- // Pass access token via query string: this will make request be treated as 'simple' request
17
- // which does not require OPTIONS call as part of CORS check.
18
- const urlWithAccessTokenInQueryString = new URL(url);
19
- // IMPORTANT: Do not apply encodeURIComponent to token, param value is automatically encoded
20
- // when set via URLSearchParams class
21
- urlWithAccessTokenInQueryString.searchParams.set("access_token", token);
22
- // ODSP APIs have a limitation that the query string cannot exceed 2048 characters.
23
- // If the query string exceeds 2048, we have to fall back to sending the access token as a header, which
24
- // has a negative performance implication as it adds a performance overhead.
25
- // NOTE: URL.search.length value includes '?' symbol and it is unclear whether backend logic which enforces
26
- // query length limit accounts for it. This logic errs on side of caution and includes that key in overall
27
- // query length.
28
- if (urlWithAccessTokenInQueryString.search.length <= 2048) {
29
- return {
30
- headers: {},
31
- url: urlWithAccessTokenInQueryString.href,
32
- };
33
- }
34
- }
8
+ export function getHeadersWithAuth(
9
+ // eslint-disable-next-line @rushstack/no-new-null
10
+ authHeader: string | null,
11
+ ): { [index: string]: string } {
12
+ assert(!!authHeader, 0x936 /* authHeader should not be null or empty */);
35
13
 
36
14
  return {
37
- headers: {
38
- Authorization: `Bearer ${token}`,
39
- },
40
- url,
15
+ Authorization: authHeader,
41
16
  };
42
17
  }
@@ -70,7 +70,7 @@ export class OdspDelayLoadedDeltaStream {
70
70
  /**
71
71
  * @param odspResolvedUrl - resolved url identifying document that will be managed by this service instance.
72
72
  * @param policies - Document service policies.
73
- * @param getStorageToken - function that can provide the storage token. This is is also referred to as
73
+ * @param getAuthHeader - function that can provide the Authentication header value. This is is also referred to as
74
74
  * the "Vroom" token in SPO.
75
75
  * @param getWebsocketToken - function that can provide a token for accessing the web socket. This is also referred
76
76
  * to as the "Push" token in SPO. If undefined then websocket token is expected to be returned with joinSession
@@ -85,13 +85,13 @@ export class OdspDelayLoadedDeltaStream {
85
85
  public constructor(
86
86
  public readonly odspResolvedUrl: IOdspResolvedUrl,
87
87
  public policies: IDocumentServicePolicies,
88
- private readonly getStorageToken: InstrumentedStorageTokenFetcher,
88
+ private readonly getAuthHeader: InstrumentedStorageTokenFetcher,
89
89
  private readonly getWebsocketToken:
90
90
  | ((options: TokenFetchOptions) => Promise<string | null>)
91
91
  | undefined,
92
92
  private readonly mc: MonitoringContext,
93
93
  private readonly cache: IOdspCache,
94
- private readonly hostPolicy: HostStoragePolicy,
94
+ _hostPolicy: HostStoragePolicy,
95
95
  private readonly epochTracker: EpochTracker,
96
96
  private readonly opsReceived: (ops: ISequencedDocumentMessage[]) => void,
97
97
  private readonly metadataUpdateHandler: (metadata: Record<string, string>) => void,
@@ -391,13 +391,12 @@ export class OdspDelayLoadedDeltaStream {
391
391
  "opStream/joinSession",
392
392
  "POST",
393
393
  this.mc.logger,
394
- this.getStorageToken,
394
+ this.getAuthHeader,
395
395
  this.epochTracker,
396
396
  requestSocketToken,
397
397
  options,
398
398
  disableJoinSessionRefresh,
399
399
  isRefreshingJoinSession,
400
- this.hostPolicy.sessionOptions?.unauthenticatedUserDisplayName,
401
400
  );
402
401
  // Emit event only in case it is fetched from the network.
403
402
  if (joinSessionResponse.sensitivityLabelsInfo !== undefined) {