@fluidframework/odsp-driver 1.2.7 → 2.0.0-dev.1.3.0.96595

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 (254) hide show
  1. package/.mocharc.js +12 -0
  2. package/README.md +19 -0
  3. package/dist/ReadBufferUtils.d.ts.map +1 -1
  4. package/dist/ReadBufferUtils.js +1 -0
  5. package/dist/ReadBufferUtils.js.map +1 -1
  6. package/dist/WriteBufferUtils.d.ts +3 -5
  7. package/dist/WriteBufferUtils.d.ts.map +1 -1
  8. package/dist/WriteBufferUtils.js +59 -55
  9. package/dist/WriteBufferUtils.js.map +1 -1
  10. package/dist/compactSnapshotParser.d.ts +2 -2
  11. package/dist/compactSnapshotParser.d.ts.map +1 -1
  12. package/dist/compactSnapshotParser.js +91 -34
  13. package/dist/compactSnapshotParser.js.map +1 -1
  14. package/dist/compactSnapshotWriter.d.ts +1 -2
  15. package/dist/compactSnapshotWriter.d.ts.map +1 -1
  16. package/dist/compactSnapshotWriter.js +17 -14
  17. package/dist/compactSnapshotWriter.js.map +1 -1
  18. package/dist/contracts.d.ts +1 -18
  19. package/dist/contracts.d.ts.map +1 -1
  20. package/dist/contracts.js.map +1 -1
  21. package/dist/createFile.d.ts +1 -1
  22. package/dist/createFile.d.ts.map +1 -1
  23. package/dist/createFile.js +53 -16
  24. package/dist/createFile.js.map +1 -1
  25. package/dist/createNewUtils.d.ts.map +1 -1
  26. package/dist/createNewUtils.js +0 -1
  27. package/dist/createNewUtils.js.map +1 -1
  28. package/dist/createOdspCreateContainerRequest.d.ts +4 -3
  29. package/dist/createOdspCreateContainerRequest.d.ts.map +1 -1
  30. package/dist/createOdspCreateContainerRequest.js +6 -3
  31. package/dist/createOdspCreateContainerRequest.js.map +1 -1
  32. package/dist/epochTracker.d.ts +1 -0
  33. package/dist/epochTracker.d.ts.map +1 -1
  34. package/dist/epochTracker.js +24 -5
  35. package/dist/epochTracker.js.map +1 -1
  36. package/dist/fetchSnapshot.d.ts +1 -2
  37. package/dist/fetchSnapshot.d.ts.map +1 -1
  38. package/dist/fetchSnapshot.js +22 -27
  39. package/dist/fetchSnapshot.js.map +1 -1
  40. package/dist/getFileLink.d.ts +3 -6
  41. package/dist/getFileLink.d.ts.map +1 -1
  42. package/dist/getFileLink.js +22 -33
  43. package/dist/getFileLink.js.map +1 -1
  44. package/dist/index.d.ts +1 -2
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +3 -3
  47. package/dist/index.js.map +1 -1
  48. package/dist/localOdspDriver/localOdspDocumentStorageManager.d.ts.map +1 -1
  49. package/dist/localOdspDriver/localOdspDocumentStorageManager.js +1 -2
  50. package/dist/localOdspDriver/localOdspDocumentStorageManager.js.map +1 -1
  51. package/dist/odspDeltaStorageService.d.ts +3 -2
  52. package/dist/odspDeltaStorageService.d.ts.map +1 -1
  53. package/dist/odspDeltaStorageService.js +9 -12
  54. package/dist/odspDeltaStorageService.js.map +1 -1
  55. package/dist/odspDocumentDeltaConnection.d.ts.map +1 -1
  56. package/dist/odspDocumentDeltaConnection.js +3 -6
  57. package/dist/odspDocumentDeltaConnection.js.map +1 -1
  58. package/dist/odspDocumentService.d.ts +1 -0
  59. package/dist/odspDocumentService.d.ts.map +1 -1
  60. package/dist/odspDocumentService.js +12 -6
  61. package/dist/odspDocumentService.js.map +1 -1
  62. package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  63. package/dist/odspDocumentServiceFactoryCore.js +29 -6
  64. package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
  65. package/dist/odspDocumentStorageManager.d.ts +4 -4
  66. package/dist/odspDocumentStorageManager.d.ts.map +1 -1
  67. package/dist/odspDocumentStorageManager.js +82 -64
  68. package/dist/odspDocumentStorageManager.js.map +1 -1
  69. package/dist/odspDocumentStorageServiceBase.d.ts +1 -1
  70. package/dist/odspDocumentStorageServiceBase.d.ts.map +1 -1
  71. package/dist/odspDocumentStorageServiceBase.js +4 -2
  72. package/dist/odspDocumentStorageServiceBase.js.map +1 -1
  73. package/dist/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
  74. package/dist/odspDriverUrlResolverForShareLink.js +4 -4
  75. package/dist/odspDriverUrlResolverForShareLink.js.map +1 -1
  76. package/dist/odspFluidFileLink.js +1 -1
  77. package/dist/odspFluidFileLink.js.map +1 -1
  78. package/dist/odspLocationRedirection.d.ts +14 -0
  79. package/dist/odspLocationRedirection.d.ts.map +1 -0
  80. package/dist/odspLocationRedirection.js +24 -0
  81. package/dist/odspLocationRedirection.js.map +1 -0
  82. package/dist/odspSnapshotParser.d.ts.map +1 -1
  83. package/dist/odspSnapshotParser.js +1 -2
  84. package/dist/odspSnapshotParser.js.map +1 -1
  85. package/dist/odspSummaryUploadManager.d.ts +6 -3
  86. package/dist/odspSummaryUploadManager.d.ts.map +1 -1
  87. package/dist/odspSummaryUploadManager.js +14 -17
  88. package/dist/odspSummaryUploadManager.js.map +1 -1
  89. package/dist/odspUrlHelper.js +2 -1
  90. package/dist/odspUrlHelper.js.map +1 -1
  91. package/dist/odspUtils.d.ts +11 -2
  92. package/dist/odspUtils.d.ts.map +1 -1
  93. package/dist/odspUtils.js +32 -5
  94. package/dist/odspUtils.js.map +1 -1
  95. package/dist/opsCaching.d.ts.map +1 -1
  96. package/dist/opsCaching.js +3 -2
  97. package/dist/opsCaching.js.map +1 -1
  98. package/dist/packageVersion.d.ts +1 -1
  99. package/dist/packageVersion.d.ts.map +1 -1
  100. package/dist/packageVersion.js +1 -1
  101. package/dist/packageVersion.js.map +1 -1
  102. package/dist/prefetchLatestSnapshot.d.ts +6 -4
  103. package/dist/prefetchLatestSnapshot.d.ts.map +1 -1
  104. package/dist/prefetchLatestSnapshot.js +6 -4
  105. package/dist/prefetchLatestSnapshot.js.map +1 -1
  106. package/dist/retryUtils.d.ts.map +1 -1
  107. package/dist/retryUtils.js +8 -4
  108. package/dist/retryUtils.js.map +1 -1
  109. package/dist/zipItDataRepresentationUtils.d.ts +30 -18
  110. package/dist/zipItDataRepresentationUtils.d.ts.map +1 -1
  111. package/dist/zipItDataRepresentationUtils.js +170 -76
  112. package/dist/zipItDataRepresentationUtils.js.map +1 -1
  113. package/lib/ReadBufferUtils.d.ts.map +1 -1
  114. package/lib/ReadBufferUtils.js +1 -0
  115. package/lib/ReadBufferUtils.js.map +1 -1
  116. package/lib/WriteBufferUtils.d.ts +3 -5
  117. package/lib/WriteBufferUtils.d.ts.map +1 -1
  118. package/lib/WriteBufferUtils.js +61 -57
  119. package/lib/WriteBufferUtils.js.map +1 -1
  120. package/lib/compactSnapshotParser.d.ts +2 -2
  121. package/lib/compactSnapshotParser.d.ts.map +1 -1
  122. package/lib/compactSnapshotParser.js +92 -35
  123. package/lib/compactSnapshotParser.js.map +1 -1
  124. package/lib/compactSnapshotWriter.d.ts +1 -2
  125. package/lib/compactSnapshotWriter.d.ts.map +1 -1
  126. package/lib/compactSnapshotWriter.js +18 -15
  127. package/lib/compactSnapshotWriter.js.map +1 -1
  128. package/lib/contracts.d.ts +1 -18
  129. package/lib/contracts.d.ts.map +1 -1
  130. package/lib/contracts.js.map +1 -1
  131. package/lib/createFile.d.ts +1 -1
  132. package/lib/createFile.d.ts.map +1 -1
  133. package/lib/createFile.js +54 -17
  134. package/lib/createFile.js.map +1 -1
  135. package/lib/createNewUtils.d.ts.map +1 -1
  136. package/lib/createNewUtils.js +0 -1
  137. package/lib/createNewUtils.js.map +1 -1
  138. package/lib/createOdspCreateContainerRequest.d.ts +4 -3
  139. package/lib/createOdspCreateContainerRequest.d.ts.map +1 -1
  140. package/lib/createOdspCreateContainerRequest.js +6 -3
  141. package/lib/createOdspCreateContainerRequest.js.map +1 -1
  142. package/lib/epochTracker.d.ts +1 -0
  143. package/lib/epochTracker.d.ts.map +1 -1
  144. package/lib/epochTracker.js +26 -7
  145. package/lib/epochTracker.js.map +1 -1
  146. package/lib/fetchSnapshot.d.ts +1 -2
  147. package/lib/fetchSnapshot.d.ts.map +1 -1
  148. package/lib/fetchSnapshot.js +22 -27
  149. package/lib/fetchSnapshot.js.map +1 -1
  150. package/lib/getFileLink.d.ts +3 -6
  151. package/lib/getFileLink.d.ts.map +1 -1
  152. package/lib/getFileLink.js +24 -35
  153. package/lib/getFileLink.js.map +1 -1
  154. package/lib/index.d.ts +1 -2
  155. package/lib/index.d.ts.map +1 -1
  156. package/lib/index.js +1 -3
  157. package/lib/index.js.map +1 -1
  158. package/lib/localOdspDriver/localOdspDocumentStorageManager.d.ts.map +1 -1
  159. package/lib/localOdspDriver/localOdspDocumentStorageManager.js +1 -2
  160. package/lib/localOdspDriver/localOdspDocumentStorageManager.js.map +1 -1
  161. package/lib/odspDeltaStorageService.d.ts +3 -2
  162. package/lib/odspDeltaStorageService.d.ts.map +1 -1
  163. package/lib/odspDeltaStorageService.js +9 -12
  164. package/lib/odspDeltaStorageService.js.map +1 -1
  165. package/lib/odspDocumentDeltaConnection.d.ts.map +1 -1
  166. package/lib/odspDocumentDeltaConnection.js +3 -6
  167. package/lib/odspDocumentDeltaConnection.js.map +1 -1
  168. package/lib/odspDocumentService.d.ts +1 -0
  169. package/lib/odspDocumentService.d.ts.map +1 -1
  170. package/lib/odspDocumentService.js +14 -8
  171. package/lib/odspDocumentService.js.map +1 -1
  172. package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
  173. package/lib/odspDocumentServiceFactoryCore.js +29 -6
  174. package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
  175. package/lib/odspDocumentStorageManager.d.ts +4 -4
  176. package/lib/odspDocumentStorageManager.d.ts.map +1 -1
  177. package/lib/odspDocumentStorageManager.js +83 -65
  178. package/lib/odspDocumentStorageManager.js.map +1 -1
  179. package/lib/odspDocumentStorageServiceBase.d.ts +1 -1
  180. package/lib/odspDocumentStorageServiceBase.d.ts.map +1 -1
  181. package/lib/odspDocumentStorageServiceBase.js +4 -2
  182. package/lib/odspDocumentStorageServiceBase.js.map +1 -1
  183. package/lib/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
  184. package/lib/odspDriverUrlResolverForShareLink.js +4 -4
  185. package/lib/odspDriverUrlResolverForShareLink.js.map +1 -1
  186. package/lib/odspFluidFileLink.js +1 -1
  187. package/lib/odspFluidFileLink.js.map +1 -1
  188. package/lib/odspLocationRedirection.d.ts +14 -0
  189. package/lib/odspLocationRedirection.d.ts.map +1 -0
  190. package/lib/odspLocationRedirection.js +20 -0
  191. package/lib/odspLocationRedirection.js.map +1 -0
  192. package/lib/odspSnapshotParser.d.ts.map +1 -1
  193. package/lib/odspSnapshotParser.js +1 -2
  194. package/lib/odspSnapshotParser.js.map +1 -1
  195. package/lib/odspSummaryUploadManager.d.ts +6 -3
  196. package/lib/odspSummaryUploadManager.d.ts.map +1 -1
  197. package/lib/odspSummaryUploadManager.js +14 -17
  198. package/lib/odspSummaryUploadManager.js.map +1 -1
  199. package/lib/odspUrlHelper.js +2 -1
  200. package/lib/odspUrlHelper.js.map +1 -1
  201. package/lib/odspUtils.d.ts +11 -2
  202. package/lib/odspUtils.d.ts.map +1 -1
  203. package/lib/odspUtils.js +31 -5
  204. package/lib/odspUtils.js.map +1 -1
  205. package/lib/opsCaching.d.ts.map +1 -1
  206. package/lib/opsCaching.js +3 -2
  207. package/lib/opsCaching.js.map +1 -1
  208. package/lib/packageVersion.d.ts +1 -1
  209. package/lib/packageVersion.d.ts.map +1 -1
  210. package/lib/packageVersion.js +1 -1
  211. package/lib/packageVersion.js.map +1 -1
  212. package/lib/prefetchLatestSnapshot.d.ts +6 -4
  213. package/lib/prefetchLatestSnapshot.d.ts.map +1 -1
  214. package/lib/prefetchLatestSnapshot.js +6 -4
  215. package/lib/prefetchLatestSnapshot.js.map +1 -1
  216. package/lib/retryUtils.d.ts.map +1 -1
  217. package/lib/retryUtils.js +9 -5
  218. package/lib/retryUtils.js.map +1 -1
  219. package/lib/zipItDataRepresentationUtils.d.ts +30 -18
  220. package/lib/zipItDataRepresentationUtils.d.ts.map +1 -1
  221. package/lib/zipItDataRepresentationUtils.js +166 -75
  222. package/lib/zipItDataRepresentationUtils.js.map +1 -1
  223. package/package.json +33 -20
  224. package/src/ReadBufferUtils.ts +1 -0
  225. package/src/WriteBufferUtils.ts +67 -58
  226. package/src/compactSnapshotParser.ts +102 -40
  227. package/src/compactSnapshotWriter.ts +25 -17
  228. package/src/contracts.ts +2 -14
  229. package/src/createFile.ts +75 -15
  230. package/src/createNewUtils.ts +2 -4
  231. package/src/createOdspCreateContainerRequest.ts +7 -4
  232. package/src/epochTracker.ts +47 -7
  233. package/src/fetchSnapshot.ts +35 -31
  234. package/src/getFileLink.ts +26 -39
  235. package/src/index.ts +1 -3
  236. package/src/localOdspDriver/localOdspDocumentStorageManager.ts +2 -2
  237. package/src/odspDeltaStorageService.ts +8 -9
  238. package/src/odspDocumentDeltaConnection.ts +3 -5
  239. package/src/odspDocumentService.ts +16 -13
  240. package/src/odspDocumentServiceFactoryCore.ts +40 -4
  241. package/src/odspDocumentStorageManager.ts +64 -50
  242. package/src/odspDocumentStorageServiceBase.ts +4 -2
  243. package/src/odspDriverUrlResolverForShareLink.ts +2 -5
  244. package/src/odspFluidFileLink.ts +1 -1
  245. package/src/odspLocationRedirection.ts +23 -0
  246. package/src/odspSnapshotParser.ts +3 -4
  247. package/src/odspSummaryUploadManager.ts +10 -11
  248. package/src/odspUrlHelper.ts +1 -1
  249. package/src/odspUtils.ts +40 -7
  250. package/src/opsCaching.ts +3 -2
  251. package/src/packageVersion.ts +1 -1
  252. package/src/prefetchLatestSnapshot.ts +6 -4
  253. package/src/retryUtils.ts +8 -5
  254. package/src/zipItDataRepresentationUtils.ts +198 -75
