@fluidframework/odsp-driver 2.0.0-internal.1.0.0.82693 → 2.0.0-internal.1.1.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/.mocharc.js +12 -0
- package/README.md +19 -0
- package/dist/contracts.d.ts +1 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/createFile.d.ts +1 -1
- package/dist/createFile.d.ts.map +1 -1
- package/dist/createFile.js +53 -16
- package/dist/createFile.js.map +1 -1
- package/dist/createOdspCreateContainerRequest.d.ts +4 -3
- package/dist/createOdspCreateContainerRequest.d.ts.map +1 -1
- package/dist/createOdspCreateContainerRequest.js +6 -3
- package/dist/createOdspCreateContainerRequest.js.map +1 -1
- package/dist/fetchSnapshot.d.ts.map +1 -1
- package/dist/fetchSnapshot.js +10 -5
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/odspDeltaStorageService.d.ts +1 -1
- package/dist/odspDeltaStorageService.js +1 -1
- package/dist/odspDeltaStorageService.js.map +1 -1
- package/dist/odspDocumentService.js +1 -1
- package/dist/odspDocumentService.js.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/dist/odspDocumentServiceFactoryCore.js +29 -6
- package/dist/odspDocumentServiceFactoryCore.js.map +1 -1
- package/dist/odspDocumentStorageManager.d.ts.map +1 -1
- package/dist/odspDocumentStorageManager.js +5 -3
- package/dist/odspDocumentStorageManager.js.map +1 -1
- package/dist/odspUtils.d.ts +11 -2
- package/dist/odspUtils.d.ts.map +1 -1
- package/dist/odspUtils.js +21 -1
- package/dist/odspUtils.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.d.ts.map +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/dist/retryUtils.d.ts.map +1 -1
- package/dist/retryUtils.js +8 -4
- package/dist/retryUtils.js.map +1 -1
- package/lib/contracts.d.ts +1 -0
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/createFile.d.ts +1 -1
- package/lib/createFile.d.ts.map +1 -1
- package/lib/createFile.js +54 -17
- package/lib/createFile.js.map +1 -1
- package/lib/createOdspCreateContainerRequest.d.ts +4 -3
- package/lib/createOdspCreateContainerRequest.d.ts.map +1 -1
- package/lib/createOdspCreateContainerRequest.js +6 -3
- package/lib/createOdspCreateContainerRequest.js.map +1 -1
- package/lib/fetchSnapshot.d.ts.map +1 -1
- package/lib/fetchSnapshot.js +10 -5
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/odspDeltaStorageService.d.ts +1 -1
- package/lib/odspDeltaStorageService.js +1 -1
- package/lib/odspDeltaStorageService.js.map +1 -1
- package/lib/odspDocumentService.js +1 -1
- package/lib/odspDocumentService.js.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.d.ts.map +1 -1
- package/lib/odspDocumentServiceFactoryCore.js +29 -6
- package/lib/odspDocumentServiceFactoryCore.js.map +1 -1
- package/lib/odspDocumentStorageManager.d.ts.map +1 -1
- package/lib/odspDocumentStorageManager.js +5 -3
- package/lib/odspDocumentStorageManager.js.map +1 -1
- package/lib/odspUtils.d.ts +11 -2
- package/lib/odspUtils.d.ts.map +1 -1
- package/lib/odspUtils.js +19 -0
- package/lib/odspUtils.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.d.ts.map +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/lib/retryUtils.d.ts.map +1 -1
- package/lib/retryUtils.js +9 -5
- package/lib/retryUtils.js.map +1 -1
- package/package.json +15 -15
- package/src/contracts.ts +2 -0
- package/src/createFile.ts +75 -15
- package/src/createOdspCreateContainerRequest.ts +7 -4
- package/src/fetchSnapshot.ts +9 -5
- package/src/odspDeltaStorageService.ts +1 -1
- package/src/odspDocumentService.ts +1 -1
- package/src/odspDocumentServiceFactoryCore.ts +40 -4
- package/src/odspDocumentStorageManager.ts +5 -3
- package/src/odspUtils.ts +24 -1
- package/src/packageVersion.ts +1 -1
- package/src/retryUtils.ts +8 -5
package/lib/retryUtils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retryUtils.js","sourceRoot":"","sources":["../src/retryUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"retryUtils.js","sourceRoot":"","sources":["../src/retryUtils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAC9B,GAAqB,EACrB,QAAgB,EAChB,MAAwB,EACxB,aAA0B;;IAE1B,IAAI,UAAU,GAAG,IAAI,CAAC;IACtB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAChC,IAAI,SAAc,CAAC;IACnB,KAAK,IAAI,QAAQ,GAAG,CAAC,GAAI,QAAQ,EAAE,EAAE;QACjC,IAAI,aAAa,KAAK,SAAS,EAAE;YAC7B,aAAa,EAAE,CAAC;SACnB;QACD,IAAI;YACA,MAAM,MAAM,GAAG,MAAM,GAAG,EAAE,CAAC;YAC3B,IAAI,QAAQ,GAAG,CAAC,EAAE;gBACd,MAAM,CAAC,kBAAkB,CACrB;oBACI,SAAS,EAAE,iBAAiB;oBAC5B,QAAQ;oBACR,QAAQ;oBACR,QAAQ,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK;iBACtC,EACD,SAAS,CAAC,CAAC;aAClB;YACD,OAAO,MAAM,CAAC;SACjB;QAAC,OAAO,KAAU,EAAE;YACjB,MAAM,QAAQ,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;YAExC,MAAM,cAAc,GAAG,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAG,YAAY,CAAC,MAAK,IAAI,CAAC;YACtD,MAAM,oBAAoB,GAAG,CAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,SAAS,MAAK,aAAa,CAAC,eAAe,CAAC;YAChF,wGAAwG;YACxG,oGAAoG;YACpG,yBAAyB;YACzB,IAAI,CAAC,CAAC,CAAC,cAAc,IAAI,oBAAoB,CAAC,IAAI,QAAQ,CAAC,EAAE;gBACzD,MAAM,KAAK,CAAC;aACf;YAED,+EAA+E;YAC/E,oFAAoF;YACpF,uEAAuE;YACvE,IAAI,QAAQ,KAAK,CAAC,EAAE;gBAChB,MAAM,CAAC,cAAc,CACjB;oBACI,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC;wBACxD,oCAAoC;oBACxC,QAAQ;oBACR,QAAQ;oBACR,QAAQ,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,0BAA0B;iBAClE,EACD,KAAK,CAAC,CAAC;gBACX,aAAa;gBACb,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC;gBACvB,MAAM,KAAK,CAAC;aACf;YAED,UAAU,GAAG,MAAA,sBAAsB,CAAC,KAAK,CAAC,mCAAI,UAAU,CAAC;YACzD,MAAM,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;YACpC,UAAU,IAAI,UAAU,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACnD,SAAS,GAAG,KAAK,CAAC;SACrB;KACJ;AACL,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 { delay, performance } from \"@fluidframework/common-utils\";\nimport { canRetryOnError, getRetryDelayFromError } from \"@fluidframework/driver-utils\";\nimport { OdspErrorType } from \"@fluidframework/odsp-driver-definitions\";\nimport { Odsp409Error } from \"./epochTracker\";\n\n/**\n * This method retries only for retriable coherency and service read only errors.\n */\nexport async function runWithRetry<T>(\n api: () => Promise<T>,\n callName: string,\n logger: ITelemetryLogger,\n checkDisposed?: () => void,\n): Promise<T> {\n let retryAfter = 1000;\n const start = performance.now();\n let lastError: any;\n for (let attempts = 1; ; attempts++) {\n if (checkDisposed !== undefined) {\n checkDisposed();\n }\n try {\n const result = await api();\n if (attempts > 1) {\n logger.sendTelemetryEvent(\n {\n eventName: \"MultipleRetries\",\n callName,\n attempts,\n duration: performance.now() - start,\n },\n lastError);\n }\n return result;\n } catch (error: any) {\n const canRetry = canRetryOnError(error);\n\n const coherencyError = error?.[Odsp409Error] === true;\n const serviceReadonlyError = error?.errorType === OdspErrorType.serviceReadOnly;\n // Retry for retriable 409 coherency errors or serviceReadOnly errors. These errors are always retriable\n // unless someone specifically set canRetry = false on the error like in fetchSnapshot() flow. So in\n // that case don't retry.\n if (!((coherencyError || serviceReadonlyError) && canRetry)) {\n throw error;\n }\n\n // SPO itself does number of retries internally before returning 409 to client.\n // That multiplied to 5 suggests need to reconsider current design, as client spends\n // too much time / bandwidth doing the same thing without any progress.\n if (attempts === 5) {\n logger.sendErrorEvent(\n {\n eventName: coherencyError ? \"CoherencyErrorTooManyRetries\" :\n \"ServiceReadonlyErrorTooManyRetries\",\n callName,\n attempts,\n duration: performance.now() - start, // record total wait time.\n },\n error);\n // Fail hard.\n error.canRetry = false;\n throw error;\n }\n\n retryAfter = getRetryDelayFromError(error) ?? retryAfter;\n await delay(Math.floor(retryAfter));\n retryAfter += retryAfter / 4 * (1 + Math.random());\n lastError = error;\n }\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluidframework/odsp-driver",
|
|
3
|
-
"version": "2.0.0-internal.1.
|
|
3
|
+
"version": "2.0.0-internal.1.1.0",
|
|
4
4
|
"description": "Socket storage implementation for SPO and ODC",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -61,17 +61,17 @@
|
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
64
|
-
"@fluidframework/common-utils": "^0.
|
|
65
|
-
"@fluidframework/core-interfaces": "2.0.0-internal.1.
|
|
66
|
-
"@fluidframework/driver-base": "2.0.0-internal.1.
|
|
67
|
-
"@fluidframework/driver-definitions": "2.0.0-internal.1.
|
|
68
|
-
"@fluidframework/driver-utils": "2.0.0-internal.1.
|
|
69
|
-
"@fluidframework/gitresources": "^0.1037.1000
|
|
70
|
-
"@fluidframework/odsp-doclib-utils": "2.0.0-internal.1.
|
|
71
|
-
"@fluidframework/odsp-driver-definitions": "2.0.0-internal.1.
|
|
72
|
-
"@fluidframework/protocol-base": "^0.1037.1000
|
|
73
|
-
"@fluidframework/protocol-definitions": "^0.
|
|
74
|
-
"@fluidframework/telemetry-utils": "2.0.0-internal.1.
|
|
64
|
+
"@fluidframework/common-utils": "^1.0.0",
|
|
65
|
+
"@fluidframework/core-interfaces": "^2.0.0-internal.1.1.0",
|
|
66
|
+
"@fluidframework/driver-base": "^2.0.0-internal.1.1.0",
|
|
67
|
+
"@fluidframework/driver-definitions": "^2.0.0-internal.1.1.0",
|
|
68
|
+
"@fluidframework/driver-utils": "^2.0.0-internal.1.1.0",
|
|
69
|
+
"@fluidframework/gitresources": "^0.1037.1000",
|
|
70
|
+
"@fluidframework/odsp-doclib-utils": "^2.0.0-internal.1.1.0",
|
|
71
|
+
"@fluidframework/odsp-driver-definitions": "^2.0.0-internal.1.1.0",
|
|
72
|
+
"@fluidframework/protocol-base": "^0.1037.1000",
|
|
73
|
+
"@fluidframework/protocol-definitions": "^1.0.0",
|
|
74
|
+
"@fluidframework/telemetry-utils": "^2.0.0-internal.1.1.0",
|
|
75
75
|
"abort-controller": "^3.0.0",
|
|
76
76
|
"node-fetch": "^2.6.1",
|
|
77
77
|
"socket.io-client": "^4.4.1",
|
|
@@ -79,9 +79,9 @@
|
|
|
79
79
|
},
|
|
80
80
|
"devDependencies": {
|
|
81
81
|
"@fluidframework/build-common": "^0.24.0",
|
|
82
|
-
"@fluidframework/build-tools": "^0.3.
|
|
82
|
+
"@fluidframework/build-tools": "^0.3.1000",
|
|
83
83
|
"@fluidframework/eslint-config-fluid": "^0.28.2000",
|
|
84
|
-
"@fluidframework/mocha-test-setup": "2.0.0-internal.1.
|
|
84
|
+
"@fluidframework/mocha-test-setup": "^2.0.0-internal.1.1.0",
|
|
85
85
|
"@fluidframework/odsp-driver-previous": "npm:@fluidframework/odsp-driver@^1.0.0",
|
|
86
86
|
"@microsoft/api-extractor": "^7.22.2",
|
|
87
87
|
"@rushstack/eslint-config": "^2.5.1",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"@types/node-fetch": "^2.5.10",
|
|
90
90
|
"@types/sha.js": "^2.4.0",
|
|
91
91
|
"concurrently": "^6.2.0",
|
|
92
|
-
"copyfiles": "^2.1
|
|
92
|
+
"copyfiles": "^2.4.1",
|
|
93
93
|
"cross-env": "^7.0.2",
|
|
94
94
|
"eslint": "~8.6.0",
|
|
95
95
|
"mocha": "^10.0.0",
|
package/src/contracts.ts
CHANGED
|
@@ -216,6 +216,8 @@ export interface ICreateFileResponse {
|
|
|
216
216
|
itemId: string;
|
|
217
217
|
itemUrl: string;
|
|
218
218
|
sequenceNumber: number;
|
|
219
|
+
// sharing object contains shareId, sharingLink data or error in the response
|
|
220
|
+
sharing?: any;
|
|
219
221
|
sharingLink?: string;
|
|
220
222
|
sharingLinkErrorReason?: string;
|
|
221
223
|
}
|
package/src/createFile.ts
CHANGED
|
@@ -15,6 +15,9 @@ import {
|
|
|
15
15
|
InstrumentedStorageTokenFetcher,
|
|
16
16
|
IOdspResolvedUrl,
|
|
17
17
|
OdspErrorType,
|
|
18
|
+
ShareLinkInfoType,
|
|
19
|
+
ISharingLinkKind,
|
|
20
|
+
ShareLinkTypes,
|
|
18
21
|
} from "@fluidframework/odsp-driver-definitions";
|
|
19
22
|
import { DriverErrorType } from "@fluidframework/driver-definitions";
|
|
20
23
|
import {
|
|
@@ -26,6 +29,7 @@ import {
|
|
|
26
29
|
} from "./contracts";
|
|
27
30
|
import { getUrlAndHeadersWithAuth } from "./getUrlAndHeadersWithAuth";
|
|
28
31
|
import {
|
|
32
|
+
buildOdspShareLinkReqParams,
|
|
29
33
|
createCacheSnapshotKey,
|
|
30
34
|
getWithRetryForTokenRefresh,
|
|
31
35
|
INewFileInfo,
|
|
@@ -61,6 +65,8 @@ export async function createNewFluidFile(
|
|
|
61
65
|
createNewCaching: boolean,
|
|
62
66
|
forceAccessTokenViaAuthorizationHeader: boolean,
|
|
63
67
|
isClpCompliantApp?: boolean,
|
|
68
|
+
enableSingleRequestForShareLinkWithCreate?: boolean,
|
|
69
|
+
enableShareLinkWithCreate?: boolean,
|
|
64
70
|
): Promise<IOdspResolvedUrl> {
|
|
65
71
|
// Check for valid filename before the request to create file is actually made.
|
|
66
72
|
if (isInvalidFileName(newFileInfo.filename)) {
|
|
@@ -71,8 +77,7 @@ export async function createNewFluidFile(
|
|
|
71
77
|
|
|
72
78
|
let itemId: string;
|
|
73
79
|
let summaryHandle: string = "";
|
|
74
|
-
let
|
|
75
|
-
let sharingLinkErrorReason: string | undefined;
|
|
80
|
+
let shareLinkInfo: ShareLinkInfoType | undefined;
|
|
76
81
|
if (createNewSummary === undefined) {
|
|
77
82
|
itemId = await createNewEmptyFluidFile(
|
|
78
83
|
getStorageToken, newFileInfo, logger, epochTracker, forceAccessTokenViaAuthorizationHeader);
|
|
@@ -87,8 +92,12 @@ export async function createNewFluidFile(
|
|
|
87
92
|
);
|
|
88
93
|
itemId = content.itemId;
|
|
89
94
|
summaryHandle = content.id;
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
|
|
96
|
+
shareLinkInfo = extractShareLinkData(
|
|
97
|
+
newFileInfo.createLinkType,
|
|
98
|
+
content,
|
|
99
|
+
enableSingleRequestForShareLinkWithCreate,
|
|
100
|
+
enableShareLinkWithCreate);
|
|
92
101
|
}
|
|
93
102
|
|
|
94
103
|
const odspUrl = createOdspUrl({ ...newFileInfo, itemId, dataStorePath: "/" });
|
|
@@ -100,15 +109,7 @@ export async function createNewFluidFile(
|
|
|
100
109
|
fileEntry.docId = odspResolvedUrl.hashedDocumentId;
|
|
101
110
|
fileEntry.resolvedUrl = odspResolvedUrl;
|
|
102
111
|
|
|
103
|
-
|
|
104
|
-
odspResolvedUrl.shareLinkInfo = {
|
|
105
|
-
createLink: {
|
|
106
|
-
type: newFileInfo.createLinkType,
|
|
107
|
-
link: sharingLink,
|
|
108
|
-
error: sharingLinkErrorReason,
|
|
109
|
-
},
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
+
odspResolvedUrl.shareLinkInfo = shareLinkInfo;
|
|
112
113
|
|
|
113
114
|
if (createNewSummary !== undefined && createNewCaching) {
|
|
114
115
|
assert(summaryHandle !== undefined, 0x203 /* "Summary handle is undefined" */);
|
|
@@ -120,6 +121,61 @@ export async function createNewFluidFile(
|
|
|
120
121
|
return odspResolvedUrl;
|
|
121
122
|
}
|
|
122
123
|
|
|
124
|
+
/**
|
|
125
|
+
* If user requested creation of a sharing link along with the creation of the file by providing either
|
|
126
|
+
* createLinkType (now deprecated) or createLinkScope in the request parameters, extract and save
|
|
127
|
+
* sharing link information from the response if it is available.
|
|
128
|
+
* In case there was an error in creation of the sharing link, error is provided back in the response,
|
|
129
|
+
* and does not impact the creation of file in ODSP.
|
|
130
|
+
* @param requestedSharingLinkKind - Kind of sharing link requested to be created along with the creation of file.
|
|
131
|
+
* @param response - Response object received from the /snapshot api call
|
|
132
|
+
* @returns Sharing link information received in the response from a successful creation of a file.
|
|
133
|
+
*/
|
|
134
|
+
function extractShareLinkData(
|
|
135
|
+
requestedSharingLinkKind: ShareLinkTypes | ISharingLinkKind | undefined,
|
|
136
|
+
response: ICreateFileResponse,
|
|
137
|
+
enableSingleRequestForShareLinkWithCreate?: boolean,
|
|
138
|
+
enableShareLinkWithCreate?: boolean,
|
|
139
|
+
): ShareLinkInfoType | undefined {
|
|
140
|
+
if (!requestedSharingLinkKind) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
let shareLinkInfo: ShareLinkInfoType | undefined;
|
|
144
|
+
if (enableSingleRequestForShareLinkWithCreate) {
|
|
145
|
+
const { sharing } = response;
|
|
146
|
+
if (!sharing) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
shareLinkInfo = {
|
|
150
|
+
createLink: {
|
|
151
|
+
type: requestedSharingLinkKind,
|
|
152
|
+
link: sharing.sharingLink ? {
|
|
153
|
+
scope: sharing.sharingLink.scope,
|
|
154
|
+
role: sharing.sharingLink.type,
|
|
155
|
+
webUrl: sharing.sharingLink.webUrl,
|
|
156
|
+
...sharing.sharingLink,
|
|
157
|
+
} : undefined,
|
|
158
|
+
error: sharing.error,
|
|
159
|
+
shareId: sharing.shareId,
|
|
160
|
+
},
|
|
161
|
+
};
|
|
162
|
+
} else if (enableShareLinkWithCreate) {
|
|
163
|
+
const { sharing, sharingLink, sharingLinkErrorReason } = response;
|
|
164
|
+
if (!sharingLink && !sharingLinkErrorReason) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
shareLinkInfo = {
|
|
168
|
+
createLink: {
|
|
169
|
+
type: requestedSharingLinkKind,
|
|
170
|
+
link: sharingLink,
|
|
171
|
+
error: sharingLinkErrorReason,
|
|
172
|
+
shareId: sharing?.shareId,
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
return shareLinkInfo;
|
|
177
|
+
}
|
|
178
|
+
|
|
123
179
|
export async function createNewEmptyFluidFile(
|
|
124
180
|
getStorageToken: InstrumentedStorageTokenFetcher,
|
|
125
181
|
newFileInfo: INewFileInfo,
|
|
@@ -192,8 +248,12 @@ export async function createNewFluidFileFromSummary(
|
|
|
192
248
|
`${filePath}/${encodedFilename}`;
|
|
193
249
|
|
|
194
250
|
const containerSnapshot = convertSummaryIntoContainerSnapshot(createNewSummary);
|
|
195
|
-
|
|
196
|
-
|
|
251
|
+
|
|
252
|
+
// Build share link parameter based on the createLinkType provided so that the
|
|
253
|
+
// snapshot api can create and return the share link along with creation of file in the response.
|
|
254
|
+
const createShareLinkParam = buildOdspShareLinkReqParams(newFileInfo.createLinkType);
|
|
255
|
+
const initialUrl =
|
|
256
|
+
`${baseUrl}:/opStream/snapshots/snapshot${createShareLinkParam ? `?${createShareLinkParam}` : ""}`;
|
|
197
257
|
|
|
198
258
|
return getWithRetryForTokenRefresh(async (options) => {
|
|
199
259
|
const storageToken = await getStorageToken(options, "CreateNewFile");
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { IRequest } from "@fluidframework/core-interfaces";
|
|
6
6
|
import { DriverHeader } from "@fluidframework/driver-definitions";
|
|
7
|
-
import { ShareLinkTypes } from "@fluidframework/odsp-driver-definitions";
|
|
7
|
+
import { ShareLinkTypes, ISharingLinkKind } from "@fluidframework/odsp-driver-definitions";
|
|
8
|
+
import { buildOdspShareLinkReqParams } from "./odspUtils";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Create the request object with url and headers for creating a new file on OneDrive Sharepoint
|
|
@@ -12,19 +13,21 @@ import { ShareLinkTypes } from "@fluidframework/odsp-driver-definitions";
|
|
|
12
13
|
* @param driveId - drive identifier
|
|
13
14
|
* @param filePath - path where file needs to be created
|
|
14
15
|
* @param fileName - name of the new file to be created
|
|
15
|
-
* @param
|
|
16
|
+
* @param createShareLinkType - type of sharing link you would like to create for this file. ShareLinkTypes
|
|
17
|
+
* will be deprecated soon, so for any new implementation please provide createShareLinkType of type ShareLink
|
|
16
18
|
*/
|
|
17
19
|
export function createOdspCreateContainerRequest(
|
|
18
20
|
siteUrl: string,
|
|
19
21
|
driveId: string,
|
|
20
22
|
filePath: string,
|
|
21
23
|
fileName: string,
|
|
22
|
-
|
|
24
|
+
createShareLinkType?: ShareLinkTypes | ISharingLinkKind,
|
|
23
25
|
): IRequest {
|
|
26
|
+
const shareLinkRequestParams = buildOdspShareLinkReqParams(createShareLinkType);
|
|
24
27
|
const createNewRequest: IRequest = {
|
|
25
28
|
url: `${siteUrl}?driveId=${encodeURIComponent(
|
|
26
29
|
driveId,
|
|
27
|
-
)}&path=${encodeURIComponent(filePath)}${
|
|
30
|
+
)}&path=${encodeURIComponent(filePath)}${shareLinkRequestParams ? `&${shareLinkRequestParams}` : ""}`,
|
|
28
31
|
headers: {
|
|
29
32
|
[DriverHeader.createNew]: {
|
|
30
33
|
fileName,
|
package/src/fetchSnapshot.ts
CHANGED
|
@@ -252,9 +252,15 @@ async function fetchLatestSnapshotCore(
|
|
|
252
252
|
});
|
|
253
253
|
|
|
254
254
|
let parsedSnapshotContents: IOdspResponse<ISnapshotContents> | undefined;
|
|
255
|
+
let contentTypeToRead: string | undefined;
|
|
256
|
+
if (contentType?.indexOf("application/ms-fluid") !== -1) {
|
|
257
|
+
contentTypeToRead = "application/ms-fluid";
|
|
258
|
+
} else if (contentType?.indexOf("application/json") !== -1) {
|
|
259
|
+
contentTypeToRead = "application/json";
|
|
260
|
+
}
|
|
255
261
|
|
|
256
262
|
try {
|
|
257
|
-
switch (
|
|
263
|
+
switch (contentTypeToRead) {
|
|
258
264
|
case "application/json": {
|
|
259
265
|
const text = await odspResponse.content.text();
|
|
260
266
|
propsToLog.bodySize = text.length;
|
|
@@ -549,14 +555,12 @@ export async function downloadSnapshot(
|
|
|
549
555
|
};
|
|
550
556
|
// Decide what snapshot format to fetch as per the feature gate.
|
|
551
557
|
switch (snapshotFormatFetchType) {
|
|
552
|
-
case SnapshotFormatSupportType.JsonAndBinary:
|
|
553
|
-
headers.accept = `application/json, application/ms-fluid; v=${currentReadVersion}`;
|
|
554
|
-
break;
|
|
555
558
|
case SnapshotFormatSupportType.Binary:
|
|
556
559
|
headers.accept = `application/ms-fluid; v=${currentReadVersion}`;
|
|
557
560
|
break;
|
|
558
561
|
default:
|
|
559
|
-
|
|
562
|
+
// By default ask both versions and let the server decide the format.
|
|
563
|
+
headers.accept = `application/json, application/ms-fluid; v=${currentReadVersion}`;
|
|
560
564
|
}
|
|
561
565
|
|
|
562
566
|
const odspResponse = await (epochTracker?.fetch(url, fetchOptions, "treesLatest", true, scenarioName) ??
|
|
@@ -189,7 +189,7 @@ export class OdspDocumentService implements IDocumentService {
|
|
|
189
189
|
},
|
|
190
190
|
() => {
|
|
191
191
|
assert(this.relayServiceTenantAndSessionId !== undefined,
|
|
192
|
-
|
|
192
|
+
0x37b /* relayServiceTenantAndSessionId should be present */);
|
|
193
193
|
return this.relayServiceTenantAndSessionId;
|
|
194
194
|
},
|
|
195
195
|
this.mc.config.getNumber("Fluid.Driver.Odsp.snapshotFormatFetchType"),
|
|
@@ -26,6 +26,10 @@ import {
|
|
|
26
26
|
HostStoragePolicy,
|
|
27
27
|
IFileEntry,
|
|
28
28
|
IOdspUrlParts,
|
|
29
|
+
SharingLinkScope,
|
|
30
|
+
SharingLinkRole,
|
|
31
|
+
ShareLinkTypes,
|
|
32
|
+
ISharingLinkKind,
|
|
29
33
|
} from "@fluidframework/odsp-driver-definitions";
|
|
30
34
|
import type { io as SocketIOClientStatic } from "socket.io-client";
|
|
31
35
|
import { v4 as uuid } from "uuid";
|
|
@@ -84,15 +88,13 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
|
|
|
84
88
|
}
|
|
85
89
|
}
|
|
86
90
|
|
|
91
|
+
const createShareLinkParam = getSharingLinkParams(this.hostPolicy, searchParams);
|
|
87
92
|
const newFileInfo: INewFileInfo = {
|
|
88
93
|
driveId: odspResolvedUrl.driveId,
|
|
89
94
|
siteUrl: odspResolvedUrl.siteUrl,
|
|
90
95
|
filePath,
|
|
91
96
|
filename: odspResolvedUrl.fileName,
|
|
92
|
-
|
|
93
|
-
// so that share link creation with create file can be enabled
|
|
94
|
-
createLinkType: this.hostPolicy.enableShareLinkWithCreate ?
|
|
95
|
-
odspResolvedUrl.shareLinkInfo?.createLink?.type : undefined,
|
|
97
|
+
createLinkType: createShareLinkParam,
|
|
96
98
|
};
|
|
97
99
|
|
|
98
100
|
const odspLogger = createOdspLogger(logger);
|
|
@@ -110,6 +112,10 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
|
|
|
110
112
|
{
|
|
111
113
|
eventName: "CreateNew",
|
|
112
114
|
isWithSummaryUpload: true,
|
|
115
|
+
createShareLinkParam: JSON.stringify(createShareLinkParam),
|
|
116
|
+
enableShareLinkWithCreate: this.hostPolicy.enableShareLinkWithCreate,
|
|
117
|
+
enableSingleRequestForShareLinkWithCreate:
|
|
118
|
+
this.hostPolicy.enableSingleRequestForShareLinkWithCreate,
|
|
113
119
|
},
|
|
114
120
|
async (event) => {
|
|
115
121
|
odspResolvedUrl = await createNewFluidFile(
|
|
@@ -127,6 +133,8 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
|
|
|
127
133
|
this.hostPolicy.cacheCreateNewSummary ?? true,
|
|
128
134
|
!!this.hostPolicy.sessionOptions?.forceAccessTokenViaAuthorizationHeader,
|
|
129
135
|
odspResolvedUrl.isClpCompliantApp,
|
|
136
|
+
this.hostPolicy.enableSingleRequestForShareLinkWithCreate,
|
|
137
|
+
this.hostPolicy.enableShareLinkWithCreate,
|
|
130
138
|
);
|
|
131
139
|
const docService = this.createDocumentServiceCore(odspResolvedUrl, odspLogger,
|
|
132
140
|
cacheAndTracker, clientIsSummarizer);
|
|
@@ -223,3 +231,31 @@ export class OdspDocumentServiceFactoryCore implements IDocumentServiceFactory {
|
|
|
223
231
|
);
|
|
224
232
|
}
|
|
225
233
|
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Extract the sharing link kind from the resolved URL's query paramerters
|
|
237
|
+
*/
|
|
238
|
+
function getSharingLinkParams(
|
|
239
|
+
hostPolicy: HostStoragePolicy,
|
|
240
|
+
searchParams: URLSearchParams,
|
|
241
|
+
): ShareLinkTypes | ISharingLinkKind | undefined {
|
|
242
|
+
// extract request parameters for creation of sharing link (if provided) if the feature is enabled
|
|
243
|
+
let createShareLinkParam: ShareLinkTypes | ISharingLinkKind | undefined;
|
|
244
|
+
if (hostPolicy.enableSingleRequestForShareLinkWithCreate) {
|
|
245
|
+
const createLinkScope = searchParams.get("createLinkScope");
|
|
246
|
+
const createLinkRole = searchParams.get("createLinkRole");
|
|
247
|
+
if (createLinkScope && SharingLinkScope[createLinkScope]) {
|
|
248
|
+
createShareLinkParam = {
|
|
249
|
+
scope: SharingLinkScope[createLinkScope],
|
|
250
|
+
...(createLinkRole && SharingLinkRole[createLinkRole] ?
|
|
251
|
+
{ role: SharingLinkRole[createLinkRole] } : {}),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
} else if (hostPolicy.enableShareLinkWithCreate) {
|
|
255
|
+
const createLinkType = searchParams.get("createLinkType");
|
|
256
|
+
if (createLinkType && ShareLinkTypes[createLinkType]) {
|
|
257
|
+
createShareLinkParam = ShareLinkTypes[createLinkType || ""];
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return createShareLinkParam;
|
|
261
|
+
}
|
|
@@ -228,8 +228,7 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
228
228
|
}
|
|
229
229
|
|
|
230
230
|
// If count is one, we can use the trees/latest API, which returns the latest version and trees in a single request for better performance
|
|
231
|
-
|
|
232
|
-
if (this.firstVersionCall && count === 1 && (blobid === null || blobid === this.documentId)) {
|
|
231
|
+
if (count === 1 && (blobid === null || blobid === this.documentId)) {
|
|
233
232
|
const hostSnapshotOptions = this.hostPolicy.snapshotOptions;
|
|
234
233
|
const odspSnapshotCacheValue: ISnapshotContents = await PerformanceEvent.timedExecAsync(
|
|
235
234
|
this.logger,
|
|
@@ -275,7 +274,10 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
275
274
|
// Based on the concurrentSnapshotFetch policy:
|
|
276
275
|
// Either retrieve both the network and cache snapshots concurrently and pick the first to return,
|
|
277
276
|
// or grab the cache value and then the network value if the cache value returns undefined.
|
|
278
|
-
|
|
277
|
+
// For summarizer which could call this during refreshing of summary parent, always use the cache
|
|
278
|
+
// first. Also for other clients, if it is not critical path which is determined by firstVersionCall,
|
|
279
|
+
// then also check the cache first.
|
|
280
|
+
if (this.firstVersionCall && this.hostPolicy.concurrentSnapshotFetch && !this.hostPolicy.summarizerClient) {
|
|
279
281
|
const networkSnapshotP = this.fetchSnapshot(hostSnapshotOptions, scenarioName);
|
|
280
282
|
|
|
281
283
|
// Ensure that failures on both paths are ignored initially.
|
package/src/odspUtils.ts
CHANGED
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
isTokenFromCache,
|
|
28
28
|
OdspResourceTokenFetchOptions,
|
|
29
29
|
ShareLinkTypes,
|
|
30
|
+
ISharingLinkKind,
|
|
30
31
|
TokenFetcher,
|
|
31
32
|
ICacheEntry,
|
|
32
33
|
snapshotKey,
|
|
@@ -233,8 +234,10 @@ export interface INewFileInfo {
|
|
|
233
234
|
* application can request creation of a share link along with the creation of a new file
|
|
234
235
|
* by passing in an optional param to specify the kind of sharing link
|
|
235
236
|
* (at the time of adding this comment Sept/2021), odsp only supports csl
|
|
237
|
+
* ShareLinkTypes will deprecated in future. Use ISharingLinkKind instead which specifies both
|
|
238
|
+
* share link type and the role type.
|
|
236
239
|
*/
|
|
237
|
-
createLinkType?: ShareLinkTypes;
|
|
240
|
+
createLinkType?: ShareLinkTypes | ISharingLinkKind;
|
|
238
241
|
}
|
|
239
242
|
|
|
240
243
|
export function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl {
|
|
@@ -348,3 +351,23 @@ export function createCacheSnapshotKey(odspResolvedUrl: IOdspResolvedUrl): ICach
|
|
|
348
351
|
// 80KB is the max body size that we can put in ump post body for server to be able to accept it.
|
|
349
352
|
// Keeping it 78KB to be a little cautious. As per the telemetry 99p is less than 78KB.
|
|
350
353
|
export const maxUmpPostBodySize = 79872;
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Build request parameters to request for the creation of a sharing link along with the creation of the file
|
|
357
|
+
* through the /snapshot api call.
|
|
358
|
+
* @param shareLinkType - Kind of sharing link requested
|
|
359
|
+
* @returns A string of request parameters that can be concatenated with the base URI
|
|
360
|
+
*/
|
|
361
|
+
export function buildOdspShareLinkReqParams(shareLinkType: ShareLinkTypes | ISharingLinkKind | undefined) {
|
|
362
|
+
if (!shareLinkType) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
const scope = (shareLinkType as ISharingLinkKind).scope;
|
|
366
|
+
if (!scope) {
|
|
367
|
+
return `createLinkType=${shareLinkType}`;
|
|
368
|
+
}
|
|
369
|
+
let shareLinkRequestParams = `createLinkScope=${scope}`;
|
|
370
|
+
const role = (shareLinkType as ISharingLinkKind).role;
|
|
371
|
+
shareLinkRequestParams = role ? `${shareLinkRequestParams}&createLinkRole=${role}` : shareLinkRequestParams;
|
|
372
|
+
return shareLinkRequestParams;
|
|
373
|
+
}
|
package/src/packageVersion.ts
CHANGED
package/src/retryUtils.ts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { ITelemetryLogger } from "@fluidframework/common-definitions";
|
|
7
7
|
import { delay, performance } from "@fluidframework/common-utils";
|
|
8
|
-
import { canRetryOnError } from "@fluidframework/driver-utils";
|
|
8
|
+
import { canRetryOnError, getRetryDelayFromError } from "@fluidframework/driver-utils";
|
|
9
9
|
import { OdspErrorType } from "@fluidframework/odsp-driver-definitions";
|
|
10
10
|
import { Odsp409Error } from "./epochTracker";
|
|
11
11
|
|
|
@@ -43,8 +43,10 @@ export async function runWithRetry<T>(
|
|
|
43
43
|
|
|
44
44
|
const coherencyError = error?.[Odsp409Error] === true;
|
|
45
45
|
const serviceReadonlyError = error?.errorType === OdspErrorType.serviceReadOnly;
|
|
46
|
-
// Retry for retriable 409 coherency errors or serviceReadOnly errors.
|
|
47
|
-
|
|
46
|
+
// Retry for retriable 409 coherency errors or serviceReadOnly errors. These errors are always retriable
|
|
47
|
+
// unless someone specifically set canRetry = false on the error like in fetchSnapshot() flow. So in
|
|
48
|
+
// that case don't retry.
|
|
49
|
+
if (!((coherencyError || serviceReadonlyError) && canRetry)) {
|
|
48
50
|
throw error;
|
|
49
51
|
}
|
|
50
52
|
|
|
@@ -54,8 +56,8 @@ export async function runWithRetry<T>(
|
|
|
54
56
|
if (attempts === 5) {
|
|
55
57
|
logger.sendErrorEvent(
|
|
56
58
|
{
|
|
57
|
-
eventName: coherencyError ?
|
|
58
|
-
"
|
|
59
|
+
eventName: coherencyError ? "CoherencyErrorTooManyRetries" :
|
|
60
|
+
"ServiceReadonlyErrorTooManyRetries",
|
|
59
61
|
callName,
|
|
60
62
|
attempts,
|
|
61
63
|
duration: performance.now() - start, // record total wait time.
|
|
@@ -66,6 +68,7 @@ export async function runWithRetry<T>(
|
|
|
66
68
|
throw error;
|
|
67
69
|
}
|
|
68
70
|
|
|
71
|
+
retryAfter = getRetryDelayFromError(error) ?? retryAfter;
|
|
69
72
|
await delay(Math.floor(retryAfter));
|
|
70
73
|
retryAfter += retryAfter / 4 * (1 + Math.random());
|
|
71
74
|
lastError = error;
|