@fluidframework/odsp-driver 2.0.0-internal.2.2.1 → 2.0.0-internal.2.3.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.
- package/.eslintrc.js +1 -1
- package/dist/WriteBufferUtils.d.ts.map +1 -1
- package/dist/WriteBufferUtils.js +0 -1
- package/dist/WriteBufferUtils.js.map +1 -1
- package/dist/contractsPublic.d.ts.map +1 -1
- package/dist/contractsPublic.js.map +1 -1
- package/dist/createFile.d.ts +2 -6
- package/dist/createFile.d.ts.map +1 -1
- package/dist/createFile.js +13 -125
- package/dist/createFile.js.map +1 -1
- package/dist/createNewUtils.d.ts +16 -0
- package/dist/createNewUtils.d.ts.map +1 -1
- package/dist/createNewUtils.js +131 -1
- package/dist/createNewUtils.js.map +1 -1
- package/dist/fetchSnapshot.js +1 -1
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/localOdspDriver/localOdspDocumentServiceFactory.d.ts.map +1 -1
- package/dist/localOdspDriver/localOdspDocumentServiceFactory.js +1 -1
- package/dist/localOdspDriver/localOdspDocumentServiceFactory.js.map +1 -1
- package/dist/odspDelayLoadedDeltaStream.d.ts +75 -0
- package/dist/odspDelayLoadedDeltaStream.d.ts.map +1 -0
- package/dist/odspDelayLoadedDeltaStream.js +259 -0
- package/dist/odspDelayLoadedDeltaStream.js.map +1 -0
- package/dist/odspDocumentDeltaConnection.d.ts +1 -3
- package/dist/odspDocumentDeltaConnection.d.ts.map +1 -1
- package/dist/odspDocumentDeltaConnection.js +6 -6
- package/dist/odspDocumentDeltaConnection.js.map +1 -1
- package/dist/odspDocumentService.d.ts +10 -25
- package/dist/odspDocumentService.d.ts.map +1 -1
- package/dist/odspDocumentService.js +71 -204
- package/dist/odspDocumentService.js.map +1 -1
- package/dist/odspDocumentServiceFactory.d.ts.map +1 -1
- package/dist/odspDocumentServiceFactory.js +1 -2
- package/dist/odspDocumentServiceFactory.js.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.d.ts +1 -3
- package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.js +3 -3
- package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
- package/dist/odspDocumentServiceFactoryWithCodeSplit.d.ts +4 -0
- package/dist/odspDocumentServiceFactoryWithCodeSplit.d.ts.map +1 -1
- package/dist/odspDocumentServiceFactoryWithCodeSplit.js +5 -20
- package/dist/odspDocumentServiceFactoryWithCodeSplit.js.map +1 -1
- package/dist/odspDocumentStorageManager.d.ts.map +1 -1
- package/dist/odspDocumentStorageManager.js +0 -2
- package/dist/odspDocumentStorageManager.js.map +1 -1
- package/dist/odspDocumentStorageServiceBase.d.ts.map +1 -1
- package/dist/odspDocumentStorageServiceBase.js +0 -2
- package/dist/odspDocumentStorageServiceBase.js.map +1 -1
- package/dist/odspSummaryUploadManager.d.ts.map +1 -1
- package/dist/odspSummaryUploadManager.js +0 -2
- package/dist/odspSummaryUploadManager.js.map +1 -1
- package/dist/odspUtils.d.ts +10 -1
- package/dist/odspUtils.d.ts.map +1 -1
- package/dist/odspUtils.js +5 -1
- 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/zipItDataRepresentationUtils.d.ts.map +1 -1
- package/dist/zipItDataRepresentationUtils.js +0 -1
- package/dist/zipItDataRepresentationUtils.js.map +1 -1
- package/lib/WriteBufferUtils.d.ts.map +1 -1
- package/lib/WriteBufferUtils.js +0 -1
- package/lib/WriteBufferUtils.js.map +1 -1
- package/lib/contractsPublic.d.ts.map +1 -1
- package/lib/contractsPublic.js.map +1 -1
- package/lib/createFile.d.ts +2 -6
- package/lib/createFile.d.ts.map +1 -1
- package/lib/createFile.js +15 -126
- package/lib/createFile.js.map +1 -1
- package/lib/createNewUtils.d.ts +16 -0
- package/lib/createNewUtils.d.ts.map +1 -1
- package/lib/createNewUtils.js +129 -1
- package/lib/createNewUtils.js.map +1 -1
- package/lib/fetchSnapshot.js +1 -1
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/localOdspDriver/localOdspDocumentServiceFactory.d.ts.map +1 -1
- package/lib/localOdspDriver/localOdspDocumentServiceFactory.js +1 -1
- package/lib/localOdspDriver/localOdspDocumentServiceFactory.js.map +1 -1
- package/lib/odspDelayLoadedDeltaStream.d.ts +75 -0
- package/lib/odspDelayLoadedDeltaStream.d.ts.map +1 -0
- package/lib/odspDelayLoadedDeltaStream.js +255 -0
- package/lib/odspDelayLoadedDeltaStream.js.map +1 -0
- package/lib/odspDocumentDeltaConnection.d.ts +1 -3
- package/lib/odspDocumentDeltaConnection.d.ts.map +1 -1
- package/lib/odspDocumentDeltaConnection.js +6 -6
- package/lib/odspDocumentDeltaConnection.js.map +1 -1
- package/lib/odspDocumentService.d.ts +10 -25
- package/lib/odspDocumentService.d.ts.map +1 -1
- package/lib/odspDocumentService.js +56 -207
- package/lib/odspDocumentService.js.map +1 -1
- package/lib/odspDocumentServiceFactory.d.ts.map +1 -1
- package/lib/odspDocumentServiceFactory.js +1 -2
- package/lib/odspDocumentServiceFactory.js.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.d.ts +1 -3
- package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.js +3 -3
- package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
- package/lib/odspDocumentServiceFactoryWithCodeSplit.d.ts +4 -0
- package/lib/odspDocumentServiceFactoryWithCodeSplit.d.ts.map +1 -1
- package/lib/odspDocumentServiceFactoryWithCodeSplit.js +5 -1
- package/lib/odspDocumentServiceFactoryWithCodeSplit.js.map +1 -1
- package/lib/odspDocumentStorageManager.d.ts.map +1 -1
- package/lib/odspDocumentStorageManager.js +0 -2
- package/lib/odspDocumentStorageManager.js.map +1 -1
- package/lib/odspDocumentStorageServiceBase.d.ts.map +1 -1
- package/lib/odspDocumentStorageServiceBase.js +0 -2
- package/lib/odspDocumentStorageServiceBase.js.map +1 -1
- package/lib/odspSummaryUploadManager.d.ts.map +1 -1
- package/lib/odspSummaryUploadManager.js +0 -2
- package/lib/odspSummaryUploadManager.js.map +1 -1
- package/lib/odspUtils.d.ts +10 -1
- package/lib/odspUtils.d.ts.map +1 -1
- package/lib/odspUtils.js +3 -0
- 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/zipItDataRepresentationUtils.d.ts.map +1 -1
- package/lib/zipItDataRepresentationUtils.js +0 -1
- package/lib/zipItDataRepresentationUtils.js.map +1 -1
- package/package.json +13 -12
- package/src/WriteBufferUtils.ts +0 -1
- package/src/contractsPublic.ts +0 -1
- package/src/createFile.ts +23 -168
- package/src/createNewUtils.ts +188 -2
- package/src/fetchSnapshot.ts +1 -1
- package/src/localOdspDriver/localOdspDocumentServiceFactory.ts +0 -1
- package/src/odspDelayLoadedDeltaStream.ts +359 -0
- package/src/odspDocumentDeltaConnection.ts +4 -7
- package/src/odspDocumentService.ts +63 -283
- package/src/odspDocumentServiceFactory.ts +0 -2
- package/src/odspDocumentServiceFactoryCore.ts +1 -3
- package/src/odspDocumentServiceFactoryWithCodeSplit.ts +4 -1
- package/src/odspDocumentStorageManager.ts +0 -4
- package/src/odspDocumentStorageServiceBase.ts +0 -3
- package/src/odspSummaryUploadManager.ts +0 -4
- package/src/odspUtils.ts +16 -1
- package/src/packageVersion.ts +1 -1
- package/src/zipItDataRepresentationUtils.ts +0 -1
- package/dist/getSocketIo.d.ts +0 -11
- package/dist/getSocketIo.d.ts.map +0 -1
- package/dist/getSocketIo.js +0 -20
- package/dist/getSocketIo.js.map +0 -1
- package/lib/getSocketIo.d.ts +0 -11
- package/lib/getSocketIo.d.ts.map +0 -1
- package/lib/getSocketIo.js +0 -13
- package/lib/getSocketIo.js.map +0 -1
- package/src/getSocketIo.ts +0 -14
|
@@ -4,13 +4,11 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
|
-
import { assert
|
|
7
|
+
import { assert } from "@fluidframework/common-utils";
|
|
8
8
|
import {
|
|
9
9
|
ChildLogger,
|
|
10
|
-
IFluidErrorBase,
|
|
11
10
|
loggerToMonitoringContext,
|
|
12
11
|
MonitoringContext,
|
|
13
|
-
normalizeError,
|
|
14
12
|
} from "@fluidframework/telemetry-utils";
|
|
15
13
|
import {
|
|
16
14
|
IDocumentDeltaConnection,
|
|
@@ -19,9 +17,7 @@ import {
|
|
|
19
17
|
IResolvedUrl,
|
|
20
18
|
IDocumentStorageService,
|
|
21
19
|
IDocumentServicePolicies,
|
|
22
|
-
DriverErrorType,
|
|
23
20
|
} from "@fluidframework/driver-definitions";
|
|
24
|
-
import { canRetryOnError, DeltaStreamConnectionForbiddenError, NonRetryableError } from "@fluidframework/driver-utils";
|
|
25
21
|
import {
|
|
26
22
|
IClient,
|
|
27
23
|
ISequencedDocumentMessage,
|
|
@@ -32,31 +28,32 @@ import {
|
|
|
32
28
|
IEntry,
|
|
33
29
|
HostStoragePolicy,
|
|
34
30
|
InstrumentedStorageTokenFetcher,
|
|
35
|
-
OdspErrorType,
|
|
36
31
|
} from "@fluidframework/odsp-driver-definitions";
|
|
37
|
-
import {
|
|
38
|
-
import type { io as SocketIOClientStatic } from "socket.io-client";
|
|
39
|
-
import { HostStoragePolicyInternal, ISocketStorageDiscovery } from "./contracts";
|
|
32
|
+
import { HostStoragePolicyInternal } from "./contracts";
|
|
40
33
|
import { IOdspCache } from "./odspCache";
|
|
41
34
|
import { OdspDeltaStorageService, OdspDeltaStorageWithCache } from "./odspDeltaStorageService";
|
|
42
|
-
import { OdspDocumentDeltaConnection } from "./odspDocumentDeltaConnection";
|
|
43
35
|
import { OdspDocumentStorageService } from "./odspDocumentStorageManager";
|
|
44
|
-
import {
|
|
45
|
-
import { fetchJoinSession } from "./vroom";
|
|
36
|
+
import { getOdspResolvedUrl } from "./odspUtils";
|
|
46
37
|
import { isOdcOrigin } from "./odspUrlHelper";
|
|
47
38
|
import { EpochTracker } from "./epochTracker";
|
|
48
39
|
import { OpsCache } from "./opsCaching";
|
|
49
40
|
import { RetryErrorsStorageAdapter } from "./retryErrorsStorageAdapter";
|
|
50
|
-
import {
|
|
41
|
+
import type { OdspDelayLoadedDeltaStream } from "./odspDelayLoadedDeltaStream";
|
|
51
42
|
|
|
52
43
|
/**
|
|
53
44
|
* The DocumentService manages the Socket.IO connection and manages routing requests to connected
|
|
54
45
|
* clients
|
|
55
46
|
*/
|
|
56
47
|
export class OdspDocumentService implements IDocumentService {
|
|
57
|
-
private _policies: IDocumentServicePolicies;
|
|
58
|
-
|
|
59
|
-
|
|
48
|
+
private readonly _policies: IDocumentServicePolicies;
|
|
49
|
+
|
|
50
|
+
// Promise to load socket module only once.
|
|
51
|
+
private socketModuleP: Promise<OdspDelayLoadedDeltaStream> | undefined;
|
|
52
|
+
|
|
53
|
+
private odspDelayLoadedDeltaStream: OdspDelayLoadedDeltaStream | undefined;
|
|
54
|
+
|
|
55
|
+
private odspSocketModuleLoaded: boolean = false;
|
|
56
|
+
|
|
60
57
|
/**
|
|
61
58
|
* @param resolvedUrl - resolved url identifying document that will be managed by returned service instance.
|
|
62
59
|
* @param getStorageToken - function that can provide the storage token. This is is also referred to as
|
|
@@ -65,7 +62,6 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
65
62
|
* to as the "Push" token in SPO. If undefined then websocket token is expected to be returned with joinSession
|
|
66
63
|
* response payload.
|
|
67
64
|
* @param logger - a logger that can capture performance and diagnostic information
|
|
68
|
-
* @param socketIoClientFactory - A factory that returns a promise to the socket io library required by the driver
|
|
69
65
|
* @param cache - This caches response for joinSession.
|
|
70
66
|
* @param hostPolicy - This host constructed policy which customizes service behavior.
|
|
71
67
|
* @param epochTracker - This helper class which adds epoch to backend calls made by returned service instance.
|
|
@@ -76,7 +72,6 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
76
72
|
getStorageToken: InstrumentedStorageTokenFetcher,
|
|
77
73
|
getWebsocketToken: ((options: TokenFetchOptions) => Promise<string | null>) | undefined,
|
|
78
74
|
logger: ITelemetryLogger,
|
|
79
|
-
socketIoClientFactory: () => Promise<typeof SocketIOClientStatic>,
|
|
80
75
|
cache: IOdspCache,
|
|
81
76
|
hostPolicy: HostStoragePolicy,
|
|
82
77
|
epochTracker: EpochTracker,
|
|
@@ -88,7 +83,6 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
88
83
|
getStorageToken,
|
|
89
84
|
getWebsocketToken,
|
|
90
85
|
logger,
|
|
91
|
-
socketIoClientFactory,
|
|
92
86
|
cache,
|
|
93
87
|
hostPolicy,
|
|
94
88
|
epochTracker,
|
|
@@ -101,16 +95,10 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
101
95
|
|
|
102
96
|
private readonly mc: MonitoringContext;
|
|
103
97
|
|
|
104
|
-
private readonly joinSessionKey: string;
|
|
105
|
-
|
|
106
98
|
private readonly hostPolicy: HostStoragePolicyInternal;
|
|
107
99
|
|
|
108
100
|
private _opsCache?: OpsCache;
|
|
109
101
|
|
|
110
|
-
private currentConnection?: OdspDocumentDeltaConnection;
|
|
111
|
-
|
|
112
|
-
private relayServiceTenantAndSessionId: string | undefined;
|
|
113
|
-
|
|
114
102
|
/**
|
|
115
103
|
* @param odspResolvedUrl - resolved url identifying document that will be managed by this service instance.
|
|
116
104
|
* @param getStorageToken - function that can provide the storage token. This is is also referred to as
|
|
@@ -130,7 +118,6 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
130
118
|
private readonly getStorageToken: InstrumentedStorageTokenFetcher,
|
|
131
119
|
private readonly getWebsocketToken: ((options: TokenFetchOptions) => Promise<string | null>) | undefined,
|
|
132
120
|
logger: ITelemetryLogger,
|
|
133
|
-
private readonly socketIoClientFactory: () => Promise<typeof SocketIOClientStatic>,
|
|
134
121
|
private readonly cache: IOdspCache,
|
|
135
122
|
hostPolicy: HostStoragePolicy,
|
|
136
123
|
private readonly epochTracker: EpochTracker,
|
|
@@ -142,7 +129,6 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
142
129
|
storageOnly: odspResolvedUrl.fileVersion !== undefined,
|
|
143
130
|
};
|
|
144
131
|
|
|
145
|
-
this.joinSessionKey = `${this.odspResolvedUrl.hashedDocumentId}/joinsession`;
|
|
146
132
|
this.mc = loggerToMonitoringContext(
|
|
147
133
|
ChildLogger.create(logger,
|
|
148
134
|
undefined,
|
|
@@ -182,13 +168,14 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
182
168
|
this.epochTracker,
|
|
183
169
|
// flushCallback
|
|
184
170
|
async () => {
|
|
185
|
-
|
|
186
|
-
|
|
171
|
+
const currentConnection = this.odspDelayLoadedDeltaStream?.currentDeltaConnection;
|
|
172
|
+
if (currentConnection !== undefined && !currentConnection.disposed) {
|
|
173
|
+
return currentConnection.flush();
|
|
187
174
|
}
|
|
188
175
|
throw new Error("Disconnected while uploading summary (attempt to perform flush())");
|
|
189
176
|
},
|
|
190
177
|
() => {
|
|
191
|
-
return this.relayServiceTenantAndSessionId;
|
|
178
|
+
return this.odspDelayLoadedDeltaStream?.relayServiceTenantAndSessionId;
|
|
192
179
|
},
|
|
193
180
|
this.mc.config.getNumber("Fluid.Driver.Odsp.snapshotFormatFetchType"),
|
|
194
181
|
);
|
|
@@ -219,282 +206,76 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
219
206
|
this.mc.logger,
|
|
220
207
|
batchSize,
|
|
221
208
|
concurrency,
|
|
209
|
+
// Get Ops from storage callback.
|
|
222
210
|
async (from, to, telemetryProps, fetchReason) => service.get(from, to, telemetryProps, fetchReason),
|
|
211
|
+
// Get cachedOps Callback.
|
|
223
212
|
async (from, to) => {
|
|
224
213
|
const res = await this.opsCache?.get(from, to);
|
|
225
214
|
return res as ISequencedDocumentMessage[] ?? [];
|
|
226
215
|
},
|
|
216
|
+
// Ops requestFromSocket Callback.
|
|
227
217
|
(from, to) => {
|
|
228
|
-
|
|
229
|
-
|
|
218
|
+
const currentConnection = this.odspDelayLoadedDeltaStream?.currentDeltaConnection;
|
|
219
|
+
if (currentConnection !== undefined && !currentConnection.disposed) {
|
|
220
|
+
currentConnection.requestOps(from, to);
|
|
230
221
|
}
|
|
231
222
|
},
|
|
232
223
|
(ops: ISequencedDocumentMessage[]) => this.opsReceived(ops),
|
|
233
224
|
);
|
|
234
225
|
}
|
|
235
226
|
|
|
236
|
-
/** Annotate the given error indicating which connection step failed */
|
|
237
|
-
private annotateConnectionError(
|
|
238
|
-
error: any,
|
|
239
|
-
failedConnectionStep: string,
|
|
240
|
-
separateTokenRequest: boolean,
|
|
241
|
-
): IFluidErrorBase {
|
|
242
|
-
return normalizeError(error, { props: {
|
|
243
|
-
failedConnectionStep,
|
|
244
|
-
separateTokenRequest,
|
|
245
|
-
} });
|
|
246
|
-
}
|
|
247
|
-
|
|
248
227
|
/**
|
|
249
228
|
* Connects to a delta stream endpoint for emitting ops.
|
|
250
229
|
*
|
|
251
230
|
* @returns returns the document delta stream service for onedrive/sharepoint driver.
|
|
252
231
|
*/
|
|
253
232
|
public async connectToDeltaStream(client: IClient): Promise<IDocumentDeltaConnection> {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
return getWithRetryForTokenRefresh<IDocumentDeltaConnection>(async (options) => {
|
|
257
|
-
// Presence of getWebsocketToken callback dictates whether callback is used for fetching
|
|
258
|
-
// websocket token or whether it is returned with joinSession response payload
|
|
259
|
-
const requestWebsocketTokenFromJoinSession = this.getWebsocketToken === undefined;
|
|
260
|
-
const websocketTokenPromise = requestWebsocketTokenFromJoinSession
|
|
261
|
-
? Promise.resolve(null)
|
|
262
|
-
: this.getWebsocketToken!(options);
|
|
263
|
-
|
|
264
|
-
const annotateAndRethrowConnectionError = (step: string) => (error: any) => {
|
|
265
|
-
throw this.annotateConnectionError(error, step, !requestWebsocketTokenFromJoinSession);
|
|
266
|
-
};
|
|
267
|
-
|
|
268
|
-
const joinSessionPromise = this.joinSession(requestWebsocketTokenFromJoinSession, options);
|
|
269
|
-
const [websocketEndpoint, websocketToken, io] =
|
|
270
|
-
await Promise.all([
|
|
271
|
-
joinSessionPromise.catch(annotateAndRethrowConnectionError("joinSession")),
|
|
272
|
-
websocketTokenPromise.catch(annotateAndRethrowConnectionError("getWebsocketToken")),
|
|
273
|
-
this.socketIoClientFactory().catch(annotateAndRethrowConnectionError("socketIoClientFactory")),
|
|
274
|
-
]);
|
|
275
|
-
|
|
276
|
-
const finalWebsocketToken = websocketToken ?? (websocketEndpoint.socketToken ?? null);
|
|
277
|
-
if (finalWebsocketToken === null) {
|
|
278
|
-
throw this.annotateConnectionError(
|
|
279
|
-
new NonRetryableError(
|
|
280
|
-
"Websocket token is null",
|
|
281
|
-
OdspErrorType.fetchTokenError,
|
|
282
|
-
{ driverVersion },
|
|
283
|
-
),
|
|
284
|
-
"getWebsocketToken",
|
|
285
|
-
!requestWebsocketTokenFromJoinSession);
|
|
286
|
-
}
|
|
287
|
-
try {
|
|
288
|
-
const connection = await this.createDeltaConnection(
|
|
289
|
-
websocketEndpoint.tenantId,
|
|
290
|
-
websocketEndpoint.id,
|
|
291
|
-
finalWebsocketToken,
|
|
292
|
-
io,
|
|
293
|
-
client,
|
|
294
|
-
websocketEndpoint.deltaStreamSocketUrl);
|
|
295
|
-
connection.on("op", (documentId, ops: ISequencedDocumentMessage[]) => {
|
|
296
|
-
this.opsReceived(ops);
|
|
297
|
-
});
|
|
298
|
-
// On disconnect with 401/403 error code, we can just clear the joinSession cache as we will again
|
|
299
|
-
// get the auth error on reconnecting and face latency.
|
|
300
|
-
connection.once("disconnect", (error: any) => {
|
|
301
|
-
// Clear the join session refresh timer so that it can be restarted on reconnection.
|
|
302
|
-
this.clearJoinSessionTimer();
|
|
303
|
-
if (typeof error === "object" && error !== null
|
|
304
|
-
&& error.errorType === DriverErrorType.authorizationError) {
|
|
305
|
-
this.cache.sessionJoinCache.remove(this.joinSessionKey);
|
|
306
|
-
}
|
|
307
|
-
// If we hit this assert, it means that "disconnect" event is emitted before the connection went through
|
|
308
|
-
// dispose flow which is not correct and could lead to a bunch of erros.
|
|
309
|
-
assert(connection.disposed, 0x4ae /* Connection should be disposed by now */);
|
|
310
|
-
this.currentConnection = undefined;
|
|
311
|
-
});
|
|
312
|
-
this.currentConnection = connection;
|
|
313
|
-
return connection;
|
|
314
|
-
} catch (error) {
|
|
315
|
-
this.cache.sessionJoinCache.remove(this.joinSessionKey);
|
|
316
|
-
|
|
317
|
-
const normalizedError = this.annotateConnectionError(
|
|
318
|
-
error,
|
|
319
|
-
"createDeltaConnection",
|
|
320
|
-
!requestWebsocketTokenFromJoinSession);
|
|
321
|
-
if (typeof error === "object" && error !== null) {
|
|
322
|
-
normalizedError.addTelemetryProperties({ socketDocumentId: websocketEndpoint.id });
|
|
323
|
-
}
|
|
324
|
-
throw normalizedError;
|
|
325
|
-
}
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
private clearJoinSessionTimer() {
|
|
330
|
-
if (this.joinSessionRefreshTimer !== undefined) {
|
|
331
|
-
clearTimeout(this.joinSessionRefreshTimer);
|
|
332
|
-
this.joinSessionRefreshTimer = undefined;
|
|
233
|
+
if (this.socketModuleP === undefined) {
|
|
234
|
+
this.socketModuleP = this.getDelayLoadedDeltaStream();
|
|
333
235
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
}, delta);
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
private async joinSession(
|
|
350
|
-
requestSocketToken: boolean,
|
|
351
|
-
options: TokenFetchOptionsEx,
|
|
352
|
-
) {
|
|
353
|
-
const response = await this.joinSessionCore(requestSocketToken, options).catch((e) => {
|
|
354
|
-
if (hasFacetCodes(e) && e.facetCodes !== undefined) {
|
|
355
|
-
for (const code of e.facetCodes) {
|
|
356
|
-
switch (code) {
|
|
357
|
-
case "sessionForbiddenOnPreservedFiles":
|
|
358
|
-
case "sessionForbiddenOnModerationEnabledLibrary":
|
|
359
|
-
case "sessionForbiddenOnRequireCheckout":
|
|
360
|
-
// This document can only be opened in storage-only mode.
|
|
361
|
-
// DeltaManager will recognize this error
|
|
362
|
-
// and load without a delta stream connection.
|
|
363
|
-
this._policies = { ...this._policies, storageOnly: true };
|
|
364
|
-
throw new DeltaStreamConnectionForbiddenError(code, { driverVersion });
|
|
365
|
-
default:
|
|
366
|
-
continue;
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
throw e;
|
|
371
|
-
});
|
|
372
|
-
this.relayServiceTenantAndSessionId = `${response.tenantId}/${response.id}`;
|
|
373
|
-
return response;
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
private async joinSessionCore(
|
|
377
|
-
requestSocketToken: boolean,
|
|
378
|
-
options: TokenFetchOptionsEx,
|
|
379
|
-
): Promise<ISocketStorageDiscovery> {
|
|
380
|
-
const disableJoinSessionRefresh = this.mc.config.getBoolean("Fluid.Driver.Odsp.disableJoinSessionRefresh");
|
|
381
|
-
const executeFetch = async () => {
|
|
382
|
-
const joinSessionResponse = await fetchJoinSession(
|
|
383
|
-
this.odspResolvedUrl,
|
|
384
|
-
"opStream/joinSession",
|
|
385
|
-
"POST",
|
|
386
|
-
this.mc.logger,
|
|
387
|
-
this.getStorageToken,
|
|
388
|
-
this.epochTracker,
|
|
389
|
-
requestSocketToken,
|
|
390
|
-
options,
|
|
391
|
-
disableJoinSessionRefresh,
|
|
392
|
-
this.hostPolicy.sessionOptions?.unauthenticatedUserDisplayName,
|
|
393
|
-
);
|
|
394
|
-
return {
|
|
395
|
-
entryTime: Date.now(),
|
|
396
|
-
joinSessionResponse,
|
|
397
|
-
};
|
|
398
|
-
};
|
|
399
|
-
|
|
400
|
-
const getResponseAndRefreshAfterDeltaMs = async () => {
|
|
401
|
-
const _response = await this.cache.sessionJoinCache.addOrGet(this.joinSessionKey, executeFetch);
|
|
402
|
-
// If the response does not contain refreshSessionDurationSeconds, then treat it as old flow and let the
|
|
403
|
-
// cache entry to be treated as expired after 1 hour.
|
|
404
|
-
_response.joinSessionResponse.refreshSessionDurationSeconds =
|
|
405
|
-
_response.joinSessionResponse.refreshSessionDurationSeconds ?? 3600;
|
|
406
|
-
return {
|
|
407
|
-
..._response,
|
|
408
|
-
refreshAfterDeltaMs: this.calculateJoinSessionRefreshDelta(
|
|
409
|
-
_response.entryTime, _response.joinSessionResponse.refreshSessionDurationSeconds),
|
|
410
|
-
};
|
|
411
|
-
};
|
|
412
|
-
let response = await getResponseAndRefreshAfterDeltaMs();
|
|
413
|
-
// This means that the cached entry has expired(This should not be possible if the response is fetched
|
|
414
|
-
// from the network call). In this case we remove the cached entry and fetch the new response.
|
|
415
|
-
if (response.refreshAfterDeltaMs <= 0) {
|
|
416
|
-
this.cache.sessionJoinCache.remove(this.joinSessionKey);
|
|
417
|
-
response = await getResponseAndRefreshAfterDeltaMs();
|
|
418
|
-
}
|
|
419
|
-
if (!disableJoinSessionRefresh) {
|
|
420
|
-
const props = {
|
|
421
|
-
entryTime: response.entryTime,
|
|
422
|
-
refreshSessionDurationSeconds:
|
|
423
|
-
response.joinSessionResponse.refreshSessionDurationSeconds,
|
|
424
|
-
refreshAfterDeltaMs: response.refreshAfterDeltaMs,
|
|
425
|
-
};
|
|
426
|
-
if (response.refreshAfterDeltaMs > 0) {
|
|
427
|
-
this.scheduleJoinSessionRefresh(response.refreshAfterDeltaMs)
|
|
428
|
-
.catch((error) => {
|
|
429
|
-
const canRetry = canRetryOnError(error);
|
|
430
|
-
// Only record error event in case it is non retriable.
|
|
431
|
-
if (!canRetry) {
|
|
432
|
-
this.mc.logger.sendErrorEvent({
|
|
433
|
-
eventName: "JoinSessionRefreshError",
|
|
434
|
-
details: JSON.stringify(props),
|
|
435
|
-
},
|
|
436
|
-
error,
|
|
437
|
-
);
|
|
438
|
-
}
|
|
439
|
-
});
|
|
440
|
-
} else {
|
|
441
|
-
// Logging just for informational purposes to help with debugging as this is a new feature.
|
|
442
|
-
this.mc.logger.sendTelemetryEvent({
|
|
443
|
-
eventName: "JoinSessionRefreshNotScheduled",
|
|
444
|
-
details: JSON.stringify(props),
|
|
445
|
-
});
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
return response.joinSessionResponse;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
private calculateJoinSessionRefreshDelta(responseFetchTime: number, refreshSessionDurationSeconds: number) {
|
|
452
|
-
// 30 seconds is buffer time to refresh the session.
|
|
453
|
-
return responseFetchTime + ((refreshSessionDurationSeconds * 1000) - 30000) - Date.now();
|
|
236
|
+
return this.socketModuleP
|
|
237
|
+
.then(async (m) => {
|
|
238
|
+
this.odspSocketModuleLoaded = true;
|
|
239
|
+
return m.connectToDeltaStream(client);
|
|
240
|
+
})
|
|
241
|
+
.catch((error) => {
|
|
242
|
+
// Setting undefined in case someone tries to recover from module failure by calling again.
|
|
243
|
+
this.socketModuleP = undefined;
|
|
244
|
+
this.odspSocketModuleLoaded = false;
|
|
245
|
+
throw error;
|
|
246
|
+
});
|
|
454
247
|
}
|
|
455
248
|
|
|
456
249
|
/**
|
|
457
|
-
*
|
|
458
|
-
*
|
|
459
|
-
*
|
|
460
|
-
* @
|
|
461
|
-
* @param token - authorization token for delta service
|
|
462
|
-
* @param io - websocket library
|
|
463
|
-
* @param client - information about the client
|
|
464
|
-
* @param webSocketUrl - websocket URL
|
|
250
|
+
* This dynamically imports the module for loading the delta connection. In many cases the delta stream, is not
|
|
251
|
+
* required during the critical load flow. So this way we don't have to bundle this in the initial bundle and can
|
|
252
|
+
* import this later on when required.
|
|
253
|
+
* @returns - delta stream object.
|
|
465
254
|
*/
|
|
466
|
-
private async
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
this.mc
|
|
483
|
-
|
|
255
|
+
private async getDelayLoadedDeltaStream() {
|
|
256
|
+
assert(this.odspSocketModuleLoaded === false, 0x507 /* Should be loaded only once */);
|
|
257
|
+
const module = await import(/* webpackChunkName: "socketModule" */ "./odspDelayLoadedDeltaStream")
|
|
258
|
+
.then((m) => {
|
|
259
|
+
this.mc.logger.sendTelemetryEvent({ eventName: "SocketModuleLoaded" });
|
|
260
|
+
return m;
|
|
261
|
+
})
|
|
262
|
+
.catch((error) => {
|
|
263
|
+
this.mc.logger.sendErrorEvent( { eventName: "SocketModuleLoadFailed" }, error);
|
|
264
|
+
throw error;
|
|
265
|
+
});
|
|
266
|
+
this.odspDelayLoadedDeltaStream = new module.OdspDelayLoadedDeltaStream(
|
|
267
|
+
this.odspResolvedUrl,
|
|
268
|
+
this._policies,
|
|
269
|
+
this.getStorageToken,
|
|
270
|
+
this.getWebsocketToken,
|
|
271
|
+
this.mc,
|
|
272
|
+
this.cache,
|
|
273
|
+
this.hostPolicy,
|
|
484
274
|
this.epochTracker,
|
|
275
|
+
(ops: ISequencedDocumentMessage[]) => this.opsReceived(ops),
|
|
485
276
|
this.socketReferenceKeyPrefix,
|
|
486
277
|
);
|
|
487
|
-
|
|
488
|
-
// This event happens rather often, so it adds up to cost of telemetry.
|
|
489
|
-
// Given that most reconnects result in reusing socket and happen very quickly,
|
|
490
|
-
// report event only if it took longer than threshold.
|
|
491
|
-
if (duration >= 2000) {
|
|
492
|
-
this.mc.logger.sendPerformanceEvent({
|
|
493
|
-
eventName: "ConnectionSuccess",
|
|
494
|
-
duration,
|
|
495
|
-
});
|
|
496
|
-
}
|
|
497
|
-
return connection;
|
|
278
|
+
return this.odspDelayLoadedDeltaStream;
|
|
498
279
|
}
|
|
499
280
|
|
|
500
281
|
public dispose(error?: any) {
|
|
@@ -508,9 +289,8 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
508
289
|
this._opsCache?.flushOps();
|
|
509
290
|
}
|
|
510
291
|
this._opsCache?.dispose();
|
|
511
|
-
this.
|
|
512
|
-
this.
|
|
513
|
-
this.currentConnection = undefined;
|
|
292
|
+
// Only need to dipose this, if it is already loaded.
|
|
293
|
+
this.odspDelayLoadedDeltaStream?.dispose();
|
|
514
294
|
}
|
|
515
295
|
|
|
516
296
|
protected get opsCache() {
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
HostStoragePolicy,
|
|
12
12
|
} from "@fluidframework/odsp-driver-definitions";
|
|
13
13
|
import { OdspDocumentServiceFactoryCore } from "./odspDocumentServiceFactoryCore";
|
|
14
|
-
import { getSocketIo } from "./getSocketIo";
|
|
15
14
|
// eslint-disable-next-line import/no-internal-modules
|
|
16
15
|
import { LocalOdspDocumentServiceFactory } from "./localOdspDriver/localOdspDocumentServiceFactory";
|
|
17
16
|
|
|
@@ -29,7 +28,6 @@ export class OdspDocumentServiceFactory extends OdspDocumentServiceFactoryCore {
|
|
|
29
28
|
super(
|
|
30
29
|
getStorageToken,
|
|
31
30
|
getWebsocketToken,
|
|
32
|
-
async () => getSocketIo(),
|
|
33
31
|
persistedCache,
|
|
34
32
|
hostPolicy,
|
|
35
33
|
);
|
|
@@ -31,7 +31,6 @@ import {
|
|
|
31
31
|
ShareLinkTypes,
|
|
32
32
|
ISharingLinkKind,
|
|
33
33
|
} from "@fluidframework/odsp-driver-definitions";
|
|
34
|
-
import type { io as SocketIOClientStatic } from "socket.io-client";
|
|
35
34
|
import { v4 as uuid } from "uuid";
|
|
36
35
|
import {
|
|
37
36
|
LocalPersistentCache,
|
|
@@ -90,6 +89,7 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
|
|
|
90
89
|
|
|
91
90
|
const createShareLinkParam = getSharingLinkParams(this.hostPolicy, searchParams);
|
|
92
91
|
const newFileInfo: INewFileInfo = {
|
|
92
|
+
type: 'New',
|
|
93
93
|
driveId: odspResolvedUrl.driveId,
|
|
94
94
|
siteUrl: odspResolvedUrl.siteUrl,
|
|
95
95
|
filePath,
|
|
@@ -158,7 +158,6 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
|
|
|
158
158
|
constructor(
|
|
159
159
|
private readonly getStorageToken: TokenFetcher<OdspResourceTokenFetchOptions>,
|
|
160
160
|
private readonly getWebsocketToken: TokenFetcher<OdspResourceTokenFetchOptions> | undefined,
|
|
161
|
-
private readonly getSocketIOClient: () => Promise<typeof SocketIOClientStatic>,
|
|
162
161
|
protected persistedCache: IPersistedCache = new LocalPersistentCache(),
|
|
163
162
|
private readonly hostPolicy: HostStoragePolicy = {},
|
|
164
163
|
) {
|
|
@@ -222,7 +221,6 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
|
|
|
222
221
|
storageTokenFetcher,
|
|
223
222
|
webSocketTokenFetcher,
|
|
224
223
|
odspLogger,
|
|
225
|
-
this.getSocketIOClient,
|
|
226
224
|
cacheAndTracker.cache,
|
|
227
225
|
this.hostPolicy,
|
|
228
226
|
cacheAndTracker.epochTracker,
|
|
@@ -12,6 +12,10 @@ import {
|
|
|
12
12
|
} from "@fluidframework/odsp-driver-definitions";
|
|
13
13
|
import { OdspDocumentServiceFactoryCore } from "./odspDocumentServiceFactoryCore";
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated - This is deprecated in favour of OdspDocumentServiceFactory as the socket io is now loaded inside the
|
|
17
|
+
* other dynamically imported module.
|
|
18
|
+
*/
|
|
15
19
|
export class OdspDocumentServiceFactoryWithCodeSplit
|
|
16
20
|
extends OdspDocumentServiceFactoryCore
|
|
17
21
|
implements IDocumentServiceFactory {
|
|
@@ -24,7 +28,6 @@ export class OdspDocumentServiceFactoryWithCodeSplit
|
|
|
24
28
|
super(
|
|
25
29
|
getStorageToken,
|
|
26
30
|
getWebsocketToken,
|
|
27
|
-
async () => import("./getSocketIo").then((m) => m.getSocketIo()),
|
|
28
31
|
persistedCache,
|
|
29
32
|
hostPolicy,
|
|
30
33
|
);
|
|
@@ -48,8 +48,6 @@ import { OdspDocumentStorageServiceBase } from "./odspDocumentStorageServiceBase
|
|
|
48
48
|
|
|
49
49
|
export const defaultSummarizerCacheExpiryTimeout: number = 60 * 1000; // 60 seconds.
|
|
50
50
|
|
|
51
|
-
/* eslint-disable max-len */
|
|
52
|
-
|
|
53
51
|
// An implementation of Promise.race that gives you the winner of the promise race
|
|
54
52
|
async function promiseRaceWithWinner<T>(promises: Promise<T>[]): Promise<{ index: number; value: T; }> {
|
|
55
53
|
return new Promise((resolve, reject) => {
|
|
@@ -568,5 +566,3 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
568
566
|
});
|
|
569
567
|
}
|
|
570
568
|
}
|
|
571
|
-
|
|
572
|
-
/* eslint-enable max-len */
|
|
@@ -15,8 +15,6 @@ import * as api from "@fluidframework/protocol-definitions";
|
|
|
15
15
|
import { IConfigProvider } from "@fluidframework/telemetry-utils";
|
|
16
16
|
import { ISnapshotContents } from "./odspPublicUtils";
|
|
17
17
|
|
|
18
|
-
/* eslint-disable max-len */
|
|
19
|
-
|
|
20
18
|
const maximumCacheDurationMs: FiveDaysMs = 432000000; // 5 * 24 * 60 * 60 * 1000 = 5 days in ms
|
|
21
19
|
|
|
22
20
|
class BlobCache {
|
|
@@ -288,4 +286,3 @@ export abstract class OdspDocumentStorageServiceBase implements IDocumentStorage
|
|
|
288
286
|
}
|
|
289
287
|
}
|
|
290
288
|
|
|
291
|
-
/* eslint-enable max-len */
|
|
@@ -22,8 +22,6 @@ import { EpochTracker } from "./epochTracker";
|
|
|
22
22
|
import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth";
|
|
23
23
|
import { getWithRetryForTokenRefresh } from "./odspUtils";
|
|
24
24
|
|
|
25
|
-
/* eslint-disable max-len */
|
|
26
|
-
|
|
27
25
|
/**
|
|
28
26
|
* This class manages a summary upload. When it receives a call to upload summary, it converts the summary tree into
|
|
29
27
|
* a snapshot tree and then uploads that to the server.
|
|
@@ -239,5 +237,3 @@ export class OdspSummaryUploadManager {
|
|
|
239
237
|
return { snapshotTree, blobs };
|
|
240
238
|
}
|
|
241
239
|
}
|
|
242
|
-
|
|
243
|
-
/* eslint-enable max-len */
|
package/src/odspUtils.ts
CHANGED
|
@@ -225,9 +225,15 @@ export async function fetchAndParseAsJSONHelper<T>(
|
|
|
225
225
|
return res;
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
|
|
229
|
+
export interface IFileInfoBase {
|
|
230
|
+
type: 'New' | 'Existing';
|
|
229
231
|
siteUrl: string;
|
|
230
232
|
driveId: string;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface INewFileInfo extends IFileInfoBase {
|
|
236
|
+
type: 'New';
|
|
231
237
|
filename: string;
|
|
232
238
|
filePath: string;
|
|
233
239
|
/**
|
|
@@ -240,6 +246,15 @@ export interface INewFileInfo {
|
|
|
240
246
|
createLinkType?: ShareLinkTypes | ISharingLinkKind;
|
|
241
247
|
}
|
|
242
248
|
|
|
249
|
+
export interface IExistingFileInfo extends IFileInfoBase {
|
|
250
|
+
type: 'Existing';
|
|
251
|
+
itemId: string;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function isNewFileInfo(fileInfo: INewFileInfo | IExistingFileInfo): fileInfo is INewFileInfo {
|
|
255
|
+
return fileInfo.type === undefined || fileInfo.type === 'New';
|
|
256
|
+
}
|
|
257
|
+
|
|
243
258
|
export function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl {
|
|
244
259
|
assert((resolvedUrl as IOdspResolvedUrl).odspResolvedUrl === true, 0x1de /* "Not an ODSP resolved url" */);
|
|
245
260
|
return resolvedUrl as IOdspResolvedUrl;
|
package/src/packageVersion.ts
CHANGED
|
@@ -16,7 +16,6 @@ import { ReadBuffer } from "./ReadBufferUtils";
|
|
|
16
16
|
import { pkgVersion as driverVersion } from "./packageVersion";
|
|
17
17
|
import { measure } from "./odspUtils";
|
|
18
18
|
|
|
19
|
-
// eslint-disable-next-line max-len
|
|
20
19
|
// https://onedrive.visualstudio.com/SharePoint%20Online/_git/SPO?path=/cobalt/Base/Property/BinaryEncodedPropertyReader.cs&version=GBmaster&_a=contents
|
|
21
20
|
/**
|
|
22
21
|
* Control codes used by tree serialization / decentralization code. Same as on server. These can be found on
|
package/dist/getSocketIo.d.ts
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
|
-
* Licensed under the MIT License.
|
|
4
|
-
*/
|
|
5
|
-
import io from "socket.io-client";
|
|
6
|
-
/**
|
|
7
|
-
* This function only exists to create an ESM wrapper around the socket.io client module
|
|
8
|
-
* for compatibility with ESM dynamic imports
|
|
9
|
-
*/
|
|
10
|
-
export declare function getSocketIo(): typeof io;
|
|
11
|
-
//# sourceMappingURL=getSocketIo.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"getSocketIo.d.ts","sourceRoot":"","sources":["../src/getSocketIo.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAElC;;;GAGG;AACH,wBAAgB,WAAW,IAAI,OAAO,EAAE,CAEvC"}
|