@@ -31,17 +31,18 @@ export class OdspDeltaStorageService {
31
31
  }
32
32
 
33
33
  /**
34
- * Retrieves ops from cache
34
+ * Retrieves ops from storage
35
35
  * @param from - inclusive
36
36
  * @param to - exclusive
37
37
  * @param telemetryProps - properties to add when issuing telemetry events
38
+ * @param scenarioName - reason for fetching ops
38
39
  * @returns ops retrieved & info if result was partial (i.e. more is available)
39
40
  */
40
41
  public async get(
41
42
  from: number,
42
43
  to: number,
43
44
  telemetryProps: ITelemetryProperties,
44
- fetchReason?: string,
45
+ scenarioName?: string,
45
46
  ): Promise<IDeltasFetchResult> {
46
47
  return getWithRetryForTokenRefresh(async (options) => {
47
48
  // Note - this call ends up in getSocketStorageDiscovery() and can refresh token
@@ -78,16 +79,13 @@ export class OdspDeltaStorageService {
78
79
  },
79
80
  "ops",
80
81
  true,
81
- fetchReason,
82
+ scenarioName,
82
83
  );
83
84
  clearTimeout(timer);
84
85
  const deltaStorageResponse = response.content;
85
- let messages: ISequencedDocumentMessage[];
86
- if (deltaStorageResponse.value.length > 0 && "op" in deltaStorageResponse.value[0]) {
87
- messages = (deltaStorageResponse.value as ISequencedDeltaOpMessage[]).map((operation) => operation.op);
88
- } else {
89
- messages = deltaStorageResponse.value as ISequencedDocumentMessage[];
90
- }
86
+ const messages = deltaStorageResponse.value.length > 0 && "op" in deltaStorageResponse.value[0]
87
+ ? (deltaStorageResponse.value as ISequencedDeltaOpMessage[]).map((operation) => operation.op)
88
+ : deltaStorageResponse.value as ISequencedDocumentMessage[];
91
89
 
92
90
  this.logger.sendPerformanceEvent({
93
91
  eventName: "OpsFetch",
@@ -99,6 +97,7 @@ export class OdspDeltaStorageService {
99
97
  from,
100
98
  to,
101
99
  ...telemetryProps,
100
+ reason: scenarioName,
102
101
  });
103
102
 
104
103
  // It is assumed that server always returns all the ops that it has in the range that was requested.
@@ -290,11 +290,9 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
290
290
  // Note: we suspect the incoming error object is either:
291
291
  // - a socketError: add it to the OdspError object for driver to be able to parse it and reason over it.
292
292
  // - anything else: let base class handle it
293
- if (canRetry && Number.isInteger(error?.code) && typeof error?.message === "string") {
294
- return errorObjectFromSocketError(error as IOdspSocketError, handler);
295
- } else {
296
- return super.createErrorObject(handler, error, canRetry);
297
- }
293
+ return canRetry && Number.isInteger(error?.code) && typeof error?.message === "string"
294
+ ? errorObjectFromSocketError(error as IOdspSocketError, handler)
295
+ : super.createErrorObject(handler, error, canRetry);
298
296
  }
299
297
 
300
298
  /**
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import { ITelemetryLogger } from "@fluidframework/common-definitions";
7
- import { performance } from "@fluidframework/common-utils";
7
+ import { assert, performance } from "@fluidframework/common-utils";
8
8
  import {
9
9
  ChildLogger,
10
10
  IFluidErrorBase,
@@ -21,12 +21,7 @@ import {
21
21
  IDocumentServicePolicies,
22
22
  DriverErrorType,
23
23
  } from "@fluidframework/driver-definitions";
24
- import {
25
- canRetryOnError,
26
- DeltaStreamConnectionForbiddenError,
27
- NonRetryableError,
28
- } from "@fluidframework/driver-utils";
29
- import { IFacetCodes } from "@fluidframework/odsp-doclib-utils";
24
+ import { canRetryOnError, DeltaStreamConnectionForbiddenError, NonRetryableError } from "@fluidframework/driver-utils";
30
25
  import {
31
26
  IClient,
32
27
  ISequencedDocumentMessage,
@@ -39,6 +34,7 @@ import {
39
34
  InstrumentedStorageTokenFetcher,
40
35
  OdspErrorType,
41
36
  } from "@fluidframework/odsp-driver-definitions";
37
+ import { hasFacetCodes } from "@fluidframework/odsp-doclib-utils";
42
38
  import type { io as SocketIOClientStatic } from "socket.io-client";
43
39
  import { HostStoragePolicyInternal, ISocketStorageDiscovery } from "./contracts";
44
40
  import { IOdspCache } from "./odspCache";
@@ -113,6 +109,8 @@ export class OdspDocumentService implements IDocumentService {
113
109
 
114
110
  private currentConnection?: OdspDocumentDeltaConnection;
115
111
 
112
+ private relayServiceTenantAndSessionId: string | undefined;
113
+
116
114
  /**
117
115
  * @param odspResolvedUrl - resolved url identifying document that will be managed by this service instance.
118
116
  * @param getStorageToken - function that can provide the storage token. This is is also referred to as
@@ -178,7 +176,6 @@ export class OdspDocumentService implements IDocumentService {
178
176
  this.odspResolvedUrl,
179
177
  this.getStorageToken,
180
178
  this.mc.logger,
181
- true,
182
179
  this.cache,
183
180
  this.hostPolicy,
184
181
  this.epochTracker,
@@ -189,6 +186,11 @@ export class OdspDocumentService implements IDocumentService {
189
186
  }
190
187
  throw new Error("Disconnected while uploading summary (attempt to perform flush())");
191
188
  },
189
+ () => {
190
+ assert(this.relayServiceTenantAndSessionId !== undefined,
191
+ 0x37b /* relayServiceTenantAndSessionId should be present */);
192
+ return this.relayServiceTenantAndSessionId;
193
+ },
192
194
  this.mc.config.getNumber("Fluid.Driver.Odsp.snapshotFormatFetchType"),
