@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.
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export const pkgName = "@fluidframework/odsp-driver";
8
- export const pkgVersion = "0.50.0";
8
+ export const pkgVersion = "0.50.4";
9
9
  //# sourceMappingURL=packageVersion.js.map
@@ -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.0\";\n"]}
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 driveId - The SPO drive id that this request should be made against
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
@@ -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;AAGtD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAQ9C;;;;;;;;;;;;;;GAcG;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,gBAAgB,CAAC,EAAE,MAAM,GAC1B,OAAO,CAAC,uBAAuB,CAAC,CA8DlC"}
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 { getWithRetryForTokenRefresh, getOrigin } from "./odspUtils";
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 driveId - The SPO drive id that this request should be made against
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
- return getWithRetryForTokenRefresh(async (options) => {
26
- const token = await getStorageToken(options, "JoinSession");
27
- const extraProps = options.refresh
28
- ? { hasClaims: !!options.claims, hasTenantId: !!options.tenantId }
29
- : {};
30
- return PerformanceEvent.timedExecAsync(logger, Object.assign({ eventName: "JoinSession", attempts: options.refresh ? 2 : 1 }, extraProps), async (event) => {
31
- // TODO Extract the auth header-vs-query logic out
32
- const siteOrigin = getOrigin(urlParts.siteUrl);
33
- let queryParams = `access_token=${token}`;
34
- let headers = {};
35
- if (queryParams.length > 2048) {
36
- queryParams = "";
37
- headers = { Authorization: `Bearer ${token}` };
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
- let body;
40
- if (requestSocketToken || guestDisplayName) {
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
- 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);
53
- // TODO SPO-specific telemetry
54
- event.end(Object.assign(Object.assign({}, response.commonSpoHeaders), {
55
- // pushV2 websocket urls will contain pushf
56
- pushv2: response.content.deltaStreamSocketUrl.includes("pushf") }));
57
- if (response.content.runtimeTenantId && !response.content.tenantId) {
58
- response.content.tenantId = response.content.runtimeTenantId;
59
- }
60
- return response.content;
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,2BAA2B,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAO5C;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAClC,QAAuB,EACvB,IAAY,EACZ,MAAc,EACd,MAAwB,EACxB,eAAgD,EAChD,YAA0B,EAC1B,kBAA2B,EAC3B,gBAAyB;IAEzB,OAAO,2BAA2B,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACjD,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QAE5D,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO;YAC9B,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE;YAClE,CAAC,CAAC,EAAE,CAAC;QACT,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;YACZ,kDAAkD;YAClD,MAAM,UAAU,GAAG,SAAS,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC/C,IAAI,WAAW,GAAG,gBAAgB,KAAK,EAAE,CAAC;YAC1C,IAAI,OAAO,GAAG,EAAE,CAAC;YACjB,IAAI,WAAW,CAAC,MAAM,GAAG,IAAI,EAAE;gBAC3B,WAAW,GAAG,EAAE,CAAC;gBACjB,OAAO,GAAG,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,CAAC;aAClD;YACD,IAAI,IAAkC,CAAC;YACvC,IAAI,kBAAkB,IAAI,gBAAgB,EAAE;gBACxC,IAAI,GAAG,EAAE,CAAC;gBACV,IAAI,kBAAkB,EAAE;oBACpB,IAAI,CAAC,kBAAkB,GAAG,IAAI,CAAC;iBAClC;gBACD,IAAI,gBAAgB,EAAE;oBAClB,IAAI,CAAC,gBAAgB,GAAG,gBAAgB,CAAC;iBAC5C;gBACD,gGAAgG;gBAChG,2FAA2F;gBAC3F,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAC;aAChD;YAED,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;YAEF,8BAA8B;YAC9B,KAAK,CAAC,GAAG,iCACF,QAAQ,CAAC,gBAAgB;gBAC5B,2CAA2C;gBAC3C,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,oBAAoB,CAAC,QAAQ,CAAC,OAAO,CAAC,IACjE,CAAC;YAEH,IAAI,QAAQ,CAAC,OAAO,CAAC,eAAe,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE;gBAChE,QAAQ,CAAC,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,eAAe,CAAC;aAChE;YAED,OAAO,QAAQ,CAAC,OAAO,CAAC;QAC5B,CAAC,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;AACP,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 { getWithRetryForTokenRefresh, getOrigin } 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 driveId - The SPO drive id that this request should be made against\n * @param itemId -The SPO item id that this request should be made against\n * @param siteUrl - The SPO site 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 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 guestDisplayName?: string,\n): Promise<ISocketStorageDiscovery> {\n return getWithRetryForTokenRefresh(async (options) => {\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}\n"]}
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.0",
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.0",
61
+ "@fluidframework/driver-base": "^0.50.4",
62
62
  "@fluidframework/driver-definitions": "^0.41.0",
63
- "@fluidframework/driver-utils": "^0.50.0",
63
+ "@fluidframework/driver-utils": "^0.50.4",
64
64
  "@fluidframework/gitresources": "^0.1033.0",
65
- "@fluidframework/odsp-doclib-utils": "^0.50.0",
66
- "@fluidframework/odsp-driver-definitions": "^0.50.0",
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.0",
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.0",
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",
@@ -146,10 +146,10 @@ async function redeemSharingLink(
146
146
  eventName: "RedeemShareLink",
147
147
  },
148
148
  async () => getWithRetryForTokenRefresh(async (tokenFetchOptions) => {
149
- assert(!!odspResolvedUrl.shareLinkInfo?.sharingLinkToRedeem,
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.shareLinkInfo.sharingLinkToRedeem);
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.shareLinkInfo?.sharingLinkToRedeem) {
355
- formParams.push(`sl: ${odspResolvedUrl.shareLinkInfo.sharingLinkToRedeem}`);
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.shareLinkInfo?.sharingLinkToRedeem) {
401
+ if (odspResolvedUrl.sharingLinkToRedeem) {
402
402
  // eslint-disable-next-line @typescript-eslint/dot-notation
403
- queryParams["sl"] = odspResolvedUrl.shareLinkInfo.sharingLinkToRedeem;
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(requestSocketToken: boolean): Promise<ISocketStorageDiscovery> {
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
 
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/odsp-driver";
9
- export const pkgVersion = "0.50.0";
9
+ export const pkgVersion = "0.50.4";
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 { getWithRetryForTokenRefresh, getOrigin } from "./odspUtils";
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 driveId - The SPO drive id that this request should be made against
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
- return getWithRetryForTokenRefresh(async (options) => {
46
- const token = await getStorageToken(options, "JoinSession");
45
+ const token = await getStorageToken(options, "JoinSession");
47
46
 
48
- const extraProps = options.refresh
49
- ? { hasClaims: !!options.claims, hasTenantId: !!options.tenantId }
50
- : {};
51
- return PerformanceEvent.timedExecAsync(
52
- logger, {
53
- eventName: "JoinSession",
54
- attempts: options.refresh ? 2 : 1,
55
- ...extraProps,
56
- },
57
- async (event) => {
58
- // TODO Extract the auth header-vs-query logic out
59
- const siteOrigin = getOrigin(urlParts.siteUrl);
60
- let queryParams = `access_token=${token}`;
61
- let headers = {};
62
- if (queryParams.length > 2048) {
63
- queryParams = "";
64
- headers = { Authorization: `Bearer ${token}` };
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
- let body: IJoinSessionBody | undefined;
67
- if (requestSocketToken || guestDisplayName) {
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
- const response = await runWithRetry(
81
- async () => epochTracker.fetchAndParseAsJSON<ISocketStorageDiscovery>(
82
- `${getApiRoot(siteOrigin)}/drives/${
83
- urlParts.driveId
84
- }/items/${urlParts.itemId}/${path}?${queryParams}`,
85
- { method, headers, body: body ? JSON.stringify(body) : undefined },
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
- logger,
90
- );
86
+ ),
87
+ "joinSession",
88
+ logger,
89
+ );
91
90
 
92
- // TODO SPO-specific telemetry
93
- event.end({
94
- ...response.commonSpoHeaders,
95
- // pushV2 websocket urls will contain pushf
96
- pushv2: response.content.deltaStreamSocketUrl.includes("pushf"),
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
- if (response.content.runtimeTenantId && !response.content.tenantId) {
100
- response.content.tenantId = response.content.runtimeTenantId;
101
- }
98
+ if (response.content.runtimeTenantId && !response.content.tenantId) {
99
+ response.content.tenantId = response.content.runtimeTenantId;
100
+ }
102
101
 
103
- return response.content;
104
- });
105
- });
102
+ return response.content;
103
+ });
106
104
  }