@fluidframework/odsp-driver 0.50.0 → 0.50.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fetchSnapshot.js +10 -11
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/odspDocumentService.d.ts.map +1 -1
- package/dist/odspDocumentService.js +25 -25
- package/dist/odspDocumentService.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/vroom.d.ts +4 -4
- package/dist/vroom.d.ts.map +1 -1
- package/dist/vroom.js +36 -39
- package/dist/vroom.js.map +1 -1
- package/lib/fetchSnapshot.js +10 -11
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/odspDocumentService.d.ts.map +1 -1
- package/lib/odspDocumentService.js +25 -25
- package/lib/odspDocumentService.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/vroom.d.ts +4 -4
- package/lib/vroom.d.ts.map +1 -1
- package/lib/vroom.js +37 -40
- package/lib/vroom.js.map +1 -1
- package/package.json +7 -7
- package/src/fetchSnapshot.ts +6 -6
- package/src/odspDocumentService.ts +30 -25
- package/src/packageVersion.ts +1 -1
- package/src/vroom.ts +55 -57
package/lib/packageVersion.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/odsp-driver\";\nexport const pkgVersion = \"0.50.
|
|
1
|
+
{"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,CAAC,MAAM,OAAO,GAAG,6BAA6B,CAAC;AACrD,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n *\n * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY\n */\n\nexport const pkgName = \"@fluidframework/odsp-driver\";\nexport const pkgVersion = \"0.50.4\";\n"]}
|
package/lib/vroom.d.ts
CHANGED
|
@@ -5,12 +5,11 @@
|
|
|
5
5
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
6
6
|
import { InstrumentedStorageTokenFetcher, IOdspUrlParts } from "@fluidframework/odsp-driver-definitions";
|
|
7
7
|
import { ISocketStorageDiscovery } from "./contracts";
|
|
8
|
+
import { TokenFetchOptionsEx } from "./odspUtils";
|
|
8
9
|
import { EpochTracker } from "./epochTracker";
|
|
9
10
|
/**
|
|
10
11
|
* Makes join session call on SPO to get information about the web socket for a document
|
|
11
|
-
* @param
|
|
12
|
-
* @param itemId -The SPO item id that this request should be made against
|
|
13
|
-
* @param siteUrl - The SPO site that this request should be made against
|
|
12
|
+
* @param urlParts - The SPO drive id, itemId, siteUrl that this request should be made against
|
|
14
13
|
* @param path - The API path that is relevant to this request
|
|
15
14
|
* @param method - The type of request, such as GET or POST
|
|
16
15
|
* @param logger - A logger to use for this request
|
|
@@ -18,8 +17,9 @@ import { EpochTracker } from "./epochTracker";
|
|
|
18
17
|
* @param epochTracker - fetch wrapper which incorporates epoch logic around joinSession call
|
|
19
18
|
* @param requestSocketToken - flag indicating whether joinSession is expected to return access token
|
|
20
19
|
* which is used when establishing websocket connection with collab session backend service.
|
|
20
|
+
* @param options - Options to fetch the token.
|
|
21
21
|
* @param guestDisplayName - display name used to identify guest user joining a session.
|
|
22
22
|
* This is optional and used only when collab session is being joined via invite.
|
|
23
23
|
*/
|
|
24
|
-
export declare function fetchJoinSession(urlParts: IOdspUrlParts, path: string, method: string, logger: ITelemetryLogger, getStorageToken: InstrumentedStorageTokenFetcher, epochTracker: EpochTracker, requestSocketToken: boolean, guestDisplayName?: string): Promise<ISocketStorageDiscovery>;
|
|
24
|
+
export declare function fetchJoinSession(urlParts: IOdspUrlParts, path: string, method: string, logger: ITelemetryLogger, getStorageToken: InstrumentedStorageTokenFetcher, epochTracker: EpochTracker, requestSocketToken: boolean, options: TokenFetchOptionsEx, guestDisplayName?: string): Promise<ISocketStorageDiscovery>;
|
|
25
25
|
//# sourceMappingURL=vroom.d.ts.map
|
package/lib/vroom.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vroom.d.ts","sourceRoot":"","sources":["../src/vroom.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,+BAA+B,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AACzG,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"vroom.d.ts","sourceRoot":"","sources":["../src/vroom.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEtE,OAAO,EAAE,+BAA+B,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AACzG,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAa,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ9C;;;;;;;;;;;;;GAaG;AACH,wBAAsB,gBAAgB,CAClC,QAAQ,EAAE,aAAa,EACvB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,gBAAgB,EACxB,eAAe,EAAE,+BAA+B,EAChD,YAAY,EAAE,YAAY,EAC1B,kBAAkB,EAAE,OAAO,EAC3B,OAAO,EAAE,mBAAmB,EAC5B,gBAAgB,CAAC,EAAE,MAAM,GAC1B,OAAO,CAAC,uBAAuB,CAAC,CA4DlC"}
|
package/lib/vroom.js
CHANGED
|
@@ -3,14 +3,12 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import { PerformanceEvent } from "@fluidframework/telemetry-utils";
|
|
6
|
-
import {
|
|
6
|
+
import { getOrigin } from "./odspUtils";
|
|
7
7
|
import { getApiRoot } from "./odspUrlHelper";
|
|
8
8
|
import { runWithRetry } from "./retryUtils";
|
|
9
9
|
/**
|
|
10
10
|
* Makes join session call on SPO to get information about the web socket for a document
|
|
11
|
-
* @param
|
|
12
|
-
* @param itemId -The SPO item id that this request should be made against
|
|
13
|
-
* @param siteUrl - The SPO site that this request should be made against
|
|
11
|
+
* @param urlParts - The SPO drive id, itemId, siteUrl that this request should be made against
|
|
14
12
|
* @param path - The API path that is relevant to this request
|
|
15
13
|
* @param method - The type of request, such as GET or POST
|
|
16
14
|
* @param logger - A logger to use for this request
|
|
@@ -18,47 +16,46 @@ import { runWithRetry } from "./retryUtils";
|
|
|
18
16
|
* @param epochTracker - fetch wrapper which incorporates epoch logic around joinSession call
|
|
19
17
|
* @param requestSocketToken - flag indicating whether joinSession is expected to return access token
|
|
20
18
|
* which is used when establishing websocket connection with collab session backend service.
|
|
19
|
+
* @param options - Options to fetch the token.
|
|
21
20
|
* @param guestDisplayName - display name used to identify guest user joining a session.
|
|
22
21
|
* This is optional and used only when collab session is being joined via invite.
|
|
23
22
|
*/
|
|
24
|
-
export async function fetchJoinSession(urlParts, path, method, logger, getStorageToken, epochTracker, requestSocketToken, guestDisplayName) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
export async function fetchJoinSession(urlParts, path, method, logger, getStorageToken, epochTracker, requestSocketToken, options, guestDisplayName) {
|
|
24
|
+
const token = await getStorageToken(options, "JoinSession");
|
|
25
|
+
const extraProps = options.refresh
|
|
26
|
+
? { hasClaims: !!options.claims, hasTenantId: !!options.tenantId }
|
|
27
|
+
: {};
|
|
28
|
+
return PerformanceEvent.timedExecAsync(logger, Object.assign({ eventName: "JoinSession", attempts: options.refresh ? 2 : 1 }, extraProps), async (event) => {
|
|
29
|
+
// TODO Extract the auth header-vs-query logic out
|
|
30
|
+
const siteOrigin = getOrigin(urlParts.siteUrl);
|
|
31
|
+
let queryParams = `access_token=${token}`;
|
|
32
|
+
let headers = {};
|
|
33
|
+
if (queryParams.length > 2048) {
|
|
34
|
+
queryParams = "";
|
|
35
|
+
headers = { Authorization: `Bearer ${token}` };
|
|
36
|
+
}
|
|
37
|
+
let body;
|
|
38
|
+
if (requestSocketToken || guestDisplayName) {
|
|
39
|
+
body = {};
|
|
40
|
+
if (requestSocketToken) {
|
|
41
|
+
body.requestSocketToken = true;
|
|
38
42
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
body = {};
|
|
42
|
-
if (requestSocketToken) {
|
|
43
|
-
body.requestSocketToken = true;
|
|
44
|
-
}
|
|
45
|
-
if (guestDisplayName) {
|
|
46
|
-
body.guestDisplayName = guestDisplayName;
|
|
47
|
-
}
|
|
48
|
-
// IMPORTANT: Must set content-type header explicitly to application/json when request has body.
|
|
49
|
-
// By default, request will use text/plain as content-type and will be rejected by backend.
|
|
50
|
-
headers["Content-Type"] = "application/json";
|
|
43
|
+
if (guestDisplayName) {
|
|
44
|
+
body.guestDisplayName = guestDisplayName;
|
|
51
45
|
}
|
|
52
|
-
|
|
53
|
-
//
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
46
|
+
// IMPORTANT: Must set content-type header explicitly to application/json when request has body.
|
|
47
|
+
// By default, request will use text/plain as content-type and will be rejected by backend.
|
|
48
|
+
headers["Content-Type"] = "application/json";
|
|
49
|
+
}
|
|
50
|
+
const response = await runWithRetry(async () => epochTracker.fetchAndParseAsJSON(`${getApiRoot(siteOrigin)}/drives/${urlParts.driveId}/items/${urlParts.itemId}/${path}?${queryParams}`, { method, headers, body: body ? JSON.stringify(body) : undefined }, "joinSession"), "joinSession", logger);
|
|
51
|
+
// TODO SPO-specific telemetry
|
|
52
|
+
event.end(Object.assign(Object.assign({}, response.commonSpoHeaders), {
|
|
53
|
+
// pushV2 websocket urls will contain pushf
|
|
54
|
+
pushv2: response.content.deltaStreamSocketUrl.includes("pushf") }));
|
|
55
|
+
if (response.content.runtimeTenantId && !response.content.tenantId) {
|
|
56
|
+
response.content.tenantId = response.content.runtimeTenantId;
|
|
57
|
+
}
|
|
58
|
+
return response.content;
|
|
62
59
|
});
|
|
63
60
|
}
|
|
64
61
|
//# sourceMappingURL=vroom.js.map
|
package/lib/vroom.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vroom.js","sourceRoot":"","sources":["../src/vroom.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAGnE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"vroom.js","sourceRoot":"","sources":["../src/vroom.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAGnE,OAAO,EAAE,SAAS,EAAuB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAO5C;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAClC,QAAuB,EACvB,IAAY,EACZ,MAAc,EACd,MAAwB,EACxB,eAAgD,EAChD,YAA0B,EAC1B,kBAA2B,EAC3B,OAA4B,EAC5B,gBAAyB;IAEzB,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAE5D,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO;QAC9B,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE;QAClE,CAAC,CAAC,EAAE,CAAC;IACT,OAAO,gBAAgB,CAAC,cAAc,CAClC,MAAM,kBACF,SAAS,EAAE,aAAa,EACxB,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAC9B,UAAU,GAEjB,KAAK,EAAE,KAAK,EAAE,EAAE;QACZ,kDAAkD;QAClD,MAAM,UAAU,GAAG,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QAC/C,IAAI,WAAW,GAAG,gBAAgB,KAAK,EAAE,CAAC;QAC1C,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,IAAI,WAAW,CAAC,MAAM,GAAG,IAAI,EAAE;YAC3B,WAAW,GAAG,EAAE,CAAC;YACjB,OAAO,GAAG,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,CAAC;SAClD;QACD,IAAI,IAAkC,CAAC;QACvC,IAAI,kBAAkB,IAAI,gBAAgB,EAAE;YACxC,IAAI,GAAG,EAAE,CAAC;YACV,IAAI,kBAAkB,EAAE;gBACpB,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;aAClC;YACD,IAAI,gBAAgB,EAAE;gBAClB,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;aAC5C;YACD,gGAAgG;YAChG,2FAA2F;YAC3F,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;SAChD;QAED,MAAM,QAAQ,GAAG,MAAM,YAAY,CAC/B,KAAK,IAAI,EAAE,CAAC,YAAY,CAAC,mBAAmB,CACxC,GAAG,UAAU,CAAC,UAAU,CAAC,WACrB,QAAQ,CAAC,OACb,UAAU,QAAQ,CAAC,MAAM,IAAI,IAAI,IAAI,WAAW,EAAE,EAClD,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,EAClE,aAAa,CAChB,EACD,aAAa,EACb,MAAM,CACT,CAAC;QAEF,8BAA8B;QAC9B,KAAK,CAAC,GAAG,iCACF,QAAQ,CAAC,gBAAgB;YAC5B,2CAA2C;YAC3C,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,oBAAoB,CAAC,QAAQ,CAAC,OAAO,CAAC,IACjE,CAAC;QAEH,IAAI,QAAQ,CAAC,OAAO,CAAC,eAAe,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE;YAChE,QAAQ,CAAC,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,eAAe,CAAC;SAChE;QAED,OAAO,QAAQ,CAAC,OAAO,CAAC;IAC5B,CAAC,CAAC,CAAC;AACX,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { ITelemetryLogger } from \"@fluidframework/common-definitions\";\nimport { PerformanceEvent } from \"@fluidframework/telemetry-utils\";\nimport { InstrumentedStorageTokenFetcher, IOdspUrlParts } from \"@fluidframework/odsp-driver-definitions\";\nimport { ISocketStorageDiscovery } from \"./contracts\";\nimport { getOrigin, TokenFetchOptionsEx } from \"./odspUtils\";\nimport { getApiRoot } from \"./odspUrlHelper\";\nimport { EpochTracker } from \"./epochTracker\";\nimport { runWithRetry } from \"./retryUtils\";\n\ninterface IJoinSessionBody {\n requestSocketToken?: boolean;\n guestDisplayName?: string;\n}\n\n/**\n * Makes join session call on SPO to get information about the web socket for a document\n * @param urlParts - The SPO drive id, itemId, siteUrl that this request should be made against\n * @param path - The API path that is relevant to this request\n * @param method - The type of request, such as GET or POST\n * @param logger - A logger to use for this request\n * @param getStorageToken - A function that is able to provide the access token for this request\n * @param epochTracker - fetch wrapper which incorporates epoch logic around joinSession call\n * @param requestSocketToken - flag indicating whether joinSession is expected to return access token\n * which is used when establishing websocket connection with collab session backend service.\n * @param options - Options to fetch the token.\n * @param guestDisplayName - display name used to identify guest user joining a session.\n * This is optional and used only when collab session is being joined via invite.\n */\nexport async function fetchJoinSession(\n urlParts: IOdspUrlParts,\n path: string,\n method: string,\n logger: ITelemetryLogger,\n getStorageToken: InstrumentedStorageTokenFetcher,\n epochTracker: EpochTracker,\n requestSocketToken: boolean,\n options: TokenFetchOptionsEx,\n guestDisplayName?: string,\n): Promise<ISocketStorageDiscovery> {\n const token = await getStorageToken(options, \"JoinSession\");\n\n const extraProps = options.refresh\n ? { hasClaims: !!options.claims, hasTenantId: !!options.tenantId }\n : {};\n return PerformanceEvent.timedExecAsync(\n logger, {\n eventName: \"JoinSession\",\n attempts: options.refresh ? 2 : 1,\n ...extraProps,\n },\n async (event) => {\n // TODO Extract the auth header-vs-query logic out\n const siteOrigin = getOrigin(urlParts.siteUrl);\n let queryParams = `access_token=${token}`;\n let headers = {};\n if (queryParams.length > 2048) {\n queryParams = \"\";\n headers = { Authorization: `Bearer ${token}` };\n }\n let body: IJoinSessionBody | undefined;\n if (requestSocketToken || guestDisplayName) {\n body = {};\n if (requestSocketToken) {\n body.requestSocketToken = true;\n }\n if (guestDisplayName) {\n body.guestDisplayName = guestDisplayName;\n }\n // IMPORTANT: Must set content-type header explicitly to application/json when request has body.\n // By default, request will use text/plain as content-type and will be rejected by backend.\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const response = await runWithRetry(\n async () => epochTracker.fetchAndParseAsJSON<ISocketStorageDiscovery>(\n `${getApiRoot(siteOrigin)}/drives/${\n urlParts.driveId\n }/items/${urlParts.itemId}/${path}?${queryParams}`,\n { method, headers, body: body ? JSON.stringify(body) : undefined },\n \"joinSession\",\n ),\n \"joinSession\",\n logger,\n );\n\n // TODO SPO-specific telemetry\n event.end({\n ...response.commonSpoHeaders,\n // pushV2 websocket urls will contain pushf\n pushv2: response.content.deltaStreamSocketUrl.includes(\"pushf\"),\n });\n\n if (response.content.runtimeTenantId && !response.content.tenantId) {\n response.content.tenantId = response.content.runtimeTenantId;\n }\n\n return response.content;\n });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/odsp-driver",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.4",
|
|
4
4
|
"description": "Socket storage implementation for SPO and ODC",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": "https://github.com/microsoft/FluidFramework",
|
|
@@ -58,15 +58,15 @@
|
|
|
58
58
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
59
59
|
"@fluidframework/common-utils": "^0.32.1",
|
|
60
60
|
"@fluidframework/core-interfaces": "^0.40.0",
|
|
61
|
-
"@fluidframework/driver-base": "^0.50.
|
|
61
|
+
"@fluidframework/driver-base": "^0.50.4",
|
|
62
62
|
"@fluidframework/driver-definitions": "^0.41.0",
|
|
63
|
-
"@fluidframework/driver-utils": "^0.50.
|
|
63
|
+
"@fluidframework/driver-utils": "^0.50.4",
|
|
64
64
|
"@fluidframework/gitresources": "^0.1033.0",
|
|
65
|
-
"@fluidframework/odsp-doclib-utils": "^0.50.
|
|
66
|
-
"@fluidframework/odsp-driver-definitions": "^0.50.
|
|
65
|
+
"@fluidframework/odsp-doclib-utils": "^0.50.4",
|
|
66
|
+
"@fluidframework/odsp-driver-definitions": "^0.50.4",
|
|
67
67
|
"@fluidframework/protocol-base": "^0.1033.0",
|
|
68
68
|
"@fluidframework/protocol-definitions": "^0.1025.0",
|
|
69
|
-
"@fluidframework/telemetry-utils": "^0.50.
|
|
69
|
+
"@fluidframework/telemetry-utils": "^0.50.4",
|
|
70
70
|
"abort-controller": "^3.0.0",
|
|
71
71
|
"node-fetch": "^2.6.1",
|
|
72
72
|
"socket.io-client": "^2.4.0",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@fluidframework/build-common": "^0.23.0",
|
|
77
77
|
"@fluidframework/eslint-config-fluid": "^0.23.0",
|
|
78
|
-
"@fluidframework/mocha-test-setup": "^0.50.
|
|
78
|
+
"@fluidframework/mocha-test-setup": "^0.50.4",
|
|
79
79
|
"@microsoft/api-extractor": "^7.16.1",
|
|
80
80
|
"@types/mocha": "^8.2.2",
|
|
81
81
|
"@types/node-fetch": "^2.5.10",
|
package/src/fetchSnapshot.ts
CHANGED
|
@@ -146,10 +146,10 @@ async function redeemSharingLink(
|
|
|
146
146
|
eventName: "RedeemShareLink",
|
|
147
147
|
},
|
|
148
148
|
async () => getWithRetryForTokenRefresh(async (tokenFetchOptions) => {
|
|
149
|
-
assert(!!odspResolvedUrl.
|
|
149
|
+
assert(!!odspResolvedUrl.sharingLinkToRedeem,
|
|
150
150
|
0x1ed /* "Share link should be present" */);
|
|
151
151
|
const storageToken = await storageTokenFetcher(tokenFetchOptions, "RedeemShareLink");
|
|
152
|
-
const encodedShareUrl = getEncodedShareUrl(odspResolvedUrl.
|
|
152
|
+
const encodedShareUrl = getEncodedShareUrl(odspResolvedUrl.sharingLinkToRedeem);
|
|
153
153
|
const redeemUrl = `${odspResolvedUrl.siteUrl}/_api/v2.0/shares/${encodedShareUrl}`;
|
|
154
154
|
const { url, headers } = getUrlAndHeadersWithAuth(redeemUrl, storageToken);
|
|
155
155
|
headers.prefer = "redeemSharingLink";
|
|
@@ -351,8 +351,8 @@ async function fetchSnapshotContentsCoreV1(
|
|
|
351
351
|
}
|
|
352
352
|
});
|
|
353
353
|
}
|
|
354
|
-
if (odspResolvedUrl.
|
|
355
|
-
formParams.push(`sl: ${odspResolvedUrl.
|
|
354
|
+
if (odspResolvedUrl.sharingLinkToRedeem) {
|
|
355
|
+
formParams.push(`sl: ${odspResolvedUrl.sharingLinkToRedeem}`);
|
|
356
356
|
}
|
|
357
357
|
formParams.push(`_post: 1`);
|
|
358
358
|
formParams.push(`\r\n--${formBoundary}--`);
|
|
@@ -398,9 +398,9 @@ async function fetchSnapshotContentsCoreV2(
|
|
|
398
398
|
const fullUrl = `${odspResolvedUrl.siteUrl}/_api/v2.1/drives/${odspResolvedUrl.driveId}/items/${
|
|
399
399
|
odspResolvedUrl.itemId}/opStream/attachments/latest/content`;
|
|
400
400
|
const queryParams = { ...snapshotOptions };
|
|
401
|
-
if (odspResolvedUrl.
|
|
401
|
+
if (odspResolvedUrl.sharingLinkToRedeem) {
|
|
402
402
|
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
403
|
-
queryParams["sl"] = odspResolvedUrl.
|
|
403
|
+
queryParams["sl"] = odspResolvedUrl.sharingLinkToRedeem;
|
|
404
404
|
}
|
|
405
405
|
const queryString = getQueryString(queryParams);
|
|
406
406
|
const { url, headers } = getUrlAndHeadersWithAuth(`${fullUrl}${queryString}`, storageToken);
|
|
@@ -32,7 +32,7 @@ import { IOdspCache } from "./odspCache";
|
|
|
32
32
|
import { OdspDeltaStorageService, OdspDeltaStorageWithCache } from "./odspDeltaStorageService";
|
|
33
33
|
import { OdspDocumentDeltaConnection } from "./odspDocumentDeltaConnection";
|
|
34
34
|
import { OdspDocumentStorageService } from "./odspDocumentStorageManager";
|
|
35
|
-
import { getWithRetryForTokenRefresh, getOdspResolvedUrl } from "./odspUtils";
|
|
35
|
+
import { getWithRetryForTokenRefresh, getOdspResolvedUrl, TokenFetchOptionsEx } from "./odspUtils";
|
|
36
36
|
import { fetchJoinSession } from "./vroom";
|
|
37
37
|
import { isOdcOrigin } from "./odspUrlHelper";
|
|
38
38
|
import { EpochTracker } from "./epochTracker";
|
|
@@ -231,35 +231,36 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
231
231
|
* @returns returns the document delta stream service for onedrive/sharepoint driver.
|
|
232
232
|
*/
|
|
233
233
|
public async connectToDeltaStream(client: IClient): Promise<IDocumentDeltaConnection> {
|
|
234
|
-
// Presence of getWebsocketToken callback dictates whether callback is used for fetching
|
|
235
|
-
// websocket token or whether it is returned with joinSession response payload
|
|
236
|
-
const requestWebsocketTokenFromJoinSession = this.getWebsocketToken === undefined;
|
|
237
|
-
const joinSessionPromise = this.joinSession(requestWebsocketTokenFromJoinSession).catch((e) => {
|
|
238
|
-
const likelyFacetCodes = e as IFacetCodes;
|
|
239
|
-
if (Array.isArray(likelyFacetCodes.facetCodes)) {
|
|
240
|
-
for (const code of likelyFacetCodes.facetCodes) {
|
|
241
|
-
switch (code) {
|
|
242
|
-
case "sessionForbiddenOnPreservedFiles":
|
|
243
|
-
case "sessionForbiddenOnModerationEnabledLibrary":
|
|
244
|
-
case "sessionForbiddenOnRequireCheckout":
|
|
245
|
-
// This document can only be opened in storage-only mode.
|
|
246
|
-
// DeltaManager will recognize this error
|
|
247
|
-
// and load without a delta stream connection.
|
|
248
|
-
this._policies = {...this._policies,storageOnly: true};
|
|
249
|
-
throw new DeltaStreamConnectionForbiddenError(code);
|
|
250
|
-
default:
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
throw e;
|
|
256
|
-
});
|
|
257
234
|
// Attempt to connect twice, in case we used expired token.
|
|
258
235
|
return getWithRetryForTokenRefresh<IDocumentDeltaConnection>(async (options) => {
|
|
236
|
+
// Presence of getWebsocketToken callback dictates whether callback is used for fetching
|
|
237
|
+
// websocket token or whether it is returned with joinSession response payload
|
|
238
|
+
const requestWebsocketTokenFromJoinSession = this.getWebsocketToken === undefined;
|
|
259
239
|
const websocketTokenPromise = requestWebsocketTokenFromJoinSession
|
|
260
240
|
? Promise.resolve(null)
|
|
261
241
|
: this.getWebsocketToken!(options);
|
|
262
242
|
|
|
243
|
+
const joinSessionPromise = this.joinSession(requestWebsocketTokenFromJoinSession, options).catch((e) => {
|
|
244
|
+
const likelyFacetCodes = e as IFacetCodes;
|
|
245
|
+
if (Array.isArray(likelyFacetCodes.facetCodes)) {
|
|
246
|
+
for (const code of likelyFacetCodes.facetCodes) {
|
|
247
|
+
switch (code) {
|
|
248
|
+
case "sessionForbiddenOnPreservedFiles":
|
|
249
|
+
case "sessionForbiddenOnModerationEnabledLibrary":
|
|
250
|
+
case "sessionForbiddenOnRequireCheckout":
|
|
251
|
+
// This document can only be opened in storage-only mode.
|
|
252
|
+
// DeltaManager will recognize this error
|
|
253
|
+
// and load without a delta stream connection.
|
|
254
|
+
this._policies = {...this._policies,storageOnly: true};
|
|
255
|
+
throw new DeltaStreamConnectionForbiddenError(code);
|
|
256
|
+
default:
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
throw e;
|
|
262
|
+
});
|
|
263
|
+
|
|
263
264
|
const [websocketEndpoint, websocketToken, io] =
|
|
264
265
|
await Promise.all([
|
|
265
266
|
joinSessionPromise,
|
|
@@ -294,7 +295,10 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
294
295
|
});
|
|
295
296
|
}
|
|
296
297
|
|
|
297
|
-
private async joinSession(
|
|
298
|
+
private async joinSession(
|
|
299
|
+
requestSocketToken: boolean,
|
|
300
|
+
options: TokenFetchOptionsEx,
|
|
301
|
+
): Promise<ISocketStorageDiscovery> {
|
|
298
302
|
const executeFetch = async () =>
|
|
299
303
|
fetchJoinSession(
|
|
300
304
|
this.odspResolvedUrl,
|
|
@@ -304,6 +308,7 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
304
308
|
this.getStorageToken,
|
|
305
309
|
this.epochTracker,
|
|
306
310
|
requestSocketToken,
|
|
311
|
+
options,
|
|
307
312
|
this.hostPolicy.sessionOptions?.unauthenticatedUserDisplayName,
|
|
308
313
|
);
|
|
309
314
|
|
package/src/packageVersion.ts
CHANGED
package/src/vroom.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
|
7
7
|
import { PerformanceEvent } from "@fluidframework/telemetry-utils";
|
|
8
8
|
import { InstrumentedStorageTokenFetcher, IOdspUrlParts } from "@fluidframework/odsp-driver-definitions";
|
|
9
9
|
import { ISocketStorageDiscovery } from "./contracts";
|
|
10
|
-
import {
|
|
10
|
+
import { getOrigin, TokenFetchOptionsEx } from "./odspUtils";
|
|
11
11
|
import { getApiRoot } from "./odspUrlHelper";
|
|
12
12
|
import { EpochTracker } from "./epochTracker";
|
|
13
13
|
import { runWithRetry } from "./retryUtils";
|
|
@@ -19,9 +19,7 @@ interface IJoinSessionBody {
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Makes join session call on SPO to get information about the web socket for a document
|
|
22
|
-
* @param
|
|
23
|
-
* @param itemId -The SPO item id that this request should be made against
|
|
24
|
-
* @param siteUrl - The SPO site that this request should be made against
|
|
22
|
+
* @param urlParts - The SPO drive id, itemId, siteUrl that this request should be made against
|
|
25
23
|
* @param path - The API path that is relevant to this request
|
|
26
24
|
* @param method - The type of request, such as GET or POST
|
|
27
25
|
* @param logger - A logger to use for this request
|
|
@@ -29,6 +27,7 @@ interface IJoinSessionBody {
|
|
|
29
27
|
* @param epochTracker - fetch wrapper which incorporates epoch logic around joinSession call
|
|
30
28
|
* @param requestSocketToken - flag indicating whether joinSession is expected to return access token
|
|
31
29
|
* which is used when establishing websocket connection with collab session backend service.
|
|
30
|
+
* @param options - Options to fetch the token.
|
|
32
31
|
* @param guestDisplayName - display name used to identify guest user joining a session.
|
|
33
32
|
* This is optional and used only when collab session is being joined via invite.
|
|
34
33
|
*/
|
|
@@ -40,67 +39,66 @@ export async function fetchJoinSession(
|
|
|
40
39
|
getStorageToken: InstrumentedStorageTokenFetcher,
|
|
41
40
|
epochTracker: EpochTracker,
|
|
42
41
|
requestSocketToken: boolean,
|
|
42
|
+
options: TokenFetchOptionsEx,
|
|
43
43
|
guestDisplayName?: string,
|
|
44
44
|
): Promise<ISocketStorageDiscovery> {
|
|
45
|
-
|
|
46
|
-
const token = await getStorageToken(options, "JoinSession");
|
|
45
|
+
const token = await getStorageToken(options, "JoinSession");
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
47
|
+
const extraProps = options.refresh
|
|
48
|
+
? { hasClaims: !!options.claims, hasTenantId: !!options.tenantId }
|
|
49
|
+
: {};
|
|
50
|
+
return PerformanceEvent.timedExecAsync(
|
|
51
|
+
logger, {
|
|
52
|
+
eventName: "JoinSession",
|
|
53
|
+
attempts: options.refresh ? 2 : 1,
|
|
54
|
+
...extraProps,
|
|
55
|
+
},
|
|
56
|
+
async (event) => {
|
|
57
|
+
// TODO Extract the auth header-vs-query logic out
|
|
58
|
+
const siteOrigin = getOrigin(urlParts.siteUrl);
|
|
59
|
+
let queryParams = `access_token=${token}`;
|
|
60
|
+
let headers = {};
|
|
61
|
+
if (queryParams.length > 2048) {
|
|
62
|
+
queryParams = "";
|
|
63
|
+
headers = { Authorization: `Bearer ${token}` };
|
|
64
|
+
}
|
|
65
|
+
let body: IJoinSessionBody | undefined;
|
|
66
|
+
if (requestSocketToken || guestDisplayName) {
|
|
67
|
+
body = {};
|
|
68
|
+
if (requestSocketToken) {
|
|
69
|
+
body.requestSocketToken = true;
|
|
65
70
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
body = {};
|
|
69
|
-
if (requestSocketToken) {
|
|
70
|
-
body.requestSocketToken = true;
|
|
71
|
-
}
|
|
72
|
-
if (guestDisplayName) {
|
|
73
|
-
body.guestDisplayName = guestDisplayName;
|
|
74
|
-
}
|
|
75
|
-
// IMPORTANT: Must set content-type header explicitly to application/json when request has body.
|
|
76
|
-
// By default, request will use text/plain as content-type and will be rejected by backend.
|
|
77
|
-
headers["Content-Type"] = "application/json";
|
|
71
|
+
if (guestDisplayName) {
|
|
72
|
+
body.guestDisplayName = guestDisplayName;
|
|
78
73
|
}
|
|
74
|
+
// IMPORTANT: Must set content-type header explicitly to application/json when request has body.
|
|
75
|
+
// By default, request will use text/plain as content-type and will be rejected by backend.
|
|
76
|
+
headers["Content-Type"] = "application/json";
|
|
77
|
+
}
|
|
79
78
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
"joinSession",
|
|
87
|
-
),
|
|
79
|
+
const response = await runWithRetry(
|
|
80
|
+
async () => epochTracker.fetchAndParseAsJSON<ISocketStorageDiscovery>(
|
|
81
|
+
`${getApiRoot(siteOrigin)}/drives/${
|
|
82
|
+
urlParts.driveId
|
|
83
|
+
}/items/${urlParts.itemId}/${path}?${queryParams}`,
|
|
84
|
+
{ method, headers, body: body ? JSON.stringify(body) : undefined },
|
|
88
85
|
"joinSession",
|
|
89
|
-
|
|
90
|
-
|
|
86
|
+
),
|
|
87
|
+
"joinSession",
|
|
88
|
+
logger,
|
|
89
|
+
);
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
91
|
+
// TODO SPO-specific telemetry
|
|
92
|
+
event.end({
|
|
93
|
+
...response.commonSpoHeaders,
|
|
94
|
+
// pushV2 websocket urls will contain pushf
|
|
95
|
+
pushv2: response.content.deltaStreamSocketUrl.includes("pushf"),
|
|
96
|
+
});
|
|
98
97
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
if (response.content.runtimeTenantId && !response.content.tenantId) {
|
|
99
|
+
response.content.tenantId = response.content.runtimeTenantId;
|
|
100
|
+
}
|
|
102
101
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
});
|
|
102
|
+
return response.content;
|
|
103
|
+
});
|
|
106
104
|
}
|