193
195
  );
194
196
  }
@@ -271,7 +273,7 @@ export class OdspDocumentService implements IDocumentService {
271
273
  this.socketIoClientFactory().catch(annotateAndRethrowConnectionError("socketIoClientFactory")),
272
274
  ]);
273
275
 
274
- const finalWebsocketToken = websocketToken ?? (websocketEndpoint.socketToken || null);
276
+ const finalWebsocketToken = websocketToken ?? (websocketEndpoint.socketToken ?? null);
275
277
  if (finalWebsocketToken === null) {
276
278
  throw this.annotateConnectionError(
277
279
  new NonRetryableError(
@@ -344,10 +346,9 @@ export class OdspDocumentService implements IDocumentService {
344
346
  requestSocketToken: boolean,
345
347
  options: TokenFetchOptionsEx,
346
348
  ) {
347
- return this.joinSessionCore(requestSocketToken, options).catch((e) => {
348
- const likelyFacetCodes = e as IFacetCodes;
349
- if (Array.isArray(likelyFacetCodes.facetCodes)) {
350
- for (const code of likelyFacetCodes.facetCodes) {
349
+ const response = await this.joinSessionCore(requestSocketToken, options).catch((e) => {
350
+ if (hasFacetCodes(e) && e.facetCodes !== undefined) {
351
+ for (const code of e.facetCodes) {
351
352
  switch (code) {
352
353
  case "sessionForbiddenOnPreservedFiles":
353
354
  case "sessionForbiddenOnModerationEnabledLibrary":
@@ -364,6 +365,8 @@ export class OdspDocumentService implements IDocumentService {
364
365
  }
365
366
  throw e;
366
367
  });
368
+ this.relayServiceTenantAndSessionId = `${response.tenantId}/${response.id}`;
369
+ return response;
367
370
  }
368
371
 
369
372
  private async joinSessionCore(
@@ -26,6 +26,10 @@ import {
26
26
  HostStoragePolicy,
27
27
  IFileEntry,
28
28
  IOdspUrlParts,
29
+ SharingLinkScope,
30
+ SharingLinkRole,
31
+ ShareLinkTypes,
32
+ ISharingLinkKind,
29
33
  } from "@fluidframework/odsp-driver-definitions";
30
34
  import type { io as SocketIOClientStatic } from "socket.io-client";
31
35
  import { v4 as uuid } from "uuid";
@@ -84,15 +88,13 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
84
88
  }
85
89
  }
86
90
 
91
+ const createShareLinkParam = getSharingLinkParams(this.hostPolicy, searchParams);
87
92
  const newFileInfo: INewFileInfo = {
88
93
  driveId: odspResolvedUrl.driveId,
89
94
  siteUrl: odspResolvedUrl.siteUrl,
90
95
  filePath,
91
96
  filename: odspResolvedUrl.fileName,
92
- // set createLinkType to undefined if enableShareLinkWithCreate is set to false,
93
- // so that share link creation with create file can be enabled
94
- createLinkType: this.hostPolicy.enableShareLinkWithCreate ?
95
- odspResolvedUrl.shareLinkInfo?.createLink?.type : undefined,
97
+ createLinkType: createShareLinkParam,
96
98
  };
97
99
 
98
100
  const odspLogger = createOdspLogger(logger);
@@ -110,6 +112,10 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
110
112
  {
111
113
  eventName: "CreateNew",
112
114
  isWithSummaryUpload: true,
115
+ createShareLinkParam: JSON.stringify(createShareLinkParam),
116
+ enableShareLinkWithCreate: this.hostPolicy.enableShareLinkWithCreate,
117
+ enableSingleRequestForShareLinkWithCreate:
118
+ this.hostPolicy.enableSingleRequestForShareLinkWithCreate,
113
119
  },
114
120
  async (event) => {
115
121
  odspResolvedUrl = await createNewFluidFile(
@@ -127,6 +133,8 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
127
133
  this.hostPolicy.cacheCreateNewSummary ?? true,
128
134
  !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
129
135
  odspResolvedUrl.isClpCompliantApp,
136
+ this.hostPolicy.enableSingleRequestForShareLinkWithCreate,
137
+ this.hostPolicy.enableShareLinkWithCreate,
130
138
  );
131
139
  const docService = this.createDocumentServiceCore(odspResolvedUrl, odspLogger,
132
140
  cacheAndTracker, clientIsSummarizer);
@@ -223,3 +231,31 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
223
231
  );
224
232
  }
225
233
  }
234
+
235
+ /**
236
+ * Extract the sharing link kind from the resolved URL's query paramerters
237
+ */
238
+ function getSharingLinkParams(
239
+ hostPolicy: HostStoragePolicy,
240
+ searchParams: URLSearchParams,
241
+ ): ShareLinkTypes | ISharingLinkKind | undefined {
242
+ // extract request parameters for creation of sharing link (if provided) if the feature is enabled
243
+ let createShareLinkParam: ShareLinkTypes | ISharingLinkKind | undefined;
244
+ if (hostPolicy.enableSingleRequestForShareLinkWithCreate) {
245
+ const createLinkScope = searchParams.get("createLinkScope");
246
+ const createLinkRole = searchParams.get("createLinkRole");
247
+ if (createLinkScope && SharingLinkScope[createLinkScope]) {
248
+ createShareLinkParam = {
249
+ scope: SharingLinkScope[createLinkScope],
250
+ ...(createLinkRole && SharingLinkRole[createLinkRole] ?
251
+ { role: SharingLinkRole[createLinkRole] } : {}),
252
+ };
253
+ }
254
+ } else if (hostPolicy.enableShareLinkWithCreate) {
255
+ const createLinkType = searchParams.get("createLinkType");
256
+ if (createLinkType && ShareLinkTypes[createLinkType]) {
257
+ createShareLinkParam = ShareLinkTypes[createLinkType || ""];
258
+ }
259
+ }
260
+ return createShareLinkParam;
261
+ }
@@ -16,6 +16,7 @@ import * as api from "@fluidframework/protocol-definitions";
16
16
  import {
17
17
  ISummaryContext,
18
18
  DriverErrorType,
19
+ FetchSource,
19
20
  } from "@fluidframework/driver-definitions";
20
21
  import { RateLimiter, NonRetryableError } from "@fluidframework/driver-utils";
21
22
  import {
@@ -87,11 +88,11 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
87
88
  private readonly odspResolvedUrl: IOdspResolvedUrl,
88
89
  private readonly getStorageToken: InstrumentedStorageTokenFetcher,
89
90
  private readonly logger: ITelemetryLogger,
90
- private readonly fetchFullSnapshot: boolean,
91
91
  private readonly cache: IOdspCache,
92
92
  private readonly hostPolicy: HostStoragePolicyInternal,
93
93
  private readonly epochTracker: EpochTracker,
94
94
  private readonly flushCallback: () => Promise<FlushResult>,
95
+ private readonly relayServiceTenantAndSessionId: () => string,
95
96
  private readonly snapshotFormatFetchType?: SnapshotFormatSupportType,
96
97
  ) {
97
98
  super();
@@ -107,6 +108,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
107
108
  logger,
108
109
  epochTracker,
109
110
  !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
111
+ this.relayServiceTenantAndSessionId,
110
112
  );
111
113
  }
112
114
 
@@ -204,7 +206,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
204
206
  return super.getSnapshotTree(version, scenarioName);
205
207
  }
206
208
 
207
- public async getVersions(blobid: string | null, count: number, scenarioName?: string): Promise<api.IVersion[]> {
209
+ public async getVersions(blobid: string | null, count: number, scenarioName?: string, fetchSource?: FetchSource): Promise<api.IVersion[]> {
208
210
  // Regular load workflow uses blobId === documentID to indicate "latest".
209
211
  if (blobid !== this.documentId && blobid) {
210
212
  // FluidFetch & FluidDebugger tools use empty sting to query for versions
@@ -225,19 +227,23 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
225
227
  }
226
228
 
227
229
  // If count is one, we can use the trees/latest API, which returns the latest version and trees in a single request for better performance
228
- // Do it only once - we might get more here due to summarizer - it needs only container tree, not full snapshot.
229
- if (this.firstVersionCall && count === 1 && (blobid === null || blobid === this.documentId)) {
230
+ if (count === 1 && (blobid === null || blobid === this.documentId)) {
230
231
  const hostSnapshotOptions = this.hostPolicy.snapshotOptions;
231
232
  const odspSnapshotCacheValue: ISnapshotContents = await PerformanceEvent.timedExecAsync(
232
233
  this.logger,
233
- { eventName: "ObtainSnapshot" },
234
+ { eventName: "ObtainSnapshot", fetchSource },
234
235
  async (event: PerformanceEvent) => {
235
236
  const props: GetVersionsTelemetryProps = {};
236
237
  let retrievedSnapshot: ISnapshotContents | undefined;
237
- // Here's the logic to grab the persistent cache snapshot implemented by the host
238
- // Epoch tracker is responsible for communicating with the persistent cache, handling epochs and cache versions
239
- const cachedSnapshotP: Promise<ISnapshotContents | undefined> =
240
- this.epochTracker.get(createCacheSnapshotKey(this.odspResolvedUrl))
238
+
239
+ let method: string;
240
+ if (fetchSource === FetchSource.noCache) {
241
+ retrievedSnapshot = await this.fetchSnapshot(hostSnapshotOptions, scenarioName);
242
+ method = "networkOnly";
243
+ } else {
244
+ // Here's the logic to grab the persistent cache snapshot implemented by the host
245
+ // Epoch tracker is responsible for communicating with the persistent cache, handling epochs and cache versions
246
+ const cachedSnapshotP: Promise<ISnapshotContents | undefined> = this.epochTracker.get(createCacheSnapshotKey(this.odspResolvedUrl))
241
247
  .then(async (snapshotCachedEntry: ISnapshotCachedEntry) => {
242
248
  if (snapshotCachedEntry !== undefined) {
243
249
  // If the cached entry does not contain the entry time, then assign it a default of 30 days old.
@@ -264,46 +270,56 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
264
270
  return snapshotCachedEntry;
265
271
  });
266
272
 
267
- // Based on the concurrentSnapshotFetch policy:
268
- // Either retrieve both the network and cache snapshots concurrently and pick the first to return,
269
- // or grab the cache value and then the network value if the cache value returns undefined.
270
- let method: string;
271
- if (this.hostPolicy.concurrentSnapshotFetch && !this.hostPolicy.summarizerClient) {
272
- const networkSnapshotP = this.fetchSnapshot(hostSnapshotOptions, scenarioName);
273
-
274
- // Ensure that failures on both paths are ignored initially.
275
- // I.e. if cache fails for some reason, we will proceed with network result.
276
- // And vice versa - if (for example) client is offline and network request fails first, we
277
- // do want to attempt to succeed with cached data!
278
- const promiseRaceWinner = await promiseRaceWithWinner([
279
- cachedSnapshotP.catch(() => undefined),
280
- networkSnapshotP.catch(() => undefined),
281
- ]);
282
- retrievedSnapshot = promiseRaceWinner.value;
283
- method = promiseRaceWinner.index === 0 ? "cache" : "network";
284
-
285
- if (retrievedSnapshot === undefined) {
286
- // if network failed -> wait for cache ( then return network failure)
287
- // If cache returned empty or failed -> wait for network (success of failure)
288
- if (promiseRaceWinner.index === 1) {
289
- retrievedSnapshot = await cachedSnapshotP;
290
- method = "cache";
291
- }
273
+ // Based on the concurrentSnapshotFetch policy:
274
+ // Either retrieve both the network and cache snapshots concurrently and pick the first to return,
275
+ // or grab the cache value and then the network value if the cache value returns undefined.
276
+ // For summarizer which could call this during refreshing of summary parent, always use the cache
277
+ // first. Also for other clients, if it is not critical path which is determined by firstVersionCall,
278
+ // then also check the cache first.
279
+ if (this.firstVersionCall && this.hostPolicy.concurrentSnapshotFetch && !this.hostPolicy.summarizerClient) {
280
+ const networkSnapshotP = this.fetchSnapshot(hostSnapshotOptions, scenarioName);
281
+
282
+ // Ensure that failures on both paths are ignored initially.
283
+ // I.e. if cache fails for some reason, we will proceed with network result.
284
+ // And vice versa - if (for example) client is offline and network request fails first, we
285
+ // do want to attempt to succeed with cached data!
286
+ const promiseRaceWinner = await promiseRaceWithWinner([
287
+ cachedSnapshotP.catch(() => undefined),
288
+ networkSnapshotP.catch(() => undefined),
289
+ ]);
290
+ retrievedSnapshot = promiseRaceWinner.value;
291
+ method = promiseRaceWinner.index === 0 ? "cache" : "network";
292
+
292
293
  if (retrievedSnapshot === undefined) {
293
- retrievedSnapshot = await networkSnapshotP;
294
- method = "network";
294
+ // if network failed -> wait for cache ( then return network failure)
295
+ // If cache returned empty or failed -> wait for network (success of failure)
296
+ if (promiseRaceWinner.index === 1) {
297
+ retrievedSnapshot = await cachedSnapshotP;
298
+ method = "cache";
299
+ }
300
+ if (retrievedSnapshot === undefined) {
301
+ retrievedSnapshot = await networkSnapshotP;
302
+ method = "network";
303
+ }
295
304
  }
296
- }
297
- } else {
298
- // Note: There's a race condition here - another caller may come past the undefined check
299
- // while the first caller is awaiting later async code in this block.
300
-
301
- retrievedSnapshot = await cachedSnapshotP;
305
+ } else {
306
+ // Note: There's a race condition here - another caller may come past the undefined check
307
+ // while the first caller is awaiting later async code in this block.
302
308
 
303
- method = retrievedSnapshot !== undefined ? "cache" : "network";
304
-
305
- if (retrievedSnapshot === undefined) {
306
- retrievedSnapshot = await this.fetchSnapshot(hostSnapshotOptions, scenarioName);
309
+ retrievedSnapshot = await cachedSnapshotP;
310
+ if (retrievedSnapshot !== undefined) {
311
+ method = "cache";
312
+ } else {
313
+ method = "network";
314
+ const options: ISnapshotOptions = { ...hostSnapshotOptions };
315
+ // Don't fetch the blobs/deltas if it is not the first call. By default server will add
316
+ // blobs and deltas to the response.
317
+ if (!this.firstVersionCall) {
318
+ options.blobs = 0;
319
+ options.deltas = 0;
320
+ }
321
+ retrievedSnapshot = await this.fetchSnapshot(options, scenarioName);
322
+ }
307
323
  }
308
324
  }
309
325
  if (method === "network") {
@@ -314,10 +330,9 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
314
330
  },
315
331
  );
316
332
 
317
- // Successful call, make network calls only
333
+ // Don't override ops which were fetched during initial load, since we could still need them.
334
+ const id = this.initializeFromSnapshot(odspSnapshotCacheValue, this.firstVersionCall);
318
335
  this.firstVersionCall = false;
319
- const id = this.initializeFromSnapshot(odspSnapshotCacheValue);
320
-
321
336
  return id ? [{ id, treeId: undefined! }] : [];
322
337
  }
323
338
 
@@ -537,7 +552,6 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
537
552
  this.snapshotUrl!,
538
553
  storageToken,
539
554
  id,
540
- this.fetchFullSnapshot,
541
555
  !!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
542
556
  this.logger,
543
557
  snapshotDownloader,
@@ -244,7 +244,7 @@ export abstract class OdspDocumentStorageServiceBase implements IDocumentStorage
244
244
  return summarySnapshotTree;
245
245
  }
246
246
 
247
- protected initializeFromSnapshot(odspSnapshotCacheValue: ISnapshotContents): string | undefined {
247
+ protected initializeFromSnapshot(odspSnapshotCacheValue: ISnapshotContents, cacheOps: boolean = true): string | undefined {
248
248
  this._snapshotSequenceNumber = odspSnapshotCacheValue.sequenceNumber;
249
249
  const { snapshotTree, blobs, ops } = odspSnapshotCacheValue;
250
250
 
@@ -260,7 +260,9 @@ export abstract class OdspDocumentStorageServiceBase implements IDocumentStorage
260
260
  this.initBlobsCache(blobs);
261
261
  }
262
262
 
263
- this.ops = ops;
263
+ if (cacheOps) {
264
+ this.ops = ops;
265
+ }
264
266
  return id;
265
267
  }
266
268
  }
@@ -136,7 +136,7 @@ export class OdspDriverUrlResolverForShareLink implements IUrlResolver {
136
136
  // We need to remove the nav param if set by host when setting the sharelink as otherwise the shareLinkId
137
137
  // when redeeming the share link during the redeem fallback for trees latest call becomes greater than
138
138
  // the eligible length.
139
- odspResolvedUrl.shareLinkInfo = Object.assign(odspResolvedUrl.shareLinkInfo || {},
139
+ odspResolvedUrl.shareLinkInfo = Object.assign(odspResolvedUrl.shareLinkInfo ?? {},
140
140
  { sharingLinkToRedeem: this.removeNavParam(request.url) });
141
141
  }
142
142
  if (odspResolvedUrl.itemId) {
@@ -171,10 +171,7 @@ export class OdspDriverUrlResolverForShareLink implements IUrlResolver {
171
171
  return cachedLinkPromise;
172
172
  }
173
173
  const newLinkPromise = getFileLink(
174
- this.shareLinkFetcherProps.tokenFetcher,
175
- resolvedUrl,
176
- this.shareLinkFetcherProps.identityType,
177
- this.logger,
174
+ this.shareLinkFetcherProps.tokenFetcher, resolvedUrl, this.logger,
178
175
  ).catch((error) => {
179
176
  // This should imply that error is a non-retriable error.
180
177
  this.logger.sendErrorEvent({ eventName: "FluidFileUrlError" }, error);
@@ -52,7 +52,7 @@ export function encodeOdspFluidDataStoreLocator(locator: OdspFluidDataStoreLocat
52
52
  /**
53
53
  * Decodes given encoded value representing Fluid data store locator extracted from ODSP Fluid file link
54
54
  * @param encodedLocatorValue - encoded Fluid data store locator value which was produced by
55
- * encodeOdspFluidDataStoreLocator method
55
+ * {@link encodeOdspFluidDataStoreLocator} function
56
56
  * @param siteOriginUrl - site origin that will be appended to encoded relative path to form absolute file url
57
57
  * @returns object representing Fluid data store location in ODSP terms
58
58
  */
@@ -0,0 +1,23 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { IFluidResolvedUrl } from "@fluidframework/driver-definitions";
7
+ import { IOdspResolvedUrl } from "@fluidframework/odsp-driver-definitions";
8
+ import { getOdspResolvedUrl } from "./odspUtils";
9
+
10
+ /**
11
+ * It takes a resolved url with old siteUrl and creates a new resolved url with updated site url domain.
12
+ * @param resolvedUrl - Previous odsp resolved url with older site url.
13
+ * @param redirectLocation - Url at which the network call has to be made. It contains new site info.
14
+ * @returns - Resolved url after patching the correct siteUrl.
15
+ */
16
+ export function patchOdspResolvedUrl(resolvedUrl: IFluidResolvedUrl, redirectLocation: string): IOdspResolvedUrl {
17
+ const odspResolvedUrl = { ...getOdspResolvedUrl(resolvedUrl) };
18
+ // Generate the new SiteUrl from the redirection location.
19
+ const newSiteDomain = new URL(redirectLocation).origin;
20
+ const newSiteUrl = `${newSiteDomain}${new URL(odspResolvedUrl.siteUrl).pathname}`;
21
+ odspResolvedUrl.siteUrl = newSiteUrl;
22
+ return odspResolvedUrl;
23
+ }
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { assert, stringToBuffer } from "@fluidframework/common-utils";
7
7
  import * as api from "@fluidframework/protocol-definitions";
8
- import { IOdspSnapshot, IOdspSnapshotCommit, ISnapshotTreeEx } from "./contracts";
8
+ import { IOdspSnapshot, IOdspSnapshotCommit } from "./contracts";
9
9
  import { ISnapshotContents } from "./odspPublicUtils";
10
10
 
11
11
  /**
@@ -18,7 +18,7 @@ import { ISnapshotContents } from "./odspPublicUtils";
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: ISnapshotTreeEx = { id: flatTree.id, blobs: {}, commits: {}, trees: {} };
21
+ const root: api.ISnapshotTree = { id: flatTree.id, blobs: {}, trees: {} };
22
22
  lookup[""] = root;
23
23
 
24
24
  for (const entry of flatTree.entries) {
@@ -31,10 +31,9 @@ function buildHierarchy(flatTree: IOdspSnapshotCommit): api.ISnapshotTree {
31
31
 
32
32
  // Add in either the blob or tree
33
33
  if (entry.type === "tree") {
34
- const newTree: ISnapshotTreeEx = {
34
+ const newTree: api.ISnapshotTree = {
35
35
  blobs: {},
36
36
  trees: {},
37
- commits: {},
38
37
  unreferenced: entry.unreferenced,
39
38
  };
40
39
  node.trees[decodeURIComponent(entryPathBase)] = newTree;
@@ -39,6 +39,7 @@ export class OdspSummaryUploadManager {
39
39
  logger: ITelemetryLogger,
40
40
  private readonly epochTracker: EpochTracker,
41
41
  private readonly forceAccessTokenViaAuthorizationHeader: boolean,
42
+ private readonly relayServiceTenantAndSessionId: () => string,
42
43
  ) {
43
44
  this.mc = loggerToMonitoringContext(logger);
44
45
  }
@@ -93,9 +94,8 @@ export class OdspSummaryUploadManager {
93
94
  this.forceAccessTokenViaAuthorizationHeader,
94
95
  );
95
96
  headers["Content-Type"] = "application/json";
96
- if (parentHandle) {
97
- headers["If-Match"] = `fluid:containerid=${parentHandle}`;
98
- }
97
+ headers["If-Match"] = `fluid:sessionid=${
98
+ this.relayServiceTenantAndSessionId()}${parentHandle ? `;containerid=${parentHandle}` : ""}`;
99
99
 
100
100
  const postBody = JSON.stringify(snapshot);
101
101
 
@@ -126,8 +126,10 @@ export class OdspSummaryUploadManager {
126
126
  }
127
127
 
128
128
  /**
129
- * Following are the goals of this function.
130
- * a.) Converts the summary tree to a snapshot/odsp tree to be uploaded. Always upload full snapshot tree.
129
+ * Following are the goals of this function:
130
+ *
131
+ * a. Converts the summary tree to a snapshot/odsp tree to be uploaded. Always upload full snapshot tree.
132
+ *
131
133
  * @param parentHandle - Handle of the last uploaded summary or detach new summary.
132
134
  * @param tree - Summary Tree which will be converted to snapshot tree to be uploaded.
133
135
  * @param rootNodeName - Root node name of the summary tree.
@@ -169,19 +171,16 @@ export class OdspSummaryUploadManager {
169
171
  break;
170
172
  }
171
173
  case api.SummaryType.Blob: {
172
- if (typeof summaryObject.content === "string") {
173
- value = {
174
+ value = typeof summaryObject.content === "string"
175
+ ? {
174
176
  type: "blob",
175
177
  content: summaryObject.content,
176
178
  encoding: "utf-8",
177
- };
178
- } else {
179
- value = {
179
+ } : {
180
180
  type: "blob",
181
181
  content: Uint8ArrayToString(summaryObject.content, "base64"),
182
182
  encoding: "base64",
183
183
  };
184
- }
185
184
  blobs++;
186
185
  break;
187
186
  }
@@ -69,7 +69,7 @@ export function isOdcUrl(url: string | URL): boolean {
69
69
  // Format: /v2.1/drives('ABC123')/items('ABC123!123')
70
70
  const odcODataRegex = /\/v2.1\/drives\('[^/]+'\)\/items\('[\da-z]+!\d+'\)/;
71
71
 
72
- return !!(odcRegex.exec(path) || odcODataRegex.exec(path));
72
+ return !!(odcRegex.exec(path) ?? odcODataRegex.exec(path));
73
73
  }
74
74
 
75
75
  /**