@fluidframework/odsp-driver 2.13.0 → 2.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/WriteBufferUtils.js.map +1 -1
  3. package/dist/compactSnapshotParser.js +5 -5
  4. package/dist/compactSnapshotParser.js.map +1 -1
  5. package/dist/fetchSnapshot.d.ts.map +1 -1
  6. package/dist/fetchSnapshot.js +8 -3
  7. package/dist/fetchSnapshot.js.map +1 -1
  8. package/dist/getFileLink.js +3 -3
  9. package/dist/getFileLink.js.map +1 -1
  10. package/dist/odspDelayLoadedDeltaStream.d.ts +2 -1
  11. package/dist/odspDelayLoadedDeltaStream.d.ts.map +1 -1
  12. package/dist/odspDelayLoadedDeltaStream.js +10 -9
  13. package/dist/odspDelayLoadedDeltaStream.js.map +1 -1
  14. package/dist/odspDocumentStorageServiceBase.d.ts.map +1 -1
  15. package/dist/odspDocumentStorageServiceBase.js.map +1 -1
  16. package/dist/odspDriverUrlResolver.js +1 -1
  17. package/dist/odspDriverUrlResolver.js.map +1 -1
  18. package/dist/package.json +2 -1
  19. package/dist/packageVersion.d.ts +1 -1
  20. package/dist/packageVersion.js +1 -1
  21. package/dist/packageVersion.js.map +1 -1
  22. package/dist/vroom.d.ts +4 -3
  23. package/dist/vroom.d.ts.map +1 -1
  24. package/dist/vroom.js +12 -7
  25. package/dist/vroom.js.map +1 -1
  26. package/lib/WriteBufferUtils.js.map +1 -1
  27. package/lib/compactSnapshotParser.js +5 -5
  28. package/lib/compactSnapshotParser.js.map +1 -1
  29. package/lib/fetchSnapshot.d.ts.map +1 -1
  30. package/lib/fetchSnapshot.js +8 -3
  31. package/lib/fetchSnapshot.js.map +1 -1
  32. package/lib/getFileLink.js +3 -3
  33. package/lib/getFileLink.js.map +1 -1
  34. package/lib/odspDelayLoadedDeltaStream.d.ts +2 -1
  35. package/lib/odspDelayLoadedDeltaStream.d.ts.map +1 -1
  36. package/lib/odspDelayLoadedDeltaStream.js +10 -9
  37. package/lib/odspDelayLoadedDeltaStream.js.map +1 -1
  38. package/lib/odspDocumentStorageServiceBase.d.ts.map +1 -1
  39. package/lib/odspDocumentStorageServiceBase.js.map +1 -1
  40. package/lib/odspDriverUrlResolver.js +1 -1
  41. package/lib/odspDriverUrlResolver.js.map +1 -1
  42. package/lib/packageVersion.d.ts +1 -1
  43. package/lib/packageVersion.js +1 -1
  44. package/lib/packageVersion.js.map +1 -1
  45. package/lib/vroom.d.ts +4 -3
  46. package/lib/vroom.d.ts.map +1 -1
  47. package/lib/vroom.js +12 -7
  48. package/lib/vroom.js.map +1 -1
  49. package/package.json +12 -12
  50. package/src/WriteBufferUtils.ts +2 -2
  51. package/src/compactSnapshotParser.ts +5 -5
  52. package/src/fetchSnapshot.ts +7 -8
  53. package/src/getFileLink.ts +2 -3
  54. package/src/odspDelayLoadedDeltaStream.ts +11 -2
  55. package/src/odspDocumentStorageServiceBase.ts +2 -3
  56. package/src/odspDriverUrlResolver.ts +1 -1
  57. package/src/packageVersion.ts +1 -1
  58. package/src/vroom.ts +14 -8
