@fluidframework/odsp-driver 2.10.0 → 2.12.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/CHANGELOG.md +8 -0
- package/api-report/odsp-driver.legacy.alpha.api.md +5 -2
- package/dist/contracts.d.ts +6 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js.map +1 -1
- package/dist/createFile/createFile.d.ts +8 -4
- package/dist/createFile/createFile.d.ts.map +1 -1
- package/dist/createFile/createFile.js +51 -6
- package/dist/createFile/createFile.js.map +1 -1
- package/dist/createFile/createNewModule.d.ts +1 -1
- package/dist/createFile/createNewModule.d.ts.map +1 -1
- package/dist/createFile/createNewModule.js +2 -1
- package/dist/createFile/createNewModule.js.map +1 -1
- package/dist/createFile/createNewUtils.d.ts.map +1 -1
- package/dist/createFile/createNewUtils.js +3 -1
- package/dist/createFile/createNewUtils.js.map +1 -1
- package/dist/createOdspCreateContainerRequest.d.ts +4 -1
- package/dist/createOdspCreateContainerRequest.d.ts.map +1 -1
- package/dist/createOdspCreateContainerRequest.js +4 -2
- package/dist/createOdspCreateContainerRequest.js.map +1 -1
- package/dist/epochTracker.d.ts +1 -1
- package/dist/epochTracker.d.ts.map +1 -1
- package/dist/epochTracker.js.map +1 -1
- package/dist/fetchSnapshot.d.ts.map +1 -1
- package/dist/fetchSnapshot.js +5 -0
- package/dist/fetchSnapshot.js.map +1 -1
- package/dist/legacy.d.ts +1 -0
- package/dist/odspDocumentDeltaConnection.d.ts.map +1 -1
- package/dist/odspDocumentDeltaConnection.js +4 -0
- package/dist/odspDocumentDeltaConnection.js.map +1 -1
- package/dist/odspDocumentStorageManager.d.ts.map +1 -1
- package/dist/odspDocumentStorageManager.js +13 -0
- package/dist/odspDocumentStorageManager.js.map +1 -1
- package/dist/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
- package/dist/odspDriverUrlResolverForShareLink.js +7 -35
- package/dist/odspDriverUrlResolverForShareLink.js.map +1 -1
- package/dist/odspUrlHelper.d.ts +6 -0
- package/dist/odspUrlHelper.d.ts.map +1 -1
- package/dist/odspUrlHelper.js +18 -1
- package/dist/odspUrlHelper.js.map +1 -1
- package/dist/odspUtils.d.ts +22 -2
- package/dist/odspUtils.d.ts.map +1 -1
- package/dist/odspUtils.js +61 -2
- package/dist/odspUtils.js.map +1 -1
- package/dist/packageVersion.d.ts +1 -1
- package/dist/packageVersion.js +1 -1
- package/dist/packageVersion.js.map +1 -1
- package/lib/contracts.d.ts +6 -0
- package/lib/contracts.d.ts.map +1 -1
- package/lib/contracts.js.map +1 -1
- package/lib/createFile/createFile.d.ts +8 -4
- package/lib/createFile/createFile.d.ts.map +1 -1
- package/lib/createFile/createFile.js +51 -7
- package/lib/createFile/createFile.js.map +1 -1
- package/lib/createFile/createNewModule.d.ts +1 -1
- package/lib/createFile/createNewModule.d.ts.map +1 -1
- package/lib/createFile/createNewModule.js +1 -1
- package/lib/createFile/createNewModule.js.map +1 -1
- package/lib/createFile/createNewUtils.d.ts.map +1 -1
- package/lib/createFile/createNewUtils.js +3 -1
- package/lib/createFile/createNewUtils.js.map +1 -1
- package/lib/createOdspCreateContainerRequest.d.ts +4 -1
- package/lib/createOdspCreateContainerRequest.d.ts.map +1 -1
- package/lib/createOdspCreateContainerRequest.js +6 -4
- package/lib/createOdspCreateContainerRequest.js.map +1 -1
- package/lib/epochTracker.d.ts +1 -1
- package/lib/epochTracker.d.ts.map +1 -1
- package/lib/epochTracker.js.map +1 -1
- package/lib/fetchSnapshot.d.ts.map +1 -1
- package/lib/fetchSnapshot.js +5 -0
- package/lib/fetchSnapshot.js.map +1 -1
- package/lib/legacy.d.ts +1 -0
- package/lib/odspDocumentDeltaConnection.d.ts.map +1 -1
- package/lib/odspDocumentDeltaConnection.js +4 -0
- package/lib/odspDocumentDeltaConnection.js.map +1 -1
- package/lib/odspDocumentStorageManager.d.ts.map +1 -1
- package/lib/odspDocumentStorageManager.js +13 -0
- package/lib/odspDocumentStorageManager.js.map +1 -1
- package/lib/odspDriverUrlResolverForShareLink.d.ts.map +1 -1
- package/lib/odspDriverUrlResolverForShareLink.js +8 -36
- package/lib/odspDriverUrlResolverForShareLink.js.map +1 -1
- package/lib/odspUrlHelper.d.ts +6 -0
- package/lib/odspUrlHelper.d.ts.map +1 -1
- package/lib/odspUrlHelper.js +16 -0
- package/lib/odspUrlHelper.js.map +1 -1
- package/lib/odspUtils.d.ts +22 -2
- package/lib/odspUtils.d.ts.map +1 -1
- package/lib/odspUtils.js +58 -1
- package/lib/odspUtils.js.map +1 -1
- package/lib/packageVersion.d.ts +1 -1
- package/lib/packageVersion.js +1 -1
- package/lib/packageVersion.js.map +1 -1
- package/package.json +14 -14
- package/src/contracts.ts +7 -0
- package/src/createFile/createFile.ts +100 -7
- package/src/createFile/createNewModule.ts +1 -1
- package/src/createFile/createNewUtils.ts +3 -1
- package/src/createOdspCreateContainerRequest.ts +9 -3
- package/src/epochTracker.ts +2 -1
- package/src/fetchSnapshot.ts +5 -0
- package/src/odspDocumentDeltaConnection.ts +4 -0
- package/src/odspDocumentStorageManager.ts +28 -0
- package/src/odspDriverUrlResolverForShareLink.ts +21 -38
- package/src/odspUrlHelper.ts +18 -0
- package/src/odspUtils.ts +75 -2
- package/src/packageVersion.ts +1 -1
package/src/contracts.ts
CHANGED
|
@@ -189,6 +189,13 @@ export interface ICreateFileResponse {
|
|
|
189
189
|
"sharing"?: any;
|
|
190
190
|
"sharingLink"?: string;
|
|
191
191
|
"sharingLinkErrorReason"?: string;
|
|
192
|
+
"name": string;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export interface IRenameFileResponse {
|
|
196
|
+
"@odata.context": string;
|
|
197
|
+
"id": string;
|
|
198
|
+
"name": string;
|
|
192
199
|
}
|
|
193
200
|
|
|
194
201
|
export interface IVersionedValueWithEpoch {
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
InstrumentedStorageTokenFetcher,
|
|
14
14
|
OdspErrorTypes,
|
|
15
15
|
ShareLinkInfoType,
|
|
16
|
+
type IOdspUrlParts,
|
|
16
17
|
} from "@fluidframework/odsp-driver-definitions/internal";
|
|
17
18
|
import {
|
|
18
19
|
ITelemetryLoggerExt,
|
|
@@ -20,15 +21,16 @@ import {
|
|
|
20
21
|
PerformanceEvent,
|
|
21
22
|
} from "@fluidframework/telemetry-utils/internal";
|
|
22
23
|
|
|
23
|
-
import { ICreateFileResponse } from "./../contracts.js";
|
|
24
|
+
import { ICreateFileResponse, type IRenameFileResponse } from "./../contracts.js";
|
|
24
25
|
import { ClpCompliantAppHeader } from "./../contractsPublic.js";
|
|
25
26
|
import { createOdspUrl } from "./../createOdspUrl.js";
|
|
26
27
|
import { EpochTracker } from "./../epochTracker.js";
|
|
27
28
|
import { getHeadersWithAuth } from "./../getUrlAndHeadersWithAuth.js";
|
|
28
29
|
import { OdspDriverUrlResolver } from "./../odspDriverUrlResolver.js";
|
|
29
|
-
import { getApiRoot } from "./../odspUrlHelper.js";
|
|
30
|
+
import { checkForKnownServerFarmType, getApiRoot } from "./../odspUrlHelper.js";
|
|
30
31
|
import {
|
|
31
32
|
INewFileInfo,
|
|
33
|
+
appendNavParam,
|
|
32
34
|
buildOdspShareLinkReqParams,
|
|
33
35
|
createCacheSnapshotKey,
|
|
34
36
|
getWithRetryForTokenRefresh,
|
|
@@ -62,6 +64,7 @@ export async function createNewFluidFile(
|
|
|
62
64
|
forceAccessTokenViaAuthorizationHeader: boolean,
|
|
63
65
|
isClpCompliantApp?: boolean,
|
|
64
66
|
enableSingleRequestForShareLinkWithCreate?: boolean,
|
|
67
|
+
resolvedUrl?: IOdspResolvedUrl,
|
|
65
68
|
): Promise<IOdspResolvedUrl> {
|
|
66
69
|
// Check for valid filename before the request to create file is actually made.
|
|
67
70
|
if (isInvalidFileName(newFileInfo.filename)) {
|
|
@@ -74,10 +77,18 @@ export async function createNewFluidFile(
|
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
let itemId: string;
|
|
80
|
+
let pendingRename: string | undefined;
|
|
77
81
|
let summaryHandle: string = "";
|
|
78
82
|
let shareLinkInfo: ShareLinkInfoType | undefined;
|
|
79
83
|
if (createNewSummary === undefined) {
|
|
80
|
-
|
|
84
|
+
const content = await createNewEmptyFluidFile(
|
|
85
|
+
getAuthHeader,
|
|
86
|
+
newFileInfo,
|
|
87
|
+
logger,
|
|
88
|
+
epochTracker,
|
|
89
|
+
);
|
|
90
|
+
itemId = content.itemId;
|
|
91
|
+
pendingRename = newFileInfo.filename;
|
|
81
92
|
} else {
|
|
82
93
|
const content = await createNewFluidFileFromSummary(
|
|
83
94
|
getAuthHeader,
|
|
@@ -102,7 +113,23 @@ export async function createNewFluidFile(
|
|
|
102
113
|
fileEntry.docId = odspResolvedUrl.hashedDocumentId;
|
|
103
114
|
fileEntry.resolvedUrl = odspResolvedUrl;
|
|
104
115
|
|
|
116
|
+
odspResolvedUrl.context = resolvedUrl?.context;
|
|
117
|
+
odspResolvedUrl.appName = resolvedUrl?.appName;
|
|
118
|
+
odspResolvedUrl.codeHint = resolvedUrl?.codeHint;
|
|
119
|
+
|
|
120
|
+
if (shareLinkInfo?.createLink?.link) {
|
|
121
|
+
let newWebUrl = shareLinkInfo.createLink.link.webUrl;
|
|
122
|
+
newWebUrl = appendNavParam(
|
|
123
|
+
newWebUrl,
|
|
124
|
+
odspResolvedUrl,
|
|
125
|
+
odspResolvedUrl.dataStorePath ?? "/",
|
|
126
|
+
odspResolvedUrl.codeHint?.containerPackageName,
|
|
127
|
+
);
|
|
128
|
+
shareLinkInfo.createLink.link.webUrl = newWebUrl;
|
|
129
|
+
}
|
|
130
|
+
|
|
105
131
|
odspResolvedUrl.shareLinkInfo = shareLinkInfo;
|
|
132
|
+
odspResolvedUrl.pendingRename = pendingRename;
|
|
106
133
|
|
|
107
134
|
if (createNewSummary !== undefined && createNewCaching) {
|
|
108
135
|
assert(summaryHandle !== undefined, 0x203 /* "Summary handle is undefined" */);
|
|
@@ -178,9 +205,8 @@ export async function createNewEmptyFluidFile(
|
|
|
178
205
|
newFileInfo: INewFileInfo,
|
|
179
206
|
logger: ITelemetryLoggerExt,
|
|
180
207
|
epochTracker: EpochTracker,
|
|
181
|
-
): Promise<string> {
|
|
208
|
+
): Promise<{ itemId: string; fileName: string }> {
|
|
182
209
|
const filePath = encodeFilePath(newFileInfo.filePath);
|
|
183
|
-
// add .tmp extension to empty file (host is expected to rename)
|
|
184
210
|
const encodedFilename = encodeURIComponent(`${newFileInfo.filename}.tmp`);
|
|
185
211
|
const initialUrl = `${getApiRoot(new URL(newFileInfo.siteUrl))}/drives/${
|
|
186
212
|
newFileInfo.driveId
|
|
@@ -193,10 +219,16 @@ export async function createNewEmptyFluidFile(
|
|
|
193
219
|
{ ...options, request: { url, method } },
|
|
194
220
|
"CreateNewFile",
|
|
195
221
|
);
|
|
222
|
+
const internalFarmType = checkForKnownServerFarmType(newFileInfo.siteUrl);
|
|
196
223
|
|
|
197
224
|
return PerformanceEvent.timedExecAsync(
|
|
198
225
|
logger,
|
|
199
|
-
{
|
|
226
|
+
{
|
|
227
|
+
eventName: "createNewEmptyFile",
|
|
228
|
+
details: {
|
|
229
|
+
internalFarmType,
|
|
230
|
+
},
|
|
231
|
+
},
|
|
200
232
|
async (event) => {
|
|
201
233
|
const headers = getHeadersWithAuth(authHeader);
|
|
202
234
|
headers["Content-Type"] = "application/json";
|
|
@@ -228,7 +260,68 @@ export async function createNewEmptyFluidFile(
|
|
|
228
260
|
event.end({
|
|
229
261
|
...fetchResponse.propsToLog,
|
|
230
262
|
});
|
|
231
|
-
return content.id;
|
|
263
|
+
return { itemId: content.id, fileName: content.name };
|
|
264
|
+
},
|
|
265
|
+
{ end: true, cancel: "error" },
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export async function renameEmptyFluidFile(
|
|
271
|
+
getAuthHeader: InstrumentedStorageTokenFetcher,
|
|
272
|
+
odspParts: IOdspUrlParts,
|
|
273
|
+
requestedFileName: string,
|
|
274
|
+
logger: ITelemetryLoggerExt,
|
|
275
|
+
epochTracker: EpochTracker,
|
|
276
|
+
): Promise<IRenameFileResponse> {
|
|
277
|
+
const initialUrl = `${getApiRoot(new URL(odspParts.siteUrl))}/drives/${
|
|
278
|
+
odspParts.driveId
|
|
279
|
+
}/items/${odspParts.itemId}?@name.conflictBehavior=rename`;
|
|
280
|
+
|
|
281
|
+
return getWithRetryForTokenRefresh(async (options) => {
|
|
282
|
+
const url = initialUrl;
|
|
283
|
+
const method = "PATCH";
|
|
284
|
+
const authHeader = await getAuthHeader(
|
|
285
|
+
{ ...options, request: { url, method } },
|
|
286
|
+
"renameFile",
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
return PerformanceEvent.timedExecAsync(
|
|
290
|
+
logger,
|
|
291
|
+
{ eventName: "renameFile" },
|
|
292
|
+
async (event) => {
|
|
293
|
+
const headers = getHeadersWithAuth(authHeader);
|
|
294
|
+
headers["Content-Type"] = "application/json";
|
|
295
|
+
|
|
296
|
+
const fetchResponse = await runWithRetry(
|
|
297
|
+
async () =>
|
|
298
|
+
epochTracker.fetchAndParseAsJSON<IRenameFileResponse>(
|
|
299
|
+
url,
|
|
300
|
+
{
|
|
301
|
+
body: JSON.stringify({
|
|
302
|
+
name: requestedFileName,
|
|
303
|
+
}),
|
|
304
|
+
headers,
|
|
305
|
+
method: "PATCH",
|
|
306
|
+
},
|
|
307
|
+
"renameFile",
|
|
308
|
+
),
|
|
309
|
+
"renameFile",
|
|
310
|
+
logger,
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
const content = fetchResponse.content;
|
|
314
|
+
if (!content?.id) {
|
|
315
|
+
throw new NonRetryableError(
|
|
316
|
+
"ODSP RenameFile call returned no item ID (for empty file)",
|
|
317
|
+
OdspErrorTypes.incorrectServerResponse,
|
|
318
|
+
{ driverVersion },
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
event.end({
|
|
322
|
+
...fetchResponse.propsToLog,
|
|
323
|
+
});
|
|
324
|
+
return content;
|
|
232
325
|
},
|
|
233
326
|
{ end: true, cancel: "error" },
|
|
234
327
|
);
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export { createNewFluidFile } from "./createFile.js";
|
|
6
|
+
export { createNewFluidFile, renameEmptyFluidFile } from "./createFile.js";
|
|
7
7
|
export { createNewContainerOnExistingFile } from "./createNewContainerOnExistingFile.js";
|
|
8
8
|
export { convertCreateNewSummaryTreeToTreeAndBlobs } from "./createNewUtils.js";
|
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
} from "./../contracts.js";
|
|
33
33
|
import { EpochTracker, FetchType } from "./../epochTracker.js";
|
|
34
34
|
import { getHeadersWithAuth } from "./../getUrlAndHeadersWithAuth.js";
|
|
35
|
+
import { checkForKnownServerFarmType } from "./../odspUrlHelper.js";
|
|
35
36
|
import { getWithRetryForTokenRefresh, maxUmpPostBodySize } from "./../odspUtils.js";
|
|
36
37
|
import { runWithRetry } from "./../retryUtils.js";
|
|
37
38
|
|
|
@@ -222,11 +223,12 @@ export async function createNewFluidContainerCore<T>(args: {
|
|
|
222
223
|
fetchType,
|
|
223
224
|
validateResponseCallback,
|
|
224
225
|
} = args;
|
|
226
|
+
const internalFarmType = checkForKnownServerFarmType(initialUrl);
|
|
225
227
|
|
|
226
228
|
return getWithRetryForTokenRefresh(async (options) => {
|
|
227
229
|
return PerformanceEvent.timedExecAsync(
|
|
228
230
|
logger,
|
|
229
|
-
{ eventName: telemetryName },
|
|
231
|
+
{ eventName: telemetryName, details: { internalFarmType } },
|
|
230
232
|
async (event) => {
|
|
231
233
|
const snapshotBody = JSON.stringify(containerSnapshot);
|
|
232
234
|
let url: string;
|
|
@@ -4,10 +4,13 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { IRequest } from "@fluidframework/core-interfaces";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
DriverHeader,
|
|
9
|
+
type IContainerPackageInfo,
|
|
10
|
+
} from "@fluidframework/driver-definitions/internal";
|
|
8
11
|
import { ISharingLinkKind } from "@fluidframework/odsp-driver-definitions/internal";
|
|
9
12
|
|
|
10
|
-
import { buildOdspShareLinkReqParams } from "./odspUtils.js";
|
|
13
|
+
import { buildOdspShareLinkReqParams, getContainerPackageName } from "./odspUtils.js";
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* Create the request object with url and headers for creating a new file on OneDrive Sharepoint
|
|
@@ -17,6 +20,8 @@ import { buildOdspShareLinkReqParams } from "./odspUtils.js";
|
|
|
17
20
|
* @param fileName - name of the new file to be created
|
|
18
21
|
* @param createShareLinkType - type of sharing link you would like to create for this file. ShareLinkTypes
|
|
19
22
|
* will be deprecated soon, so for any new implementation please provide createShareLinkType of type ShareLink
|
|
23
|
+
* @param containerPackageInfo - container package information which will be used to extract the container package name.
|
|
24
|
+
* If not given that means that the container package does not have a name.
|
|
20
25
|
* @legacy
|
|
21
26
|
* @alpha
|
|
22
27
|
*/
|
|
@@ -26,12 +31,13 @@ export function createOdspCreateContainerRequest(
|
|
|
26
31
|
filePath: string,
|
|
27
32
|
fileName: string,
|
|
28
33
|
createShareLinkType?: ISharingLinkKind,
|
|
34
|
+
containerPackageInfo?: IContainerPackageInfo | undefined,
|
|
29
35
|
): IRequest {
|
|
30
36
|
const shareLinkRequestParams = buildOdspShareLinkReqParams(createShareLinkType);
|
|
31
37
|
const createNewRequest: IRequest = {
|
|
32
38
|
url: `${siteUrl}?driveId=${encodeURIComponent(driveId)}&path=${encodeURIComponent(
|
|
33
39
|
filePath,
|
|
34
|
-
)}${shareLinkRequestParams ? `&${shareLinkRequestParams}` : ""}`,
|
|
40
|
+
)}${containerPackageInfo ? `&containerPackageName=${getContainerPackageName(containerPackageInfo)}` : ""}${shareLinkRequestParams ? `&${shareLinkRequestParams}` : ""}`,
|
|
35
41
|
headers: {
|
|
36
42
|
[DriverHeader.createNew]: {
|
|
37
43
|
fileName,
|
package/src/epochTracker.ts
CHANGED
package/src/fetchSnapshot.ts
CHANGED
|
@@ -47,6 +47,7 @@ import { EpochTracker } from "./epochTracker.js";
|
|
|
47
47
|
import { getQueryString } from "./getQueryString.js";
|
|
48
48
|
import { getHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
|
|
49
49
|
import { convertOdspSnapshotToSnapshotTreeAndBlobs } from "./odspSnapshotParser.js";
|
|
50
|
+
import { checkForKnownServerFarmType } from "./odspUrlHelper.js";
|
|
50
51
|
import {
|
|
51
52
|
IOdspResponse,
|
|
52
53
|
fetchAndParseAsJSONHelper,
|
|
@@ -297,6 +298,7 @@ async function fetchLatestSnapshotCore(
|
|
|
297
298
|
return getWithRetryForTokenRefresh(async (tokenFetchOptions) => {
|
|
298
299
|
const fetchSnapshotForLoadingGroup = isSnapshotFetchForLoadingGroup(loadingGroupIds);
|
|
299
300
|
const eventName = fetchSnapshotForLoadingGroup ? "TreesLatestForGroup" : "TreesLatest";
|
|
301
|
+
const internalFarmType = checkForKnownServerFarmType(odspResolvedUrl.siteUrl);
|
|
300
302
|
|
|
301
303
|
const perfEvent = {
|
|
302
304
|
eventName,
|
|
@@ -304,6 +306,9 @@ async function fetchLatestSnapshotCore(
|
|
|
304
306
|
shareLinkPresent: odspResolvedUrl.shareLinkInfo?.sharingLinkToRedeem !== undefined,
|
|
305
307
|
isSummarizer: odspResolvedUrl.summarizer,
|
|
306
308
|
redeemFallbackEnabled: enableRedeemFallback,
|
|
309
|
+
details: {
|
|
310
|
+
internalFarmType,
|
|
311
|
+
},
|
|
307
312
|
};
|
|
308
313
|
if (snapshotOptions !== undefined) {
|
|
309
314
|
for (const [key, value] of Object.entries(snapshotOptions)) {
|
|
@@ -699,6 +699,10 @@ export class OdspDocumentDeltaConnection extends DocumentDeltaConnection {
|
|
|
699
699
|
);
|
|
700
700
|
|
|
701
701
|
if (filteredMsgs.length > 0) {
|
|
702
|
+
// This ternary is needed for signal-based layer compat tests to pass,
|
|
703
|
+
// specifically the layer version combination where you have an old loader and the most recent driver layer.
|
|
704
|
+
// Old loader doesn't send or receive batched signals (ISignalMessage[]),
|
|
705
|
+
// so only individual ISignalMessage's should be passed when there's one element for backcompat.
|
|
702
706
|
listener(filteredMsgs.length === 1 ? filteredMsgs[0] : filteredMsgs, documentId);
|
|
703
707
|
}
|
|
704
708
|
},
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
ISnapshotCachedEntry2,
|
|
44
44
|
IVersionedValueWithEpoch,
|
|
45
45
|
} from "./contracts.js";
|
|
46
|
+
import { useCreateNewModule } from "./createFile/index.js";
|
|
46
47
|
import { EpochTracker } from "./epochTracker.js";
|
|
47
48
|
import {
|
|
48
49
|
ISnapshotRequestAndResponseOptions,
|
|
@@ -769,6 +770,33 @@ export class OdspDocumentStorageService extends OdspDocumentStorageServiceBase {
|
|
|
769
770
|
0x56e /* summary upload manager should have been initialized */,
|
|
770
771
|
);
|
|
771
772
|
const id = await this.odspSummaryUploadManager.writeSummaryTree(summary, context);
|
|
773
|
+
const { pendingRename } = this.odspResolvedUrl;
|
|
774
|
+
if (
|
|
775
|
+
pendingRename !== undefined &&
|
|
776
|
+
this.config.getBoolean("Fluid.Driver.Odsp.disablePendingRename") !== true
|
|
777
|
+
) {
|
|
778
|
+
// This is a temporary file, so we need to rename it to remove the .tmp extension
|
|
779
|
+
// This should only happen for the initial summary upload for a new file
|
|
780
|
+
assert(
|
|
781
|
+
context.ackHandle === undefined &&
|
|
782
|
+
context.proposalHandle === undefined &&
|
|
783
|
+
context.referenceSequenceNumber === 0,
|
|
784
|
+
0xa88 /* temporaryFileName should only be set for new file creation in the empty file create flow */,
|
|
785
|
+
);
|
|
786
|
+
|
|
787
|
+
const renameResponse = await useCreateNewModule(this.logger, async (m) =>
|
|
788
|
+
m.renameEmptyFluidFile(
|
|
789
|
+
this.getAuthHeader,
|
|
790
|
+
this.odspResolvedUrl,
|
|
791
|
+
pendingRename,
|
|
792
|
+
this.logger,
|
|
793
|
+
this.epochTracker,
|
|
794
|
+
),
|
|
795
|
+
);
|
|
796
|
+
this.odspResolvedUrl.pendingRename = undefined;
|
|
797
|
+
this.odspResolvedUrl.fileName = renameResponse.name;
|
|
798
|
+
}
|
|
799
|
+
|
|
772
800
|
return id;
|
|
773
801
|
}
|
|
774
802
|
|
|
@@ -27,7 +27,12 @@ import {
|
|
|
27
27
|
locatorQueryParamName,
|
|
28
28
|
storeLocatorInOdspUrl,
|
|
29
29
|
} from "./odspFluidFileLink.js";
|
|
30
|
-
import {
|
|
30
|
+
import {
|
|
31
|
+
appendNavParam,
|
|
32
|
+
createOdspLogger,
|
|
33
|
+
getOdspResolvedUrl,
|
|
34
|
+
getContainerPackageName,
|
|
35
|
+
} from "./odspUtils.js";
|
|
31
36
|
|
|
32
37
|
/**
|
|
33
38
|
* Properties passed to the code responsible for fetching share link for a file.
|
|
@@ -45,10 +50,6 @@ export interface ShareLinkFetcherProps {
|
|
|
45
50
|
identityType: IdentityType;
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
// back-compat: GitHub #9653
|
|
49
|
-
const isFluidPackage = (pkg: Record<string, unknown>): boolean =>
|
|
50
|
-
typeof pkg === "object" && typeof pkg?.name === "string" && typeof pkg?.fluid === "object";
|
|
51
|
-
|
|
52
53
|
/**
|
|
53
54
|
* Resolver to resolve urls like the ones created by createOdspUrl which is driver inner
|
|
54
55
|
* url format and the ones which have things like driveId, siteId, itemId etc encoded in nav param.
|
|
@@ -147,6 +148,13 @@ export class OdspDriverUrlResolverForShareLink implements IUrlResolver {
|
|
|
147
148
|
|
|
148
149
|
const odspResolvedUrl = await new OdspDriverUrlResolver().resolve(requestToBeResolved);
|
|
149
150
|
|
|
151
|
+
odspResolvedUrl.context = await this.getContext?.(
|
|
152
|
+
odspResolvedUrl,
|
|
153
|
+
odspResolvedUrl.dataStorePath ?? "",
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
odspResolvedUrl.appName = this.appName;
|
|
157
|
+
|
|
150
158
|
if (isSharingLinkToRedeem) {
|
|
151
159
|
// We need to remove the nav param if set by host when setting the sharelink as otherwise the shareLinkId
|
|
152
160
|
// when redeeming the share link during the redeem fallback for trees latest call becomes greater than
|
|
@@ -240,45 +248,20 @@ export class OdspDriverUrlResolverForShareLink implements IUrlResolver {
|
|
|
240
248
|
dataStorePath: string,
|
|
241
249
|
packageInfoSource?: IContainerPackageInfo,
|
|
242
250
|
): Promise<string> {
|
|
243
|
-
const url = new URL(baseUrl);
|
|
244
251
|
const odspResolvedUrl = getOdspResolvedUrl(resolvedUrl);
|
|
245
252
|
|
|
246
253
|
// If the user has passed an empty dataStorePath, then extract it from the resolved url.
|
|
247
254
|
const actualDataStorePath = dataStorePath || (odspResolvedUrl.dataStorePath ?? "");
|
|
248
255
|
|
|
249
|
-
|
|
250
|
-
if (packageInfoSource && "name" in packageInfoSource) {
|
|
251
|
-
containerPackageName = packageInfoSource.name;
|
|
252
|
-
// packageInfoSource is cast to any as it is typed to IContainerPackageInfo instead of IFluidCodeDetails
|
|
253
|
-
// TODO: use a stronger type
|
|
254
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
255
|
-
} else if (isFluidPackage((packageInfoSource as any)?.package)) {
|
|
256
|
-
// TODO: use a stronger type
|
|
257
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
258
|
-
containerPackageName = (packageInfoSource as any)?.package.name;
|
|
259
|
-
} else {
|
|
260
|
-
// TODO: use a stronger type
|
|
261
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
262
|
-
containerPackageName = (packageInfoSource as any)?.package;
|
|
263
|
-
}
|
|
264
|
-
// TODO: use a stronger type
|
|
265
|
-
containerPackageName =
|
|
266
|
-
containerPackageName ?? odspResolvedUrl.codeHint?.containerPackageName;
|
|
267
|
-
|
|
268
|
-
const context = await this.getContext?.(odspResolvedUrl, actualDataStorePath);
|
|
269
|
-
|
|
270
|
-
storeLocatorInOdspUrl(url, {
|
|
271
|
-
siteUrl: odspResolvedUrl.siteUrl,
|
|
272
|
-
driveId: odspResolvedUrl.driveId,
|
|
273
|
-
itemId: odspResolvedUrl.itemId,
|
|
274
|
-
dataStorePath: actualDataStorePath,
|
|
275
|
-
appName: this.appName,
|
|
276
|
-
containerPackageName,
|
|
277
|
-
fileVersion: odspResolvedUrl.fileVersion,
|
|
278
|
-
context,
|
|
279
|
-
});
|
|
256
|
+
odspResolvedUrl.context = await this.getContext?.(odspResolvedUrl, actualDataStorePath);
|
|
280
257
|
|
|
281
|
-
|
|
258
|
+
const containerPackageName: string | undefined =
|
|
259
|
+
getContainerPackageName(packageInfoSource) ??
|
|
260
|
+
odspResolvedUrl.codeHint?.containerPackageName;
|
|
261
|
+
|
|
262
|
+
odspResolvedUrl.appName = this.appName;
|
|
263
|
+
|
|
264
|
+
return appendNavParam(baseUrl, odspResolvedUrl, actualDataStorePath, containerPackageName);
|
|
282
265
|
}
|
|
283
266
|
|
|
284
267
|
/**
|
package/src/odspUrlHelper.ts
CHANGED
|
@@ -126,3 +126,21 @@ export async function getOdspUrlParts(url: URL): Promise<IOdspUrlParts | undefin
|
|
|
126
126
|
return { siteUrl: `${url.origin}${url.pathname}`, driveId, itemId };
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Inspect the ODSP siteUrl to guess if this document is in SPDF or MSIT, to aid livesite investigations
|
|
132
|
+
* @param urlOnSite - The URL of the site or a resource on the site
|
|
133
|
+
* @returns undefined if the URL doesn't match known SPDF/MSIT patterns, "SPDF" if it's SPDF, "MSIT" if it's MSIT
|
|
134
|
+
*/
|
|
135
|
+
export function checkForKnownServerFarmType(urlOnSite: string): "SPDF" | "MSIT" | undefined {
|
|
136
|
+
const domain = new URL(urlOnSite).hostname.toLowerCase();
|
|
137
|
+
if (domain.endsWith(".sharepoint-df.com")) {
|
|
138
|
+
return "SPDF";
|
|
139
|
+
} else if (
|
|
140
|
+
domain === "microsoft.sharepoint.com" ||
|
|
141
|
+
domain === "microsoft-my.sharepoint.com"
|
|
142
|
+
) {
|
|
143
|
+
return "MSIT";
|
|
144
|
+
}
|
|
145
|
+
return undefined;
|
|
146
|
+
}
|
package/src/odspUtils.ts
CHANGED
|
@@ -9,7 +9,11 @@ import {
|
|
|
9
9
|
ITelemetryBaseProperties,
|
|
10
10
|
} from "@fluidframework/core-interfaces";
|
|
11
11
|
import { assert } from "@fluidframework/core-utils/internal";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
IResolvedUrl,
|
|
14
|
+
ISnapshot,
|
|
15
|
+
IContainerPackageInfo,
|
|
16
|
+
} from "@fluidframework/driver-definitions/internal";
|
|
13
17
|
import {
|
|
14
18
|
type AuthorizationError,
|
|
15
19
|
NetworkErrorBasic,
|
|
@@ -51,6 +55,7 @@ import {
|
|
|
51
55
|
} from "@fluidframework/telemetry-utils/internal";
|
|
52
56
|
|
|
53
57
|
import { fetch } from "./fetch.js";
|
|
58
|
+
import { storeLocatorInOdspUrl } from "./odspFluidFileLink.js";
|
|
54
59
|
// eslint-disable-next-line import/no-deprecated
|
|
55
60
|
import { ISnapshotContents } from "./odspPublicUtils.js";
|
|
56
61
|
import { pkgVersion as driverVersion } from "./packageVersion.js";
|
|
@@ -335,7 +340,8 @@ export function getOdspResolvedUrl(resolvedUrl: IResolvedUrl): IOdspResolvedUrl
|
|
|
335
340
|
/**
|
|
336
341
|
* Type narrowing utility to determine if the provided {@link @fluidframework/driver-definitions#IResolvedUrl}
|
|
337
342
|
* is an {@link @fluidframework/odsp-driver-definitions#IOdspResolvedUrl}.
|
|
338
|
-
* @
|
|
343
|
+
* @legacy
|
|
344
|
+
* @alpha
|
|
339
345
|
*/
|
|
340
346
|
export function isOdspResolvedUrl(resolvedUrl: IResolvedUrl): resolvedUrl is IOdspResolvedUrl {
|
|
341
347
|
return "odspResolvedUrl" in resolvedUrl && resolvedUrl.odspResolvedUrl === true;
|
|
@@ -550,3 +556,70 @@ export function useLegacyFlowWithoutGroupsForSnapshotFetch(
|
|
|
550
556
|
): boolean {
|
|
551
557
|
return loadingGroupIds === undefined;
|
|
552
558
|
}
|
|
559
|
+
|
|
560
|
+
// back-compat: GitHub #9653
|
|
561
|
+
const isFluidPackage = (pkg: Record<string, unknown>): boolean =>
|
|
562
|
+
typeof pkg === "object" && typeof pkg?.name === "string" && typeof pkg?.fluid === "object";
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Appends the store locator properties to the provided base URL. This function is useful for scenarios where an application
|
|
566
|
+
* has a base URL (for example a sharing link) of the Fluid file, but does not have the locator information that would be used by Fluid
|
|
567
|
+
* to load the file later.
|
|
568
|
+
* @param baseUrl - The input URL on which the locator params will be appended.
|
|
569
|
+
* @param resolvedUrl - odsp-driver's resolvedURL object.
|
|
570
|
+
* @param dataStorePath - The relative data store path URL.
|
|
571
|
+
* For requesting a driver URL, this value should always be '/'. If an empty string is passed, then dataStorePath
|
|
572
|
+
* will be extracted from the resolved url if present.
|
|
573
|
+
* @param containerPackageName - Name of the package to be included in the URL.
|
|
574
|
+
* @returns The provided base URL appended with odsp-specific locator information
|
|
575
|
+
*/
|
|
576
|
+
export function appendNavParam(
|
|
577
|
+
baseUrl: string,
|
|
578
|
+
odspResolvedUrl: IOdspResolvedUrl,
|
|
579
|
+
dataStorePath: string,
|
|
580
|
+
containerPackageName?: string,
|
|
581
|
+
): string {
|
|
582
|
+
const url = new URL(baseUrl);
|
|
583
|
+
|
|
584
|
+
// If the user has passed an empty dataStorePath, then extract it from the resolved url.
|
|
585
|
+
const actualDataStorePath = dataStorePath || (odspResolvedUrl.dataStorePath ?? "");
|
|
586
|
+
|
|
587
|
+
storeLocatorInOdspUrl(url, {
|
|
588
|
+
siteUrl: odspResolvedUrl.siteUrl,
|
|
589
|
+
driveId: odspResolvedUrl.driveId,
|
|
590
|
+
itemId: odspResolvedUrl.itemId,
|
|
591
|
+
dataStorePath: actualDataStorePath,
|
|
592
|
+
appName: odspResolvedUrl.appName,
|
|
593
|
+
containerPackageName,
|
|
594
|
+
fileVersion: odspResolvedUrl.fileVersion,
|
|
595
|
+
context: odspResolvedUrl.context,
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
return url.href;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Returns the package name of the container package information.
|
|
603
|
+
* @param packageInfoSource - Information of the package connected to the URL
|
|
604
|
+
* @returns The package name of the container package
|
|
605
|
+
*/
|
|
606
|
+
export function getContainerPackageName(
|
|
607
|
+
packageInfoSource: IContainerPackageInfo | undefined,
|
|
608
|
+
): string | undefined {
|
|
609
|
+
let containerPackageName: string | undefined;
|
|
610
|
+
if (packageInfoSource && "name" in packageInfoSource) {
|
|
611
|
+
containerPackageName = packageInfoSource.name;
|
|
612
|
+
// packageInfoSource is cast to any as it is typed to IContainerPackageInfo instead of IFluidCodeDetails
|
|
613
|
+
// TODO: use a stronger type
|
|
614
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
615
|
+
} else if (isFluidPackage((packageInfoSource as any)?.package)) {
|
|
616
|
+
// TODO: use a stronger type
|
|
617
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
618
|
+
containerPackageName = (packageInfoSource as any)?.package.name;
|
|
619
|
+
} else {
|
|
620
|
+
// TODO: use a stronger type
|
|
621
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
|
|
622
|
+
containerPackageName = (packageInfoSource as any)?.package;
|
|
623
|
+
}
|
|
624
|
+
return containerPackageName;
|
|
625
|
+
}
|
package/src/packageVersion.ts
CHANGED