@fluidframework/odsp-driver 0.58.2001 → 0.59.2000-61729
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/dist/compactSnapshotParser.d.ts +1 -0
- package/dist/compactSnapshotParser.d.ts.map +1 -1
- package/dist/compactSnapshotParser.js +8 -7
- package/dist/compactSnapshotParser.js.map +1 -1
- package/dist/createNewUtils.js +0 -1
- package/dist/createNewUtils.js.map +1 -1
- package/dist/epochTracker.d.ts +12 -0
- package/dist/epochTracker.d.ts.map +1 -1
- package/dist/epochTracker.js +18 -22
- package/dist/epochTracker.js.map +1 -1
- package/dist/fetchSnapshot.d.ts +11 -0
- package/dist/fetchSnapshot.d.ts.map +1 -1
- package/dist/fetchSnapshot.js +56 -70
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/odspDocumentService.d.ts +2 -1
- package/dist/odspDocumentService.d.ts.map +1 -1
- package/dist/odspDocumentService.js +14 -7
- package/dist/odspDocumentService.js.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.d.ts +2 -2
- package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.js +6 -6
- package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
- package/dist/odspDocumentStorageManager.d.ts +0 -6
- package/dist/odspDocumentStorageManager.d.ts.map +1 -1
- package/dist/odspDocumentStorageManager.js +9 -58
- package/dist/odspDocumentStorageManager.js.map +1 -1
- package/dist/odspDriverUrlResolver.d.ts +2 -6
- package/dist/odspDriverUrlResolver.d.ts.map +1 -1
- package/dist/odspDriverUrlResolver.js +15 -10
- package/dist/odspDriverUrlResolver.js.map +1 -1
- package/dist/odspDriverUrlResolverForShareLink.d.ts +2 -6
- package/dist/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
- package/dist/odspDriverUrlResolverForShareLink.js +12 -9
- package/dist/odspDriverUrlResolverForShareLink.js.map +1 -1
- package/dist/odspSnapshotParser.d.ts.map +1 -1
- package/dist/odspSnapshotParser.js +1 -5
- package/dist/odspSnapshotParser.js.map +1 -1
- package/dist/odspSummaryUploadManager.d.ts.map +1 -1
- package/dist/odspSummaryUploadManager.js +9 -6
- package/dist/odspSummaryUploadManager.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/compactSnapshotParser.d.ts +1 -0
- package/lib/compactSnapshotParser.d.ts.map +1 -1
- package/lib/compactSnapshotParser.js +7 -6
- package/lib/compactSnapshotParser.js.map +1 -1
- package/lib/createNewUtils.js +0 -1
- package/lib/createNewUtils.js.map +1 -1
- package/lib/epochTracker.d.ts +12 -0
- package/lib/epochTracker.d.ts.map +1 -1
- package/lib/epochTracker.js +19 -23
- package/lib/epochTracker.js.map +1 -1
- package/lib/fetchSnapshot.d.ts +11 -0
- package/lib/fetchSnapshot.d.ts.map +1 -1
- package/lib/fetchSnapshot.js +58 -72
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/odspDocumentService.d.ts +2 -1
- package/lib/odspDocumentService.d.ts.map +1 -1
- package/lib/odspDocumentService.js +14 -7
- package/lib/odspDocumentService.js.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.d.ts +2 -2
- package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.js +6 -6
- package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
- package/lib/odspDocumentStorageManager.d.ts +0 -6
- package/lib/odspDocumentStorageManager.d.ts.map +1 -1
- package/lib/odspDocumentStorageManager.js +9 -58
- package/lib/odspDocumentStorageManager.js.map +1 -1
- package/lib/odspDriverUrlResolver.d.ts +2 -6
- package/lib/odspDriverUrlResolver.d.ts.map +1 -1
- package/lib/odspDriverUrlResolver.js +14 -9
- package/lib/odspDriverUrlResolver.js.map +1 -1
- package/lib/odspDriverUrlResolverForShareLink.d.ts +2 -6
- package/lib/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
- package/lib/odspDriverUrlResolverForShareLink.js +10 -7
- package/lib/odspDriverUrlResolverForShareLink.js.map +1 -1
- package/lib/odspSnapshotParser.d.ts.map +1 -1
- package/lib/odspSnapshotParser.js +1 -5
- package/lib/odspSnapshotParser.js.map +1 -1
- package/lib/odspSummaryUploadManager.d.ts.map +1 -1
- package/lib/odspSummaryUploadManager.js +9 -6
- package/lib/odspSummaryUploadManager.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +19 -14
- package/src/compactSnapshotParser.ts +14 -12
- package/src/createNewUtils.ts +0 -1
- package/src/epochTracker.ts +34 -25
- package/src/fetchSnapshot.ts +59 -89
- package/src/odspDocumentService.ts +8 -5
- package/src/odspDocumentServiceFactoryCore.ts +7 -2
- package/src/odspDocumentStorageManager.ts +10 -64
- package/src/odspDriverUrlResolver.ts +18 -9
- package/src/odspDriverUrlResolverForShareLink.ts +11 -8
- package/src/odspSnapshotParser.ts +1 -4
- package/src/odspSummaryUploadManager.ts +7 -6
- package/src/packageVersion.ts +1 -1
package/src/fetchSnapshot.ts
CHANGED
|
@@ -16,19 +16,20 @@ import {
|
|
|
16
16
|
InstrumentedStorageTokenFetcher,
|
|
17
17
|
} from "@fluidframework/odsp-driver-definitions";
|
|
18
18
|
import { ISnapshotTree } from "@fluidframework/protocol-definitions";
|
|
19
|
+
import { isSystemMessage } from "@fluidframework/protocol-base";
|
|
19
20
|
import { IOdspSnapshot, ISnapshotCachedEntry, IVersionedValueWithEpoch, persistedCacheValueVersion } from "./contracts";
|
|
20
21
|
import { getQueryString } from "./getQueryString";
|
|
21
22
|
import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth";
|
|
22
23
|
import {
|
|
23
24
|
fetchAndParseAsJSONHelper,
|
|
24
|
-
|
|
25
|
+
fetchHelper,
|
|
25
26
|
getWithRetryForTokenRefresh,
|
|
26
27
|
getWithRetryForTokenRefreshRepeat,
|
|
27
28
|
IOdspResponse,
|
|
28
29
|
ISnapshotContents,
|
|
29
30
|
} from "./odspUtils";
|
|
30
31
|
import { convertOdspSnapshotToSnapsohtTreeAndBlobs } from "./odspSnapshotParser";
|
|
31
|
-
import { parseCompactSnapshotResponse } from "./compactSnapshotParser";
|
|
32
|
+
import { currentReadVersion, parseCompactSnapshotResponse } from "./compactSnapshotParser";
|
|
32
33
|
import { ReadBuffer } from "./ReadBufferUtils";
|
|
33
34
|
import { EpochTracker } from "./epochTracker";
|
|
34
35
|
|
|
@@ -313,6 +314,7 @@ async function fetchLatestSnapshotCore(
|
|
|
313
314
|
encodedBlobsSize,
|
|
314
315
|
sequenceNumber,
|
|
315
316
|
ops: snapshot.ops?.length ?? 0,
|
|
317
|
+
nonSysOps: snapshot.ops?.filter((op) => !isSystemMessage(op)).length ?? 0,
|
|
316
318
|
headers: Object.keys(response.requestHeaders).length !== 0 ? true : undefined,
|
|
317
319
|
// Interval between the first fetch until the last byte of the last redirect.
|
|
318
320
|
redirectTime,
|
|
@@ -360,89 +362,6 @@ interface ISnapshotRequestAndResponseOptions {
|
|
|
360
362
|
requestHeaders: {[index: string]: any},
|
|
361
363
|
}
|
|
362
364
|
|
|
363
|
-
/**
|
|
364
|
-
* This function fetches the older snapshot format which is the json format(IOdspSnapshot).
|
|
365
|
-
* @param odspResolvedUrl - resolved odsp url.
|
|
366
|
-
* @param storageToken - token to do the auth for network request.
|
|
367
|
-
* @param snapshotOptions - Options used to specify how and what to fetch in the snapshot.
|
|
368
|
-
* @param controller - abort controller if caller needs to abort the network call.
|
|
369
|
-
* @param epochTracker - epoch tracker used to add/validate epoch in the network call.
|
|
370
|
-
* @returns fetched snapshot.
|
|
371
|
-
*/
|
|
372
|
-
async function fetchSnapshotContentsCoreV1(
|
|
373
|
-
odspResolvedUrl: IOdspResolvedUrl,
|
|
374
|
-
storageToken: string,
|
|
375
|
-
snapshotOptions: ISnapshotOptions | undefined,
|
|
376
|
-
controller?: AbortController,
|
|
377
|
-
epochTracker?: EpochTracker,
|
|
378
|
-
): Promise<ISnapshotRequestAndResponseOptions> {
|
|
379
|
-
const snapshotUrl = odspResolvedUrl.endpoints.snapshotStorageUrl;
|
|
380
|
-
const url = `${snapshotUrl}/trees/latest?ump=1`;
|
|
381
|
-
// The location of file can move on Spo in which case server returns 308(Permanent Redirect) error.
|
|
382
|
-
// Adding below header will make VROOM API return 404 instead of 308 and browser can intercept it.
|
|
383
|
-
// This error thrown by server will contain the new redirect location. Look at the 404 error parsing
|
|
384
|
-
// for futher reference here: \packages\utils\odsp-doclib-utils\src\odspErrorUtils.ts
|
|
385
|
-
const header = { prefer: "manualredirect" };
|
|
386
|
-
const { body, headers } = getFormBodyAndHeaders(
|
|
387
|
-
odspResolvedUrl, storageToken, snapshotOptions, header);
|
|
388
|
-
headers.accept = "application/json";
|
|
389
|
-
const fetchOptions = {
|
|
390
|
-
body,
|
|
391
|
-
headers,
|
|
392
|
-
signal: controller?.signal,
|
|
393
|
-
method: "POST",
|
|
394
|
-
};
|
|
395
|
-
const response = await (epochTracker?.fetchAndParseAsJSON<IOdspSnapshot>(url, fetchOptions, "treesLatest", true) ??
|
|
396
|
-
fetchAndParseAsJSONHelper<IOdspSnapshot>(url, fetchOptions));
|
|
397
|
-
const snapshotContents: ISnapshotContents = convertOdspSnapshotToSnapsohtTreeAndBlobs(response.content);
|
|
398
|
-
const finalSnapshotContents: IOdspResponse<ISnapshotContents> = { ...response, content: snapshotContents };
|
|
399
|
-
return {
|
|
400
|
-
odspSnapshotResponse: finalSnapshotContents,
|
|
401
|
-
requestHeaders: headers,
|
|
402
|
-
requestUrl: url,
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* This function fetches the binary compact snapshot format. This is an experimental feature
|
|
408
|
-
* and is behind a feature flag.
|
|
409
|
-
* @param odspResolvedUrl - resolved odsp url.
|
|
410
|
-
* @param storageToken - token to do the auth for network request.
|
|
411
|
-
* @param snapshotOptions - Options used to specify how and what to fetch in the snapshot.
|
|
412
|
-
* @param controller - abort controller if caller needs to abort the network call.
|
|
413
|
-
* @param epochTracker - epoch tracker used to add/validate epoch in the network call.
|
|
414
|
-
* @returns fetched snapshot.
|
|
415
|
-
*/
|
|
416
|
-
async function fetchSnapshotContentsCoreV2(
|
|
417
|
-
odspResolvedUrl: IOdspResolvedUrl,
|
|
418
|
-
storageToken: string,
|
|
419
|
-
snapshotOptions: ISnapshotOptions | undefined,
|
|
420
|
-
controller?: AbortController,
|
|
421
|
-
epochTracker?: EpochTracker,
|
|
422
|
-
): Promise<ISnapshotRequestAndResponseOptions> {
|
|
423
|
-
const fullUrl = `${odspResolvedUrl.siteUrl}/_api/v2.1/drives/${odspResolvedUrl.driveId}/items/${
|
|
424
|
-
odspResolvedUrl.itemId}/opStream/attachments/latest/content?ump=1`;
|
|
425
|
-
|
|
426
|
-
const { body, headers } = getFormBodyAndHeaders(odspResolvedUrl, storageToken, snapshotOptions);
|
|
427
|
-
const fetchOptions = {
|
|
428
|
-
body,
|
|
429
|
-
headers,
|
|
430
|
-
signal: controller?.signal,
|
|
431
|
-
method: "POST",
|
|
432
|
-
};
|
|
433
|
-
|
|
434
|
-
const response = await (epochTracker?.fetchArray(fullUrl, fetchOptions, "treesLatest", true) ??
|
|
435
|
-
fetchArray(fullUrl, fetchOptions));
|
|
436
|
-
const snapshotContents: ISnapshotContents = parseCompactSnapshotResponse(
|
|
437
|
-
new ReadBuffer(new Uint8Array(response.content)));
|
|
438
|
-
const finalSnapshotContents: IOdspResponse<ISnapshotContents> = { ...response, content: snapshotContents };
|
|
439
|
-
return {
|
|
440
|
-
odspSnapshotResponse: finalSnapshotContents,
|
|
441
|
-
requestHeaders: headers,
|
|
442
|
-
requestUrl: fullUrl,
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
|
|
446
365
|
function getFormBodyAndHeaders(
|
|
447
366
|
odspResolvedUrl: IOdspResolvedUrl,
|
|
448
367
|
storageToken: string,
|
|
@@ -503,6 +422,17 @@ function countTreesInSnapshotTree(snapshotTree: ISnapshotTree): number {
|
|
|
503
422
|
return numTrees;
|
|
504
423
|
}
|
|
505
424
|
|
|
425
|
+
/**
|
|
426
|
+
* This function fetches the snapshot and parse it according to what is mentioned in response headers.
|
|
427
|
+
* @param odspResolvedUrl - resolved odsp url.
|
|
428
|
+
* @param storageToken - token to do the auth for network request.
|
|
429
|
+
* @param snapshotOptions - Options used to specify how and what to fetch in the snapshot.
|
|
430
|
+
* @param logger - logger
|
|
431
|
+
* @param fetchBinarySnapshotFormat - whether to fetch binary snapshot or not.
|
|
432
|
+
* @param controller - abort controller if caller needs to abort the network call.
|
|
433
|
+
* @param epochTracker - epoch tracker used to add/validate epoch in the network call.
|
|
434
|
+
* @returns fetched snapshot.
|
|
435
|
+
*/
|
|
506
436
|
export async function downloadSnapshot(
|
|
507
437
|
odspResolvedUrl: IOdspResolvedUrl,
|
|
508
438
|
storageToken: string,
|
|
@@ -518,13 +448,53 @@ export async function downloadSnapshot(
|
|
|
518
448
|
odspResolvedUrl.shareLinkInfo = { ...odspResolvedUrl.shareLinkInfo, sharingLinkToRedeem };
|
|
519
449
|
}
|
|
520
450
|
|
|
451
|
+
const snapshotUrl = odspResolvedUrl.endpoints.snapshotStorageUrl;
|
|
452
|
+
const url = `${snapshotUrl}/trees/latest?ump=1`;
|
|
453
|
+
// The location of file can move on Spo in which case server returns 308(Permanent Redirect) error.
|
|
454
|
+
// Adding below header will make VROOM API return 404 instead of 308 and browser can intercept it.
|
|
455
|
+
// This error thrown by server will contain the new redirect location. Look at the 404 error parsing
|
|
456
|
+
// for futher reference here: \packages\utils\odsp-doclib-utils\src\odspErrorUtils.ts
|
|
457
|
+
const header = {prefer: "manualredirect"};
|
|
458
|
+
const { body, headers } = getFormBodyAndHeaders(
|
|
459
|
+
odspResolvedUrl, storageToken, snapshotOptions, header);
|
|
460
|
+
const fetchOptions = {
|
|
461
|
+
body,
|
|
462
|
+
headers,
|
|
463
|
+
signal: controller?.signal,
|
|
464
|
+
method: "POST",
|
|
465
|
+
};
|
|
466
|
+
// For now we are keeping both json and ms-fluid values in accept header while fetching as we want to let
|
|
467
|
+
// the server decide what format it wants to send to the client as we roll it out slowly.
|
|
521
468
|
if (fetchBinarySnapshotFormat) {
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
469
|
+
headers.accept = `application/json, application/ms-fluid; v=${currentReadVersion}`;
|
|
470
|
+
} else {
|
|
471
|
+
headers.accept = "application/json";
|
|
472
|
+
}
|
|
473
|
+
const response = await (epochTracker?.fetch(url, fetchOptions, "treesLatest", true) ??
|
|
474
|
+
fetchHelper(url, fetchOptions));
|
|
475
|
+
|
|
476
|
+
let finalSnapshotContents: IOdspResponse<ISnapshotContents>;
|
|
477
|
+
const contentType = response.headers.get("content-type");
|
|
478
|
+
if (contentType === "application/json") {
|
|
479
|
+
const text = await response.content.text();
|
|
480
|
+
const content: IOdspSnapshot = JSON.parse(text);
|
|
481
|
+
response.propsToLog.bodySize = text.length;
|
|
482
|
+
const snapshotContents: ISnapshotContents = convertOdspSnapshotToSnapsohtTreeAndBlobs(content);
|
|
483
|
+
finalSnapshotContents = { ...response, content: snapshotContents };
|
|
525
484
|
} else {
|
|
526
|
-
|
|
485
|
+
assert(contentType === "application/ms-fluid", 0x2c3 /* "Content type should be application/ms-fluid" */);
|
|
486
|
+
const content = await response.content.arrayBuffer();
|
|
487
|
+
response.propsToLog.bodySize = content.byteLength;
|
|
488
|
+
const snapshotContents: ISnapshotContents = parseCompactSnapshotResponse(
|
|
489
|
+
new ReadBuffer(new Uint8Array(content)));
|
|
490
|
+
finalSnapshotContents = { ...response, content: snapshotContents };
|
|
527
491
|
}
|
|
492
|
+
response.propsToLog.contentType = contentType;
|
|
493
|
+
return {
|
|
494
|
+
odspSnapshotResponse: finalSnapshotContents,
|
|
495
|
+
requestHeaders: headers,
|
|
496
|
+
requestUrl: url,
|
|
497
|
+
};
|
|
528
498
|
}
|
|
529
499
|
|
|
530
500
|
function isRedeemSharingLinkError(odspResolvedUrl: IOdspResolvedUrl, error: any) {
|
|
@@ -81,6 +81,7 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
81
81
|
hostPolicy: HostStoragePolicy,
|
|
82
82
|
epochTracker: EpochTracker,
|
|
83
83
|
socketReferenceKeyPrefix?: string,
|
|
84
|
+
clientIsSummarizer?: boolean,
|
|
84
85
|
): Promise<IDocumentService> {
|
|
85
86
|
return new OdspDocumentService(
|
|
86
87
|
getOdspResolvedUrl(resolvedUrl),
|
|
@@ -92,6 +93,7 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
92
93
|
hostPolicy,
|
|
93
94
|
epochTracker,
|
|
94
95
|
socketReferenceKeyPrefix,
|
|
96
|
+
clientIsSummarizer,
|
|
95
97
|
);
|
|
96
98
|
}
|
|
97
99
|
|
|
@@ -131,6 +133,7 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
131
133
|
hostPolicy: HostStoragePolicy,
|
|
132
134
|
private readonly epochTracker: EpochTracker,
|
|
133
135
|
private readonly socketReferenceKeyPrefix?: string,
|
|
136
|
+
private readonly clientIsSummarizer?: boolean,
|
|
134
137
|
) {
|
|
135
138
|
this._policies = {
|
|
136
139
|
// load in storage-only mode if a file version is specified
|
|
@@ -150,7 +153,7 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
150
153
|
this.hostPolicy = hostPolicy;
|
|
151
154
|
this.hostPolicy.fetchBinarySnapshotFormat ??=
|
|
152
155
|
this.mc.config.getBoolean("Fluid.Driver.Odsp.binaryFormatSnapshot");
|
|
153
|
-
if (this.
|
|
156
|
+
if (this.clientIsSummarizer) {
|
|
154
157
|
this.hostPolicy = { ...this.hostPolicy, summarizerClient: true };
|
|
155
158
|
}
|
|
156
159
|
}
|
|
@@ -385,7 +388,7 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
385
388
|
};
|
|
386
389
|
|
|
387
390
|
const getResponseAndRefreshAfterDeltaMs = async () => {
|
|
388
|
-
|
|
391
|
+
const _response = await this.cache.sessionJoinCache.addOrGet(this.joinSessionKey, executeFetch);
|
|
389
392
|
// If the response does not contain refreshSessionDurationSeconds, then treat it as old flow and let the
|
|
390
393
|
// cache entry to be treated as expired after 1 hour.
|
|
391
394
|
_response.joinSessionResponse.refreshSessionDurationSeconds =
|
|
@@ -415,16 +418,16 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
415
418
|
.catch((error) => {
|
|
416
419
|
this.mc.logger.sendErrorEvent({
|
|
417
420
|
eventName: "JoinSessionRefreshError",
|
|
418
|
-
|
|
421
|
+
details: JSON.stringify(props),
|
|
419
422
|
},
|
|
420
423
|
error,
|
|
421
424
|
);
|
|
422
425
|
});
|
|
423
426
|
} else {
|
|
424
427
|
// Logging just for informational purposes to help with debugging as this is a new feature.
|
|
425
|
-
this.mc.logger.
|
|
428
|
+
this.mc.logger.sendTelemetryEvent({
|
|
426
429
|
eventName: "JoinSessionRefreshNotScheduled",
|
|
427
|
-
|
|
430
|
+
details: JSON.stringify(props),
|
|
428
431
|
});
|
|
429
432
|
}
|
|
430
433
|
}
|
|
@@ -55,6 +55,7 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
|
|
|
55
55
|
createNewSummary: ISummaryTree | undefined,
|
|
56
56
|
createNewResolvedUrl: IResolvedUrl,
|
|
57
57
|
logger?: ITelemetryBaseLogger,
|
|
58
|
+
clientIsSummarizer?: boolean,
|
|
58
59
|
): Promise<IDocumentService> {
|
|
59
60
|
ensureFluidResolvedUrl(createNewResolvedUrl);
|
|
60
61
|
|
|
@@ -113,7 +114,8 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
|
|
|
113
114
|
this.hostPolicy.cacheCreateNewSummary ?? true,
|
|
114
115
|
!!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
|
|
115
116
|
);
|
|
116
|
-
const docService = this.createDocumentServiceCore(odspResolvedUrl, odspLogger,
|
|
117
|
+
const docService = this.createDocumentServiceCore(odspResolvedUrl, odspLogger,
|
|
118
|
+
cacheAndTracker, clientIsSummarizer);
|
|
117
119
|
event.end({
|
|
118
120
|
docId: odspResolvedUrl.hashedDocumentId,
|
|
119
121
|
});
|
|
@@ -147,14 +149,16 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
|
|
|
147
149
|
public async createDocumentService(
|
|
148
150
|
resolvedUrl: IResolvedUrl,
|
|
149
151
|
logger?: ITelemetryBaseLogger,
|
|
152
|
+
clientIsSummarizer?: boolean,
|
|
150
153
|
): Promise<IDocumentService> {
|
|
151
|
-
return this.createDocumentServiceCore(resolvedUrl, createOdspLogger(logger));
|
|
154
|
+
return this.createDocumentServiceCore(resolvedUrl, createOdspLogger(logger), undefined, clientIsSummarizer);
|
|
152
155
|
}
|
|
153
156
|
|
|
154
157
|
private async createDocumentServiceCore(
|
|
155
158
|
resolvedUrl: IResolvedUrl,
|
|
156
159
|
odspLogger: TelemetryLogger,
|
|
157
160
|
cacheAndTrackerArg?: ICacheAndTracker,
|
|
161
|
+
clientIsSummarizer?: boolean,
|
|
158
162
|
): Promise<IDocumentService> {
|
|
159
163
|
const odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);
|
|
160
164
|
const resolvedUrlData: IOdspUrlParts = {
|
|
@@ -194,6 +198,7 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
|
|
|
194
198
|
this.hostPolicy,
|
|
195
199
|
cacheAndTracker.epochTracker,
|
|
196
200
|
this.socketReferenceKeyPrefix,
|
|
201
|
+
clientIsSummarizer,
|
|
197
202
|
);
|
|
198
203
|
}
|
|
199
204
|
}
|
|
@@ -356,38 +356,20 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
356
356
|
if (!snapshotTree) {
|
|
357
357
|
return null;
|
|
358
358
|
}
|
|
359
|
-
// Decode commit paths
|
|
360
|
-
const commits = {};
|
|
361
359
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
let finalTree: api.ISnapshotTree;
|
|
367
|
-
// For container loaded from detach new summary, we will not have a commit for ".app" in downloaded summary as the client uploaded both
|
|
368
|
-
// ".app" and ".protocol" trees by itself. For other summaries, we will have ".app" as commit because client previously only uploaded the
|
|
369
|
-
// app summary.
|
|
370
|
-
if (commits && commits[".app"]) {
|
|
371
|
-
// The latest snapshot is a summary
|
|
372
|
-
// attempt to read .protocol from commits for backwards compat
|
|
373
|
-
finalTree = await this.readSummaryTree(commits[".protocol"] || snapshotTree.trees[".protocol"], commits[".app"] as string);
|
|
374
|
-
} else {
|
|
375
|
-
if (snapshotTree.blobs) {
|
|
376
|
-
const attributesBlob = snapshotTree.blobs.attributes;
|
|
377
|
-
if (attributesBlob) {
|
|
378
|
-
this.attributesBlobHandles.add(attributesBlob);
|
|
379
|
-
}
|
|
360
|
+
if (snapshotTree.blobs) {
|
|
361
|
+
const attributesBlob = snapshotTree.blobs.attributes;
|
|
362
|
+
if (attributesBlob) {
|
|
363
|
+
this.attributesBlobHandles.add(attributesBlob);
|
|
380
364
|
}
|
|
365
|
+
}
|
|
381
366
|
|
|
382
|
-
|
|
367
|
+
// When we upload the container snapshot, we upload appTree in ".app" and protocol tree in ".protocol"
|
|
368
|
+
// So when we request the snapshot we get ".app" as tree and not as commit node as in the case just above.
|
|
369
|
+
const appTree = snapshotTree.trees[".app"];
|
|
370
|
+
const protocolTree = snapshotTree.trees[".protocol"];
|
|
383
371
|
|
|
384
|
-
|
|
385
|
-
// So when we request the snapshot we get ".app" as tree and not as commit node as in the case just above.
|
|
386
|
-
const appTree = snapshotTree.trees[".app"];
|
|
387
|
-
const protocolTree = snapshotTree.trees[".protocol"];
|
|
388
|
-
finalTree = this.combineProtocolAndAppSnapshotTree(appTree, protocolTree);
|
|
389
|
-
}
|
|
390
|
-
return finalTree;
|
|
372
|
+
return this.combineProtocolAndAppSnapshotTree(appTree, protocolTree);
|
|
391
373
|
}
|
|
392
374
|
|
|
393
375
|
public async getVersions(blobid: string | null, count: number): Promise<api.IVersion[]> {
|
|
@@ -782,39 +764,6 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
782
764
|
return tree;
|
|
783
765
|
}
|
|
784
766
|
|
|
785
|
-
/**
|
|
786
|
-
* Reads a summary tree
|
|
787
|
-
* @param protocolTreeOrId - Protocol snapshot tree or id of the protocol tree
|
|
788
|
-
* @param appTreeId - Id of the app tree
|
|
789
|
-
*/
|
|
790
|
-
private async readSummaryTree(protocolTreeOrId: api.ISnapshotTree | string, appTreeId: string): Promise<api.ISnapshotTree> {
|
|
791
|
-
// Load the app and protocol trees and return them
|
|
792
|
-
let hierarchicalProtocolTree: api.ISnapshotTree | null;
|
|
793
|
-
if (typeof (protocolTreeOrId) === "string") {
|
|
794
|
-
// Backwards compat for older summaries
|
|
795
|
-
hierarchicalProtocolTree = await this.readTree(protocolTreeOrId);
|
|
796
|
-
} else {
|
|
797
|
-
hierarchicalProtocolTree = protocolTreeOrId;
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
const hierarchicalAppTree = await this.readTree(appTreeId);
|
|
801
|
-
if (!hierarchicalProtocolTree) {
|
|
802
|
-
throw new Error("Invalid protocol tree");
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
if (!hierarchicalAppTree) {
|
|
806
|
-
throw new Error("Invalid app tree");
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
if (hierarchicalProtocolTree.blobs) {
|
|
810
|
-
const attributesBlob = hierarchicalProtocolTree.blobs.attributes;
|
|
811
|
-
if (attributesBlob) {
|
|
812
|
-
this.attributesBlobHandles.add(attributesBlob);
|
|
813
|
-
}
|
|
814
|
-
}
|
|
815
|
-
return this.combineProtocolAndAppSnapshotTree(hierarchicalAppTree, hierarchicalProtocolTree);
|
|
816
|
-
}
|
|
817
|
-
|
|
818
767
|
private combineProtocolAndAppSnapshotTree(
|
|
819
768
|
hierarchicalAppTree: api.ISnapshotTree,
|
|
820
769
|
hierarchicalProtocolTree: api.ISnapshotTree,
|
|
@@ -823,9 +772,6 @@ export class OdspDocumentStorageService implements IDocumentStorageService {
|
|
|
823
772
|
blobs: {
|
|
824
773
|
...hierarchicalAppTree.blobs,
|
|
825
774
|
},
|
|
826
|
-
commits: {
|
|
827
|
-
...hierarchicalAppTree.commits,
|
|
828
|
-
},
|
|
829
775
|
trees: {
|
|
830
776
|
...hierarchicalAppTree.trees,
|
|
831
777
|
// the app tree could have a .protocol
|
|
@@ -2,21 +2,23 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
5
|
import { assert } from "@fluidframework/common-utils";
|
|
7
|
-
import {
|
|
6
|
+
import { IRequest } from "@fluidframework/core-interfaces";
|
|
8
7
|
import {
|
|
8
|
+
DriverErrorType,
|
|
9
9
|
DriverHeader,
|
|
10
10
|
IContainerPackageInfo,
|
|
11
11
|
IResolvedUrl,
|
|
12
12
|
IUrlResolver,
|
|
13
13
|
} from "@fluidframework/driver-definitions";
|
|
14
14
|
import { IOdspResolvedUrl, ShareLinkTypes, ShareLinkInfoType } from "@fluidframework/odsp-driver-definitions";
|
|
15
|
+
import { NonRetryableError } from "@fluidframework/driver-utils";
|
|
15
16
|
import { createOdspUrl } from "./createOdspUrl";
|
|
16
17
|
import { getApiRoot } from "./odspUrlHelper";
|
|
17
18
|
import { getOdspResolvedUrl } from "./odspUtils";
|
|
18
19
|
import { getHashedDocumentId } from "./odspPublicUtils";
|
|
19
20
|
import { ClpCompliantAppHeader } from "./contractsPublic";
|
|
21
|
+
import { pkgVersion } from "./packageVersion";
|
|
20
22
|
|
|
21
23
|
function getUrlBase(siteUrl: string, driveId: string, itemId: string, fileVersion?: string) {
|
|
22
24
|
const siteOrigin = new URL(siteUrl).origin;
|
|
@@ -74,7 +76,10 @@ export class OdspDriverUrlResolver implements IUrlResolver {
|
|
|
74
76
|
const packageName = searchParams.get("containerPackageName");
|
|
75
77
|
const createLinkType = searchParams.get("createLinkType");
|
|
76
78
|
if (!(fileName && siteURL && driveID && filePath !== null && filePath !== undefined)) {
|
|
77
|
-
throw new
|
|
79
|
+
throw new NonRetryableError(
|
|
80
|
+
"Proper new file params should be there!!",
|
|
81
|
+
DriverErrorType.genericError,
|
|
82
|
+
{ driverVersion: pkgVersion });
|
|
78
83
|
}
|
|
79
84
|
let shareLinkInfo: ShareLinkInfoType | undefined;
|
|
80
85
|
if(createLinkType && createLinkType in ShareLinkTypes) {
|
|
@@ -156,22 +161,26 @@ export class OdspDriverUrlResolver implements IUrlResolver {
|
|
|
156
161
|
public async getAbsoluteUrl(
|
|
157
162
|
resolvedUrl: IResolvedUrl,
|
|
158
163
|
relativeUrl: string,
|
|
159
|
-
packageInfoSource?: IContainerPackageInfo
|
|
164
|
+
packageInfoSource?: IContainerPackageInfo,
|
|
160
165
|
): Promise<string> {
|
|
161
166
|
let dataStorePath = relativeUrl;
|
|
162
167
|
if (dataStorePath.startsWith("/")) {
|
|
163
168
|
dataStorePath = dataStorePath.substr(1);
|
|
164
169
|
}
|
|
165
170
|
const odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);
|
|
166
|
-
|
|
167
|
-
|
|
171
|
+
// back-compat: GitHub #9653
|
|
172
|
+
const isFluidPackage = (pkg: any) =>
|
|
173
|
+
typeof pkg === "object"
|
|
174
|
+
&& typeof pkg?.name === "string"
|
|
175
|
+
&& typeof pkg?.fluid === "object";
|
|
168
176
|
let containerPackageName;
|
|
169
177
|
if (packageInfoSource && "name" in packageInfoSource) {
|
|
170
178
|
containerPackageName = packageInfoSource.name;
|
|
171
|
-
|
|
172
|
-
|
|
179
|
+
// packageInfoSource is cast to any as it is typed to IContainerPackageInfo instead of IFluidCodeDetails
|
|
180
|
+
} else if (isFluidPackage((packageInfoSource as any)?.package)) {
|
|
181
|
+
containerPackageName = (packageInfoSource as any)?.package.name;
|
|
173
182
|
} else {
|
|
174
|
-
containerPackageName = packageInfoSource?.package;
|
|
183
|
+
containerPackageName = (packageInfoSource as any)?.package;
|
|
175
184
|
}
|
|
176
185
|
containerPackageName = containerPackageName ?? odspResolvedUrl.codeHint?.containerPackageName;
|
|
177
186
|
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
5
|
import { PromiseCache } from "@fluidframework/common-utils";
|
|
7
|
-
import {
|
|
6
|
+
import { IRequest } from "@fluidframework/core-interfaces";
|
|
8
7
|
import {
|
|
9
8
|
IContainerPackageInfo,
|
|
10
9
|
IResolvedUrl,
|
|
@@ -191,20 +190,24 @@ export class OdspDriverUrlResolverForShareLink implements IUrlResolver {
|
|
|
191
190
|
public async getAbsoluteUrl(
|
|
192
191
|
resolvedUrl: IResolvedUrl,
|
|
193
192
|
dataStorePath: string,
|
|
194
|
-
packageInfoSource?: IContainerPackageInfo
|
|
193
|
+
packageInfoSource?: IContainerPackageInfo,
|
|
195
194
|
): Promise<string> {
|
|
196
195
|
const odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);
|
|
197
196
|
const shareLink = await this.getShareLinkPromise(odspResolvedUrl);
|
|
198
197
|
const shareLinkUrl = new URL(shareLink);
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
// back-compat: GitHub #9653
|
|
199
|
+
const isFluidPackage = (pkg: any) =>
|
|
200
|
+
typeof pkg === "object"
|
|
201
|
+
&& typeof pkg?.name === "string"
|
|
202
|
+
&& typeof pkg?.fluid === "object";
|
|
201
203
|
let containerPackageName;
|
|
202
204
|
if (packageInfoSource && "name" in packageInfoSource) {
|
|
203
205
|
containerPackageName = packageInfoSource.name;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
+
// packageInfoSource is cast to any as it is typed to IContainerPackageInfo instead of IFluidCodeDetails
|
|
207
|
+
} else if (isFluidPackage((packageInfoSource as any)?.package)) {
|
|
208
|
+
containerPackageName = (packageInfoSource as any)?.package.name;
|
|
206
209
|
} else {
|
|
207
|
-
containerPackageName = packageInfoSource?.package;
|
|
210
|
+
containerPackageName = (packageInfoSource as any)?.package;
|
|
208
211
|
}
|
|
209
212
|
containerPackageName = containerPackageName ?? odspResolvedUrl.codeHint?.containerPackageName;
|
|
210
213
|
|
|
@@ -18,7 +18,7 @@ import { ISnapshotContents } from "./odspUtils";
|
|
|
18
18
|
function buildHierarchy(flatTree: IOdspSnapshotCommit): api.ISnapshotTree {
|
|
19
19
|
const lookup: { [path: string]: api.ISnapshotTree } = {};
|
|
20
20
|
// id is required for root tree as it will be used to determine the version we loaded from.
|
|
21
|
-
const root: api.ISnapshotTree = { id: flatTree.id, blobs: {},
|
|
21
|
+
const root: api.ISnapshotTree = { id: flatTree.id, blobs: {}, trees: {} };
|
|
22
22
|
lookup[""] = root;
|
|
23
23
|
|
|
24
24
|
for (const entry of flatTree.entries) {
|
|
@@ -33,7 +33,6 @@ function buildHierarchy(flatTree: IOdspSnapshotCommit): api.ISnapshotTree {
|
|
|
33
33
|
if (entry.type === "tree") {
|
|
34
34
|
const newTree: api.ISnapshotTree = {
|
|
35
35
|
blobs: {},
|
|
36
|
-
commits: {},
|
|
37
36
|
trees: {},
|
|
38
37
|
unreferenced: entry.unreferenced,
|
|
39
38
|
};
|
|
@@ -41,8 +40,6 @@ function buildHierarchy(flatTree: IOdspSnapshotCommit): api.ISnapshotTree {
|
|
|
41
40
|
lookup[entry.path] = newTree;
|
|
42
41
|
} else if (entry.type === "blob") {
|
|
43
42
|
node.blobs[decodeURIComponent(entryPathBase)] = entry.id;
|
|
44
|
-
} else if (entry.type === "commit") {
|
|
45
|
-
node.commits[decodeURIComponent(entryPathBase)] = entry.id;
|
|
46
43
|
}
|
|
47
44
|
}
|
|
48
45
|
|
|
@@ -69,11 +69,13 @@ export class OdspSummaryUploadManager {
|
|
|
69
69
|
referenceSequenceNumber: number,
|
|
70
70
|
tree: api.ISummaryTree,
|
|
71
71
|
): Promise<IWriteSummaryResponse> {
|
|
72
|
+
const enableContainerTypeSummaryUpload = this.mc.config.getBoolean("Fluid.Driver.Odsp.EnableContainerTypeSummaryUpload");
|
|
73
|
+
const containsProtocolTree = enableContainerTypeSummaryUpload &&
|
|
74
|
+
Object.keys(tree.tree).includes(".protocol");
|
|
72
75
|
const { snapshotTree, blobs } = await this.convertSummaryToSnapshotTree(
|
|
73
76
|
parentHandle,
|
|
74
77
|
tree,
|
|
75
78
|
".app",
|
|
76
|
-
"",
|
|
77
79
|
);
|
|
78
80
|
const snapshot: IOdspSummaryPayload = {
|
|
79
81
|
entries: snapshotTree.entries!,
|
|
@@ -81,7 +83,7 @@ export class OdspSummaryUploadManager {
|
|
|
81
83
|
sequenceNumber: referenceSequenceNumber,
|
|
82
84
|
// no ack handle implies this is initial summary after empty file creation.
|
|
83
85
|
// send container payload so server will use it without a summary op
|
|
84
|
-
type: parentHandle === undefined ? "container" : "channel",
|
|
86
|
+
type: containsProtocolTree || parentHandle === undefined ? "container" : "channel",
|
|
85
87
|
};
|
|
86
88
|
|
|
87
89
|
return getWithRetryForTokenRefresh(async (options) => {
|
|
@@ -109,6 +111,8 @@ export class OdspSummaryUploadManager {
|
|
|
109
111
|
blobs,
|
|
110
112
|
size: postBody.length,
|
|
111
113
|
referenceSequenceNumber,
|
|
114
|
+
type: snapshot.type,
|
|
115
|
+
enableContainerTypeSummaryUpload,
|
|
112
116
|
},
|
|
113
117
|
async () => {
|
|
114
118
|
const response = await this.epochTracker.fetchAndParseAsJSON<IWriteSummaryResponse>(
|
|
@@ -137,7 +141,6 @@ export class OdspSummaryUploadManager {
|
|
|
137
141
|
parentHandle: string | undefined,
|
|
138
142
|
tree: api.ISummaryTree,
|
|
139
143
|
rootNodeName: string,
|
|
140
|
-
path: string = "",
|
|
141
144
|
markUnreferencedNodes: boolean = this.mc.config.getBoolean("Fluid.Driver.Odsp.MarkUnreferencedNodes") ?? true,
|
|
142
145
|
) {
|
|
143
146
|
const snapshotTree: IOdspSummaryTree = {
|
|
@@ -157,14 +160,12 @@ export class OdspSummaryUploadManager {
|
|
|
157
160
|
// property is not present, the tree entry is considered referenced. If the property is present and is
|
|
158
161
|
// true (which is the only value it can have), the tree entry is considered unreferenced.
|
|
159
162
|
let unreferenced: true | undefined;
|
|
160
|
-
const currentPath = path === "" ? `${rootNodeName}/${key}` : `${path}/${key}`;
|
|
161
163
|
switch (summaryObject.type) {
|
|
162
164
|
case api.SummaryType.Tree: {
|
|
163
165
|
const result = await this.convertSummaryToSnapshotTree(
|
|
164
166
|
parentHandle,
|
|
165
167
|
summaryObject,
|
|
166
|
-
rootNodeName
|
|
167
|
-
currentPath);
|
|
168
|
+
rootNodeName);
|
|
168
169
|
value = result.snapshotTree;
|
|
169
170
|
unreferenced = markUnreferencedNodes ? summaryObject.unreferenced : undefined;
|
|
170
171
|
blobs += result.blobs;
|
package/src/packageVersion.ts
CHANGED