@@ -150,13 +150,8 @@ export async function fetchSnapshotWithRedeem(
150
150
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
151
151
  if (enableRedeemFallback && isRedeemSharingLinkError(odspResolvedUrl, error)) {
152
152
  // Execute the redeem fallback
153
+ await redeemSharingLink(odspResolvedUrl, storageTokenFetcher, logger);
153
154
 
154
- await redeemSharingLink(
155
- odspResolvedUrl,
156
- storageTokenFetcher,
157
- logger,
158
- forceAccessTokenViaAuthorizationHeader,
159
- );
160
155
  const odspResolvedUrlWithoutShareLink: IOdspResolvedUrl = {
161
156
  ...odspResolvedUrl,
162
157
  shareLinkInfo: {
@@ -213,7 +208,6 @@ async function redeemSharingLink(
213
208
  odspResolvedUrl: IOdspResolvedUrl,
214
209
  getAuthHeader: InstrumentedStorageTokenFetcher,
215
210
  logger: ITelemetryLoggerExt,
216
- forceAccessTokenViaAuthorizationHeader: boolean,
217
211
  ): Promise<void> {
218
212
  await PerformanceEvent.timedExecAsync(
219
213
  logger,
@@ -233,7 +227,12 @@ async function redeemSharingLink(
233
227
  let redeemUrl: string | undefined;
234
228
  async function callSharesAPI(baseUrl: string): Promise<void> {
235
229
  await getWithRetryForTokenRefresh(async (tokenFetchOptions) => {
236
- redeemUrl = `${baseUrl}/_api/v2.0/shares/${encodedShareUrl}`;
230
+ // IMPORTANT: Note that redeemUrl has '/driveItem' in it. Technically it is not required for executing redeem operation.
231
+ // However, we have other cases that use '/shares' API and do require to specify '/driveItem' in order to get specific
232
+ // drive item properties. The reason this matters is when caller of this API must possess logical permissions to call
233
+ // this API (for instance, this will be the case when call is made with app-only token) then two separate logical
234
+ // permissions are needed for the '/shares' call with and without '/driveItem'.
235
+ redeemUrl = `${baseUrl}/_api/v2.0/shares/${encodedShareUrl}/driveItem`;
237
236
  const url = redeemUrl;
238
237
  const method = "GET";
239
238
  const authHeader = await getAuthHeader(
@@ -102,7 +102,7 @@ export async function getFileLink(
102
102
  }
103
103
 
104
104
  /**
105
- * Handles location redirection while fulfilling the getFilelink call. We don't want browser to handle
105
+ * Handles location redirection while fulfilling the getFileLink call. We don't want browser to handle
106
106
  * the redirection as the browser will retry with same auth token which will not work as we need app
107
107
  * to regenerate tokens for the new site domain. So when we will make the network calls below we will set
108
108
  * the redirect:manual header to manually handle these redirects.
@@ -125,7 +125,7 @@ async function getFileLinkWithLocationRedirectionHandling(
125
125
  let locationRedirected = false;
126
126
  for (let count = 1; count <= 5; count++) {
127
127
  try {
128
- const fileItem = await getFileItemLite(getToken, resolvedUrl, logger, true);
128
+ const fileItem = await getFileItemLite(getToken, resolvedUrl, logger);
129
129
  // Sometimes the siteUrl in the actual file is different from the siteUrl in the resolvedUrl due to location
130
130
  // redirection. This creates issues in the getSharingInformation call. So we need to update the siteUrl in the
131
131
  // resolvedUrl to the siteUrl in the fileItem which is the updated siteUrl.
@@ -260,7 +260,6 @@ async function getFileItemLite(
260
260
  getToken: TokenFetcher<OdspResourceTokenFetchOptions>,
261
261
  odspUrlParts: IOdspUrlParts,
262
262
  logger: ITelemetryLoggerExt,
263
- forceAccessTokenViaAuthorizationHeader: boolean,
264
263
  ): Promise<FileItemLite> {
265
264
  return PerformanceEvent.timedExecAsync(
266
265
  logger,
@@ -91,7 +91,7 @@ export class OdspDelayLoadedDeltaStream {
91
91
  | undefined,
92
92
  private readonly mc: MonitoringContext,
93
93
  private readonly cache: IOdspCache,
94
- _hostPolicy: HostStoragePolicy,
94
+ private readonly 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,
@@ -156,6 +156,8 @@ export class OdspDelayLoadedDeltaStream {
156
156
  requestWebsocketTokenFromJoinSession,
157
157
  options,
158
158
  false /* isRefreshingJoinSession */,
159
+ undefined /* clientId */,
160
+ this.hostPolicy.sessionOptions?.displayName,
159
161
  );
160
162
  const [websocketEndpoint, websocketToken] = await Promise.all([
161
163
  joinSessionPromise.catch(annotateAndRethrowConnectionError("joinSession")),
@@ -273,6 +275,7 @@ export class OdspDelayLoadedDeltaStream {
273
275
  delta: number,
274
276
  requestSocketToken: boolean,
275
277
  clientId: string | undefined,
278
+ displayName: string | undefined,
276
279
  ): Promise<void> {
277
280
  if (this.joinSessionRefreshTimer !== undefined) {
278
281
  this.clearJoinSessionTimer();
@@ -301,6 +304,7 @@ export class OdspDelayLoadedDeltaStream {
301
304
  options,
302
305
  true /* isRefreshingJoinSession */,
303
306
  clientId,
307
+ displayName,
304
308
  );
305
309
  resolve();
306
310
  }).catch((error) => {
@@ -314,7 +318,8 @@ export class OdspDelayLoadedDeltaStream {
314
318
  requestSocketToken: boolean,
315
319
  options: TokenFetchOptionsEx,
316
320
  isRefreshingJoinSession: boolean,
317
- clientId?: string,
321
+ clientId: string | undefined,
322
+ displayName: string | undefined,
318
323
  ): Promise<ISocketStorageDiscovery> {
319
324
  // If this call is to refresh the join session for the current connection but we are already disconnected in
320
325
  // the meantime or disconnected and then reconnected then do not make the call. However, we should not have
@@ -342,6 +347,7 @@ export class OdspDelayLoadedDeltaStream {
342
347
  requestSocketToken,
343
348
  options,
344
349
  isRefreshingJoinSession,
350
+ displayName,
345
351
  ).catch((error) => {
346
352
  if (hasFacetCodes(error) && error.facetCodes !== undefined) {
347
353
  for (const code of error.facetCodes) {
@@ -378,6 +384,7 @@ export class OdspDelayLoadedDeltaStream {
378
384
  requestSocketToken: boolean,
379
385
  options: TokenFetchOptionsEx,
380
386
  isRefreshingJoinSession: boolean,
387
+ displayName: string | undefined,
381
388
  ): Promise<ISocketStorageDiscovery> {
382
389
  const disableJoinSessionRefresh = this.mc.config.getBoolean(
383
390
  "Fluid.Driver.Odsp.disableJoinSessionRefresh",
@@ -397,6 +404,7 @@ export class OdspDelayLoadedDeltaStream {
397
404
  options,
398
405
  disableJoinSessionRefresh,
399
406
  isRefreshingJoinSession,
407
+ displayName,
400
408
  );
401
409
  // Emit event only in case it is fetched from the network.
402
410
  if (joinSessionResponse.sensitivityLabelsInfo !== undefined) {
@@ -450,6 +458,7 @@ export class OdspDelayLoadedDeltaStream {
450
458
  response.refreshAfterDeltaMs,
451
459
  requestSocketToken,
452
460
  this.currentConnection?.clientId,
461
+ displayName,
453
462
  ).catch((error) => {
454
463
  // Log the error and do nothing as the reconnection would fetch the join session.
455
464
  this.mc.logger.sendTelemetryEvent(
@@ -257,9 +257,8 @@ export abstract class OdspDocumentStorageServiceBase implements IDocumentStorage
257
257
  protected combineProtocolAndAppSnapshotTree(snapshotTree: ISnapshotTree): ISnapshotTree {
258
258
  // When we upload the container snapshot, we upload appTree in ".app" and protocol tree in ".protocol"
259
259
  // So when we request the snapshot we get ".app" as tree and not as commit node as in the case just above.
260
- const hierarchicalAppTree: ISnapshotTree | undefined = snapshotTree.trees[".app"];
261
- const hierarchicalProtocolTree: ISnapshotTree | undefined =
262
- snapshotTree.trees[".protocol"];
260
+ const hierarchicalAppTree = snapshotTree.trees[".app"];
261
+ const hierarchicalProtocolTree = snapshotTree.trees[".protocol"];
263
262
  const summarySnapshotTree: ISnapshotTree = {
264
263
  blobs: {
265
264
  ...hierarchicalAppTree.blobs,
@@ -108,7 +108,7 @@ export class OdspDriverUrlResolver implements IUrlResolver {
108
108
 
109
109
  const searchParams = new URLSearchParams(queryString);
110
110
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
111
- const fileName: string = request.headers[DriverHeader.createNew]?.fileName;
111
+ const fileName: string = request.headers[DriverHeader.createNew].fileName;
112
112
  const driveID = searchParams.get("driveId");
113
113
  const filePath = searchParams.get("path");
114
114
  const packageName = searchParams.get("containerPackageName");
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/odsp-driver";
9
- export const pkgVersion = "2.13.0";
9
+ export const pkgVersion = "2.20.0";
package/src/vroom.ts CHANGED
@@ -21,8 +21,8 @@ import { TokenFetchOptionsEx } from "./odspUtils.js";
21
21
  import { runWithRetry } from "./retryUtils.js";
22
22
 
23
23
  interface IJoinSessionBody {
24
- requestSocketToken: boolean;
25
- guestDisplayName?: string;
24
+ requestSocketToken?: boolean;
25
+ displayName?: string;
26
26
  }
27
27
 
28
28
  /**
@@ -38,8 +38,9 @@ interface IJoinSessionBody {
38
38
  * @param options - Options to fetch the token.
39
39
  * @param disableJoinSessionRefresh - Whether the caller wants to disable refreshing join session periodically.
40
40
  * @param isRefreshingJoinSession - whether call is to refresh the session before expiry.
41
- * @param guestDisplayName - display name used to identify guest user joining a session.
42
- * This is optional and used only when collab session is being joined via invite.
41
+ * @param displayName - display name used to identify client joining a session.
42
+ * This is optional and used only when collab session is being joined by client acting in app-only mode (i.e. without user context).
43
+ * If not specified client display name is extracted from the access token that is used to join session.
43
44
  */
44
45
  export async function fetchJoinSession(
45
46
  urlParts: IOdspUrlParts,
@@ -52,6 +53,7 @@ export async function fetchJoinSession(
52
53
  options: TokenFetchOptionsEx,
53
54
  disableJoinSessionRefresh: boolean | undefined,
54
55
  isRefreshingJoinSession: boolean,
56
+ displayName: string | undefined,
55
57
  ): Promise<ISocketStorageDiscovery> {
56
58
  const apiRoot = getApiRoot(new URL(urlParts.siteUrl));
57
59
  const url = `${apiRoot}/drives/${urlParts.driveId}/items/${urlParts.itemId}/${path}?ump=1`;
@@ -89,11 +91,15 @@ export async function fetchJoinSession(
89
91
  }
90
92
  postBody += `_post: 1\r\n`;
91
93
 
94
+ let requestBody: IJoinSessionBody | undefined;
92
95
  if (requestSocketToken) {
93
- const body: IJoinSessionBody = {
94
- requestSocketToken: true,
95
- };
96
- postBody += `\r\n${JSON.stringify(body)}\r\n`;
96
+ requestBody = { ...requestBody, requestSocketToken: true };
97
+ }
98
+ if (displayName) {
99
+ requestBody = { ...requestBody, displayName };
100
+ }
101
+ if (requestBody) {
102
+ postBody += `\r\n${JSON.stringify(requestBody)}\r\n`;
97
103
  }
98
104
  postBody += `\r\n--${formBoundary}--`;
99
105
  const headers: { [index: string]: string } = {