@fluidframework/odsp-driver 2.0.1 → 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.
- package/api-report/odsp-driver.alpha.api.md +1 -1
- package/dist/createFile.d.ts +3 -3
- package/dist/createFile.d.ts.map +1 -1
- package/dist/createFile.js +11 -9
- package/dist/createFile.js.map +1 -1
- package/dist/createNewContainerOnExistingFile.d.ts +1 -1
- package/dist/createNewContainerOnExistingFile.d.ts.map +1 -1
- package/dist/createNewContainerOnExistingFile.js +2 -2
- package/dist/createNewContainerOnExistingFile.js.map +1 -1
- package/dist/createNewUtils.d.ts +1 -1
- package/dist/createNewUtils.d.ts.map +1 -1
- package/dist/createNewUtils.js +22 -17
- package/dist/createNewUtils.js.map +1 -1
- package/dist/fetchSnapshot.d.ts +6 -9
- package/dist/fetchSnapshot.d.ts.map +1 -1
- package/dist/fetchSnapshot.js +22 -21
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/getFileLink.js +13 -8
- package/dist/getFileLink.js.map +1 -1
- package/dist/getUrlAndHeadersWithAuth.d.ts +2 -5
- package/dist/getUrlAndHeadersWithAuth.d.ts.map +1 -1
- package/dist/getUrlAndHeadersWithAuth.js +7 -28
- package/dist/getUrlAndHeadersWithAuth.js.map +1 -1
- package/dist/odspDelayLoadedDeltaStream.d.ts +3 -4
- package/dist/odspDelayLoadedDeltaStream.d.ts.map +1 -1
- package/dist/odspDelayLoadedDeltaStream.js +4 -5
- package/dist/odspDelayLoadedDeltaStream.js.map +1 -1
- package/dist/odspDeltaStorageService.d.ts +2 -2
- package/dist/odspDeltaStorageService.d.ts.map +1 -1
- package/dist/odspDeltaStorageService.js +9 -8
- package/dist/odspDeltaStorageService.js.map +1 -1
- package/dist/odspDocumentService.d.ts +4 -4
- package/dist/odspDocumentService.d.ts.map +1 -1
- package/dist/odspDocumentService.js +9 -9
- package/dist/odspDocumentService.js.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.js +6 -4
- package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
- package/dist/odspDocumentStorageManager.d.ts +2 -2
- package/dist/odspDocumentStorageManager.d.ts.map +1 -1
- package/dist/odspDocumentStorageManager.js +25 -19
- package/dist/odspDocumentStorageManager.js.map +1 -1
- package/dist/odspSummaryUploadManager.d.ts +2 -3
- package/dist/odspSummaryUploadManager.d.ts.map +1 -1
- package/dist/odspSummaryUploadManager.js +6 -5
- package/dist/odspSummaryUploadManager.js.map +1 -1
- package/dist/odspUtils.d.ts +11 -3
- package/dist/odspUtils.d.ts.map +1 -1
- package/dist/odspUtils.js +10 -6
- package/dist/odspUtils.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/prefetchLatestSnapshot.d.ts +1 -1
- package/dist/prefetchLatestSnapshot.d.ts.map +1 -1
- package/dist/prefetchLatestSnapshot.js +5 -5
- package/dist/prefetchLatestSnapshot.js.map +1 -1
- package/dist/vroom.d.ts +2 -2
- package/dist/vroom.d.ts.map +1 -1
- package/dist/vroom.js +7 -9
- package/dist/vroom.js.map +1 -1
- package/lib/createFile.d.ts +3 -3
- package/lib/createFile.d.ts.map +1 -1
- package/lib/createFile.js +12 -10
- package/lib/createFile.js.map +1 -1
- package/lib/createNewContainerOnExistingFile.d.ts +1 -1
- package/lib/createNewContainerOnExistingFile.d.ts.map +1 -1
- package/lib/createNewContainerOnExistingFile.js +2 -2
- package/lib/createNewContainerOnExistingFile.js.map +1 -1
- package/lib/createNewUtils.d.ts +1 -1
- package/lib/createNewUtils.d.ts.map +1 -1
- package/lib/createNewUtils.js +23 -18
- package/lib/createNewUtils.js.map +1 -1
- package/lib/fetchSnapshot.d.ts +6 -9
- package/lib/fetchSnapshot.d.ts.map +1 -1
- package/lib/fetchSnapshot.js +23 -22
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/getFileLink.js +14 -9
- package/lib/getFileLink.js.map +1 -1
- package/lib/getUrlAndHeadersWithAuth.d.ts +2 -5
- package/lib/getUrlAndHeadersWithAuth.d.ts.map +1 -1
- package/lib/getUrlAndHeadersWithAuth.js +5 -26
- package/lib/getUrlAndHeadersWithAuth.js.map +1 -1
- package/lib/odspDelayLoadedDeltaStream.d.ts +3 -4
- package/lib/odspDelayLoadedDeltaStream.d.ts.map +1 -1
- package/lib/odspDelayLoadedDeltaStream.js +4 -5
- package/lib/odspDelayLoadedDeltaStream.js.map +1 -1
- package/lib/odspDeltaStorageService.d.ts +2 -2
- package/lib/odspDeltaStorageService.d.ts.map +1 -1
- package/lib/odspDeltaStorageService.js +9 -8
- package/lib/odspDeltaStorageService.js.map +1 -1
- package/lib/odspDocumentService.d.ts +4 -4
- package/lib/odspDocumentService.d.ts.map +1 -1
- package/lib/odspDocumentService.js +9 -9
- package/lib/odspDocumentService.js.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.js +6 -4
- package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
- package/lib/odspDocumentStorageManager.d.ts +2 -2
- package/lib/odspDocumentStorageManager.d.ts.map +1 -1
- package/lib/odspDocumentStorageManager.js +26 -20
- package/lib/odspDocumentStorageManager.js.map +1 -1
- package/lib/odspSummaryUploadManager.d.ts +2 -3
- package/lib/odspSummaryUploadManager.d.ts.map +1 -1
- package/lib/odspSummaryUploadManager.js +7 -6
- package/lib/odspSummaryUploadManager.js.map +1 -1
- package/lib/odspUtils.d.ts +11 -3
- package/lib/odspUtils.d.ts.map +1 -1
- package/lib/odspUtils.js +11 -7
- package/lib/odspUtils.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/prefetchLatestSnapshot.d.ts +1 -1
- package/lib/prefetchLatestSnapshot.d.ts.map +1 -1
- package/lib/prefetchLatestSnapshot.js +5 -5
- package/lib/prefetchLatestSnapshot.js.map +1 -1
- package/lib/vroom.d.ts +2 -2
- package/lib/vroom.d.ts.map +1 -1
- package/lib/vroom.js +7 -9
- package/lib/vroom.js.map +1 -1
- package/package.json +11 -11
- package/src/createFile.ts +15 -21
- package/src/createNewContainerOnExistingFile.ts +2 -2
- package/src/createNewUtils.ts +32 -24
- package/src/fetchSnapshot.ts +35 -34
- package/src/getFileLink.ts +26 -20
- package/src/getUrlAndHeadersWithAuth.ts +6 -31
- package/src/odspDelayLoadedDeltaStream.ts +4 -5
- package/src/odspDeltaStorageService.ts +11 -7
- package/src/odspDocumentService.ts +8 -8
- package/src/odspDocumentServiceFactoryCore.ts +5 -3
- package/src/odspDocumentStorageManager.ts +36 -33
- package/src/odspSummaryUploadManager.ts +9 -9
- package/src/odspUtils.ts +19 -6
- package/src/packageVersion.ts +1 -1
- package/src/prefetchLatestSnapshot.ts +9 -5
- 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.
|
|
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.
|
|
81
|
-
"@fluidframework/core-interfaces": "~2.0.
|
|
82
|
-
"@fluidframework/core-utils": "~2.0.
|
|
83
|
-
"@fluidframework/driver-base": "~2.0.
|
|
84
|
-
"@fluidframework/driver-definitions": "~2.0.
|
|
85
|
-
"@fluidframework/driver-utils": "~2.0.
|
|
86
|
-
"@fluidframework/odsp-doclib-utils": "~2.0.
|
|
87
|
-
"@fluidframework/odsp-driver-definitions": "~2.0.
|
|
88
|
-
"@fluidframework/telemetry-utils": "~2.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.
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
65
|
+
getAuthHeader,
|
|
66
66
|
logger,
|
|
67
67
|
initialUrl,
|
|
68
68
|
forceAccessTokenViaAuthorizationHeader,
|
package/src/createNewUtils.ts
CHANGED
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
OdspSummaryTreeValue,
|
|
32
32
|
} from "./contracts.js";
|
|
33
33
|
import { EpochTracker, FetchType } from "./epochTracker.js";
|
|
34
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
...
|
|
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
|
|
283
|
+
method,
|
|
276
284
|
},
|
|
277
285
|
fetchType,
|
|
278
286
|
addInBody,
|
package/src/fetchSnapshot.ts
CHANGED
|
@@ -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 {
|
|
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 -
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
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:
|
|
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
|
-
|
|
281
|
+
getAuthHeader: InstrumentedStorageTokenFetcher,
|
|
289
282
|
snapshotOptions: ISnapshotOptions | undefined,
|
|
290
283
|
logger: ITelemetryLoggerExt,
|
|
291
284
|
snapshotDownloader: (
|
|
292
285
|
finalOdspResolvedUrl: IOdspResolvedUrl,
|
|
293
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
|
738
|
+
method,
|
|
738
739
|
};
|
|
739
740
|
// Decide what snapshot format to fetch as per the feature gate.
|
|
740
741
|
switch (snapshotFormatFetchType) {
|
package/src/getFileLink.ts
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
isFluidError,
|
|
20
20
|
} from "@fluidframework/telemetry-utils/internal";
|
|
21
21
|
|
|
22
|
-
import {
|
|
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
|
|
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
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
|
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
|
|
266
|
+
const getAuthHeader = toInstrumentedOdspStorageTokenFetcher(
|
|
266
267
|
logger,
|
|
267
268
|
odspUrlParts,
|
|
268
269
|
getToken,
|
|
269
270
|
);
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
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
|
|
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
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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.
|
|
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) {
|