@fluidframework/odsp-driver 2.0.7 → 2.0.9

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.
@@ -1 +1 @@
1
- {"version":3,"file":"createFile.d.ts","sourceRoot":"","sources":["../src/createFile.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAGlE,OAAO,EACN,UAAU,EACV,gBAAgB,EAChB,+BAA+B,EAG/B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EACN,mBAAmB,EAGnB,MAAM,0CAA0C,CAAC;AAElD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAQrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIjD,OAAO,EACN,YAAY,EAKZ,MAAM,gBAAgB,CAAC;AASxB;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,aAAa,EAAE,+BAA+B,EAC9C,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,mBAAmB,EAC3B,gBAAgB,EAAE,YAAY,GAAG,SAAS,EAC1C,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAE,UAAU,EACrB,gBAAgB,EAAE,OAAO,EACzB,sCAAsC,EAAE,OAAO,EAC/C,iBAAiB,CAAC,EAAE,OAAO,EAC3B,yCAAyC,CAAC,EAAE,OAAO,GACjD,OAAO,CAAC,gBAAgB,CAAC,CA2D3B;AA2CD,wBAAsB,uBAAuB,CAC5C,aAAa,EAAE,+BAA+B,EAC9C,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,mBAAmB,EAC3B,YAAY,EAAE,YAAY,GACxB,OAAO,CAAC,MAAM,CAAC,CAuDjB;AAED,wBAAsB,6BAA6B,CAClD,aAAa,EAAE,+BAA+B,EAC9C,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,mBAAmB,EAC3B,gBAAgB,EAAE,YAAY,EAC9B,YAAY,EAAE,YAAY,EAC1B,sCAAsC,EAAE,OAAO,GAC7C,OAAO,CAAC,mBAAmB,CAAC,CAmC9B"}
1
+ {"version":3,"file":"createFile.d.ts","sourceRoot":"","sources":["../src/createFile.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAGlE,OAAO,EACN,UAAU,EACV,gBAAgB,EAChB,+BAA+B,EAG/B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EACN,mBAAmB,EAGnB,MAAM,0CAA0C,CAAC;AAElD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAQrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIjD,OAAO,EACN,YAAY,EAKZ,MAAM,gBAAgB,CAAC;AASxB;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,aAAa,EAAE,+BAA+B,EAC9C,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,mBAAmB,EAC3B,gBAAgB,EAAE,YAAY,GAAG,SAAS,EAC1C,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAE,UAAU,EACrB,gBAAgB,EAAE,OAAO,EACzB,sCAAsC,EAAE,OAAO,EAC/C,iBAAiB,CAAC,EAAE,OAAO,EAC3B,yCAAyC,CAAC,EAAE,OAAO,GACjD,OAAO,CAAC,gBAAgB,CAAC,CA2D3B;AAoDD,wBAAsB,uBAAuB,CAC5C,aAAa,EAAE,+BAA+B,EAC9C,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,mBAAmB,EAC3B,YAAY,EAAE,YAAY,GACxB,OAAO,CAAC,MAAM,CAAC,CAuDjB;AAED,wBAAsB,6BAA6B,CAClD,aAAa,EAAE,+BAA+B,EAC9C,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,mBAAmB,EAC3B,gBAAgB,EAAE,YAAY,EAC9B,YAAY,EAAE,YAAY,EAC1B,sCAAsC,EAAE,OAAO,GAC7C,OAAO,CAAC,mBAAmB,CAAC,CAmC9B"}
@@ -101,8 +101,16 @@ function extractShareLinkData(response, enableSingleRequestForShareLinkWithCreat
101
101
  }
102
102
  return shareLinkInfo;
103
103
  }
104
+ /**
105
+ * Encodes file path so it can be embedded in the request url
106
+ * @param path - path to encode
107
+ * @returns encoded path or "" if path is undefined
108
+ */
109
+ function encodeFilePath(path) {
110
+ return path ? encodeURIComponent(path.startsWith("/") ? path : `/${path}`) : "";
111
+ }
104
112
  async function createNewEmptyFluidFile(getAuthHeader, newFileInfo, logger, epochTracker) {
105
- const filePath = newFileInfo.filePath ? encodeURIComponent(`/${newFileInfo.filePath}`) : "";
113
+ const filePath = encodeFilePath(newFileInfo.filePath);
106
114
  // add .tmp extension to empty file (host is expected to rename)
107
115
  const encodedFilename = encodeURIComponent(`${newFileInfo.filename}.tmp`);
108
116
  const initialUrl = `${(0, odspUrlHelper_js_1.getApiRoot)(new URL(newFileInfo.siteUrl))}/drives/${newFileInfo.driveId}/items/root:/${filePath}/${encodedFilename}:/content?@name.conflictBehavior=rename&select=id,name,parentReference`;
@@ -133,7 +141,7 @@ async function createNewEmptyFluidFile(getAuthHeader, newFileInfo, logger, epoch
133
141
  }
134
142
  exports.createNewEmptyFluidFile = createNewEmptyFluidFile;
135
143
  async function createNewFluidFileFromSummary(getAuthHeader, newFileInfo, logger, createNewSummary, epochTracker, forceAccessTokenViaAuthorizationHeader) {
136
- const filePath = newFileInfo.filePath ? encodeURIComponent(`/${newFileInfo.filePath}`) : "";
144
+ const filePath = encodeFilePath(newFileInfo.filePath);
137
145
  const encodedFilename = encodeURIComponent(newFileInfo.filename);
138
146
  const baseUrl = `${(0, odspUrlHelper_js_1.getApiRoot)(new URL(newFileInfo.siteUrl))}/drives/${newFileInfo.driveId}/items/root:` +
139
147
  `${filePath}/${encodedFilename}`;
@@ -1 +1 @@
1
- {"version":3,"file":"createFile.js","sourceRoot":"","sources":["../src/createFile.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { ISummaryTree } from \"@fluidframework/driver-definitions\";\nimport { ISnapshot } from \"@fluidframework/driver-definitions/internal\";\nimport { NonRetryableError } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tIFileEntry,\n\tIOdspResolvedUrl,\n\tInstrumentedStorageTokenFetcher,\n\tOdspErrorTypes,\n\tShareLinkInfoType,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tloggerToMonitoringContext,\n\tPerformanceEvent,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { ICreateFileResponse } from \"./contracts.js\";\nimport { ClpCompliantAppHeader } from \"./contractsPublic.js\";\nimport {\n\tconvertCreateNewSummaryTreeToTreeAndBlobs,\n\tconvertSummaryIntoContainerSnapshot,\n\tcreateNewFluidContainerCore,\n} from \"./createNewUtils.js\";\nimport { createOdspUrl } from \"./createOdspUrl.js\";\nimport { EpochTracker } from \"./epochTracker.js\";\nimport { getHeadersWithAuth } from \"./getUrlAndHeadersWithAuth.js\";\nimport { OdspDriverUrlResolver } from \"./odspDriverUrlResolver.js\";\nimport { getApiRoot } from \"./odspUrlHelper.js\";\nimport {\n\tINewFileInfo,\n\tbuildOdspShareLinkReqParams,\n\tcreateCacheSnapshotKey,\n\tgetWithRetryForTokenRefresh,\n\tsnapshotWithLoadingGroupIdSupported,\n} from \"./odspUtils.js\";\nimport { pkgVersion as driverVersion } from \"./packageVersion.js\";\nimport { runWithRetry } from \"./retryUtils.js\";\n\nconst isInvalidFileName = (fileName: string): boolean => {\n\tconst invalidCharsRegex = /[\"*/:<>?\\\\|]+/g;\n\treturn invalidCharsRegex.test(fileName);\n};\n\n/**\n * Creates a new Fluid file.\n * Returns resolved url\n */\nexport async function createNewFluidFile(\n\tgetAuthHeader: InstrumentedStorageTokenFetcher,\n\tnewFileInfo: INewFileInfo,\n\tlogger: ITelemetryLoggerExt,\n\tcreateNewSummary: ISummaryTree | undefined,\n\tepochTracker: EpochTracker,\n\tfileEntry: IFileEntry,\n\tcreateNewCaching: boolean,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n\tisClpCompliantApp?: boolean,\n\tenableSingleRequestForShareLinkWithCreate?: boolean,\n): Promise<IOdspResolvedUrl> {\n\t// Check for valid filename before the request to create file is actually made.\n\tif (isInvalidFileName(newFileInfo.filename)) {\n\t\tthrow new NonRetryableError(\n\t\t\t// pre-0.58 error message: Invalid filename\n\t\t\t\"Invalid filename for createNew\",\n\t\t\tOdspErrorTypes.invalidFileNameError,\n\t\t\t{ driverVersion },\n\t\t);\n\t}\n\n\tlet itemId: string;\n\tlet summaryHandle: string = \"\";\n\tlet shareLinkInfo: ShareLinkInfoType | undefined;\n\tif (createNewSummary === undefined) {\n\t\titemId = await createNewEmptyFluidFile(getAuthHeader, newFileInfo, logger, epochTracker);\n\t} else {\n\t\tconst content = await createNewFluidFileFromSummary(\n\t\t\tgetAuthHeader,\n\t\t\tnewFileInfo,\n\t\t\tlogger,\n\t\t\tcreateNewSummary,\n\t\t\tepochTracker,\n\t\t\tforceAccessTokenViaAuthorizationHeader,\n\t\t);\n\t\titemId = content.itemId;\n\t\tsummaryHandle = content.id;\n\n\t\tshareLinkInfo = extractShareLinkData(content, enableSingleRequestForShareLinkWithCreate);\n\t}\n\n\tconst odspUrl = createOdspUrl({ ...newFileInfo, itemId, dataStorePath: \"/\" });\n\tconst resolver = new OdspDriverUrlResolver();\n\tconst odspResolvedUrl = await resolver.resolve({\n\t\turl: odspUrl,\n\t\theaders: { [ClpCompliantAppHeader.isClpCompliantApp]: isClpCompliantApp },\n\t});\n\tfileEntry.docId = odspResolvedUrl.hashedDocumentId;\n\tfileEntry.resolvedUrl = odspResolvedUrl;\n\n\todspResolvedUrl.shareLinkInfo = shareLinkInfo;\n\n\tif (createNewSummary !== undefined && createNewCaching) {\n\t\tassert(summaryHandle !== undefined, 0x203 /* \"Summary handle is undefined\" */);\n\t\t// converting summary and getting sequence number\n\t\tconst snapshot: ISnapshot = convertCreateNewSummaryTreeToTreeAndBlobs(\n\t\t\tcreateNewSummary,\n\t\t\tsummaryHandle,\n\t\t);\n\t\t// caching the converted summary\n\t\tawait epochTracker.put(\n\t\t\tcreateCacheSnapshotKey(\n\t\t\t\todspResolvedUrl,\n\t\t\t\tsnapshotWithLoadingGroupIdSupported(loggerToMonitoringContext(logger).config),\n\t\t\t),\n\t\t\tsnapshot,\n\t\t);\n\t}\n\treturn odspResolvedUrl;\n}\n\n/**\n * If user requested creation of a sharing link along with the creation of the file by providing\n * createLinkScope in the request parameters then extract and save the sharing link information from\n * the response if it is available.\n * In case there was an error in creation of the sharing link, error is provided back in the response,\n * and does not impact the creation of file in ODSP.\n * @param requestedSharingLinkKind - Kind of sharing link requested to be created along with the creation of file.\n * @param response - Response object received from the /snapshot api call\n * @returns Sharing link information received in the response from a successful creation of a file.\n */\nfunction extractShareLinkData(\n\tresponse: ICreateFileResponse,\n\tenableSingleRequestForShareLinkWithCreate?: boolean,\n): ShareLinkInfoType | undefined {\n\tlet shareLinkInfo: ShareLinkInfoType | undefined;\n\tif (enableSingleRequestForShareLinkWithCreate) {\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\t\tconst { sharing } = response;\n\t\tif (!sharing) {\n\t\t\treturn;\n\t\t}\n\t\t/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */\n\t\tshareLinkInfo = {\n\t\t\tcreateLink: {\n\t\t\t\tlink: sharing.sharingLink\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tscope: sharing.sharingLink.scope,\n\t\t\t\t\t\t\trole: sharing.sharingLink.type,\n\t\t\t\t\t\t\twebUrl: sharing.sharingLink.webUrl,\n\t\t\t\t\t\t\t...sharing.sharingLink,\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\t\terror: sharing.error,\n\t\t\t\tshareId: sharing.shareId,\n\t\t\t},\n\t\t};\n\t\t/* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */\n\t}\n\treturn shareLinkInfo;\n}\n\nexport async function createNewEmptyFluidFile(\n\tgetAuthHeader: InstrumentedStorageTokenFetcher,\n\tnewFileInfo: INewFileInfo,\n\tlogger: ITelemetryLoggerExt,\n\tepochTracker: EpochTracker,\n): Promise<string> {\n\tconst filePath = newFileInfo.filePath ? encodeURIComponent(`/${newFileInfo.filePath}`) : \"\";\n\t// add .tmp extension to empty file (host is expected to rename)\n\tconst encodedFilename = encodeURIComponent(`${newFileInfo.filename}.tmp`);\n\tconst initialUrl = `${getApiRoot(new URL(newFileInfo.siteUrl))}/drives/${\n\t\tnewFileInfo.driveId\n\t}/items/root:/${filePath}/${encodedFilename}:/content?@name.conflictBehavior=rename&select=id,name,parentReference`;\n\n\treturn getWithRetryForTokenRefresh(async (options) => {\n\t\tconst url = initialUrl;\n\t\tconst method = \"PUT\";\n\t\tconst authHeader = await getAuthHeader(\n\t\t\t{ ...options, request: { url, method } },\n\t\t\t\"CreateNewFile\",\n\t\t);\n\n\t\treturn PerformanceEvent.timedExecAsync(\n\t\t\tlogger,\n\t\t\t{ eventName: \"createNewEmptyFile\" },\n\t\t\tasync (event) => {\n\t\t\t\tconst headers = getHeadersWithAuth(authHeader);\n\t\t\t\theaders[\"Content-Type\"] = \"application/json\";\n\n\t\t\t\tconst fetchResponse = await runWithRetry(\n\t\t\t\t\tasync () =>\n\t\t\t\t\t\tepochTracker.fetchAndParseAsJSON<ICreateFileResponse>(\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbody: undefined,\n\t\t\t\t\t\t\t\theaders,\n\t\t\t\t\t\t\t\tmethod,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"createFile\",\n\t\t\t\t\t\t),\n\t\t\t\t\t\"createFile\",\n\t\t\t\t\tlogger,\n\t\t\t\t);\n\n\t\t\t\tconst content = fetchResponse.content;\n\t\t\t\tif (!content?.id) {\n\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t// pre-0.58 error message: ODSP CreateFile call returned no item ID\n\t\t\t\t\t\t\"ODSP CreateFile call returned no item ID (for empty file)\",\n\t\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t\t{ driverVersion },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tevent.end({\n\t\t\t\t\t...fetchResponse.propsToLog,\n\t\t\t\t});\n\t\t\t\treturn content.id;\n\t\t\t},\n\t\t\t{ end: true, cancel: \"error\" },\n\t\t);\n\t});\n}\n\nexport async function createNewFluidFileFromSummary(\n\tgetAuthHeader: InstrumentedStorageTokenFetcher,\n\tnewFileInfo: INewFileInfo,\n\tlogger: ITelemetryLoggerExt,\n\tcreateNewSummary: ISummaryTree,\n\tepochTracker: EpochTracker,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n): Promise<ICreateFileResponse> {\n\tconst filePath = newFileInfo.filePath ? encodeURIComponent(`/${newFileInfo.filePath}`) : \"\";\n\tconst encodedFilename = encodeURIComponent(newFileInfo.filename);\n\tconst baseUrl =\n\t\t`${getApiRoot(new URL(newFileInfo.siteUrl))}/drives/${newFileInfo.driveId}/items/root:` +\n\t\t`${filePath}/${encodedFilename}`;\n\n\tconst containerSnapshot = convertSummaryIntoContainerSnapshot(createNewSummary);\n\n\t// Build share link parameter based on the createLinkType provided so that the\n\t// snapshot api can create and return the share link along with creation of file in the response.\n\tconst createShareLinkParam = buildOdspShareLinkReqParams(newFileInfo.createLinkType);\n\tconst initialUrl = `${baseUrl}:/opStream/snapshots/snapshot${\n\t\tcreateShareLinkParam ? `?${createShareLinkParam}` : \"\"\n\t}`;\n\n\treturn createNewFluidContainerCore<ICreateFileResponse>({\n\t\tcontainerSnapshot,\n\t\tgetAuthHeader,\n\t\tlogger,\n\t\tinitialUrl,\n\t\tforceAccessTokenViaAuthorizationHeader,\n\t\tepochTracker,\n\t\ttelemetryName: \"CreateNewFile\",\n\t\tfetchType: \"createFile\",\n\t\tvalidateResponseCallback: (content) => {\n\t\t\tif (!content?.itemId) {\n\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\"ODSP CreateFile call returned no item ID\",\n\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t{ driverVersion },\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t});\n}\n"]}
1
+ {"version":3,"file":"createFile.js","sourceRoot":"","sources":["../src/createFile.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { ISummaryTree } from \"@fluidframework/driver-definitions\";\nimport { ISnapshot } from \"@fluidframework/driver-definitions/internal\";\nimport { NonRetryableError } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tIFileEntry,\n\tIOdspResolvedUrl,\n\tInstrumentedStorageTokenFetcher,\n\tOdspErrorTypes,\n\tShareLinkInfoType,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tloggerToMonitoringContext,\n\tPerformanceEvent,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { ICreateFileResponse } from \"./contracts.js\";\nimport { ClpCompliantAppHeader } from \"./contractsPublic.js\";\nimport {\n\tconvertCreateNewSummaryTreeToTreeAndBlobs,\n\tconvertSummaryIntoContainerSnapshot,\n\tcreateNewFluidContainerCore,\n} from \"./createNewUtils.js\";\nimport { createOdspUrl } from \"./createOdspUrl.js\";\nimport { EpochTracker } from \"./epochTracker.js\";\nimport { getHeadersWithAuth } from \"./getUrlAndHeadersWithAuth.js\";\nimport { OdspDriverUrlResolver } from \"./odspDriverUrlResolver.js\";\nimport { getApiRoot } from \"./odspUrlHelper.js\";\nimport {\n\tINewFileInfo,\n\tbuildOdspShareLinkReqParams,\n\tcreateCacheSnapshotKey,\n\tgetWithRetryForTokenRefresh,\n\tsnapshotWithLoadingGroupIdSupported,\n} from \"./odspUtils.js\";\nimport { pkgVersion as driverVersion } from \"./packageVersion.js\";\nimport { runWithRetry } from \"./retryUtils.js\";\n\nconst isInvalidFileName = (fileName: string): boolean => {\n\tconst invalidCharsRegex = /[\"*/:<>?\\\\|]+/g;\n\treturn invalidCharsRegex.test(fileName);\n};\n\n/**\n * Creates a new Fluid file.\n * Returns resolved url\n */\nexport async function createNewFluidFile(\n\tgetAuthHeader: InstrumentedStorageTokenFetcher,\n\tnewFileInfo: INewFileInfo,\n\tlogger: ITelemetryLoggerExt,\n\tcreateNewSummary: ISummaryTree | undefined,\n\tepochTracker: EpochTracker,\n\tfileEntry: IFileEntry,\n\tcreateNewCaching: boolean,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n\tisClpCompliantApp?: boolean,\n\tenableSingleRequestForShareLinkWithCreate?: boolean,\n): Promise<IOdspResolvedUrl> {\n\t// Check for valid filename before the request to create file is actually made.\n\tif (isInvalidFileName(newFileInfo.filename)) {\n\t\tthrow new NonRetryableError(\n\t\t\t// pre-0.58 error message: Invalid filename\n\t\t\t\"Invalid filename for createNew\",\n\t\t\tOdspErrorTypes.invalidFileNameError,\n\t\t\t{ driverVersion },\n\t\t);\n\t}\n\n\tlet itemId: string;\n\tlet summaryHandle: string = \"\";\n\tlet shareLinkInfo: ShareLinkInfoType | undefined;\n\tif (createNewSummary === undefined) {\n\t\titemId = await createNewEmptyFluidFile(getAuthHeader, newFileInfo, logger, epochTracker);\n\t} else {\n\t\tconst content = await createNewFluidFileFromSummary(\n\t\t\tgetAuthHeader,\n\t\t\tnewFileInfo,\n\t\t\tlogger,\n\t\t\tcreateNewSummary,\n\t\t\tepochTracker,\n\t\t\tforceAccessTokenViaAuthorizationHeader,\n\t\t);\n\t\titemId = content.itemId;\n\t\tsummaryHandle = content.id;\n\n\t\tshareLinkInfo = extractShareLinkData(content, enableSingleRequestForShareLinkWithCreate);\n\t}\n\n\tconst odspUrl = createOdspUrl({ ...newFileInfo, itemId, dataStorePath: \"/\" });\n\tconst resolver = new OdspDriverUrlResolver();\n\tconst odspResolvedUrl = await resolver.resolve({\n\t\turl: odspUrl,\n\t\theaders: { [ClpCompliantAppHeader.isClpCompliantApp]: isClpCompliantApp },\n\t});\n\tfileEntry.docId = odspResolvedUrl.hashedDocumentId;\n\tfileEntry.resolvedUrl = odspResolvedUrl;\n\n\todspResolvedUrl.shareLinkInfo = shareLinkInfo;\n\n\tif (createNewSummary !== undefined && createNewCaching) {\n\t\tassert(summaryHandle !== undefined, 0x203 /* \"Summary handle is undefined\" */);\n\t\t// converting summary and getting sequence number\n\t\tconst snapshot: ISnapshot = convertCreateNewSummaryTreeToTreeAndBlobs(\n\t\t\tcreateNewSummary,\n\t\t\tsummaryHandle,\n\t\t);\n\t\t// caching the converted summary\n\t\tawait epochTracker.put(\n\t\t\tcreateCacheSnapshotKey(\n\t\t\t\todspResolvedUrl,\n\t\t\t\tsnapshotWithLoadingGroupIdSupported(loggerToMonitoringContext(logger).config),\n\t\t\t),\n\t\t\tsnapshot,\n\t\t);\n\t}\n\treturn odspResolvedUrl;\n}\n\n/**\n * If user requested creation of a sharing link along with the creation of the file by providing\n * createLinkScope in the request parameters then extract and save the sharing link information from\n * the response if it is available.\n * In case there was an error in creation of the sharing link, error is provided back in the response,\n * and does not impact the creation of file in ODSP.\n * @param requestedSharingLinkKind - Kind of sharing link requested to be created along with the creation of file.\n * @param response - Response object received from the /snapshot api call\n * @returns Sharing link information received in the response from a successful creation of a file.\n */\nfunction extractShareLinkData(\n\tresponse: ICreateFileResponse,\n\tenableSingleRequestForShareLinkWithCreate?: boolean,\n): ShareLinkInfoType | undefined {\n\tlet shareLinkInfo: ShareLinkInfoType | undefined;\n\tif (enableSingleRequestForShareLinkWithCreate) {\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\t\tconst { sharing } = response;\n\t\tif (!sharing) {\n\t\t\treturn;\n\t\t}\n\t\t/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */\n\t\tshareLinkInfo = {\n\t\t\tcreateLink: {\n\t\t\t\tlink: sharing.sharingLink\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tscope: sharing.sharingLink.scope,\n\t\t\t\t\t\t\trole: sharing.sharingLink.type,\n\t\t\t\t\t\t\twebUrl: sharing.sharingLink.webUrl,\n\t\t\t\t\t\t\t...sharing.sharingLink,\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\t\terror: sharing.error,\n\t\t\t\tshareId: sharing.shareId,\n\t\t\t},\n\t\t};\n\t\t/* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */\n\t}\n\treturn shareLinkInfo;\n}\n\n/**\n * Encodes file path so it can be embedded in the request url\n * @param path - path to encode\n * @returns encoded path or \"\" if path is undefined\n */\nfunction encodeFilePath(path: string | undefined): string {\n\treturn path ? encodeURIComponent(path.startsWith(\"/\") ? path : `/${path}`) : \"\";\n}\n\nexport async function createNewEmptyFluidFile(\n\tgetAuthHeader: InstrumentedStorageTokenFetcher,\n\tnewFileInfo: INewFileInfo,\n\tlogger: ITelemetryLoggerExt,\n\tepochTracker: EpochTracker,\n): Promise<string> {\n\tconst filePath = encodeFilePath(newFileInfo.filePath);\n\t// add .tmp extension to empty file (host is expected to rename)\n\tconst encodedFilename = encodeURIComponent(`${newFileInfo.filename}.tmp`);\n\tconst initialUrl = `${getApiRoot(new URL(newFileInfo.siteUrl))}/drives/${\n\t\tnewFileInfo.driveId\n\t}/items/root:/${filePath}/${encodedFilename}:/content?@name.conflictBehavior=rename&select=id,name,parentReference`;\n\n\treturn getWithRetryForTokenRefresh(async (options) => {\n\t\tconst url = initialUrl;\n\t\tconst method = \"PUT\";\n\t\tconst authHeader = await getAuthHeader(\n\t\t\t{ ...options, request: { url, method } },\n\t\t\t\"CreateNewFile\",\n\t\t);\n\n\t\treturn PerformanceEvent.timedExecAsync(\n\t\t\tlogger,\n\t\t\t{ eventName: \"createNewEmptyFile\" },\n\t\t\tasync (event) => {\n\t\t\t\tconst headers = getHeadersWithAuth(authHeader);\n\t\t\t\theaders[\"Content-Type\"] = \"application/json\";\n\n\t\t\t\tconst fetchResponse = await runWithRetry(\n\t\t\t\t\tasync () =>\n\t\t\t\t\t\tepochTracker.fetchAndParseAsJSON<ICreateFileResponse>(\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbody: undefined,\n\t\t\t\t\t\t\t\theaders,\n\t\t\t\t\t\t\t\tmethod,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"createFile\",\n\t\t\t\t\t\t),\n\t\t\t\t\t\"createFile\",\n\t\t\t\t\tlogger,\n\t\t\t\t);\n\n\t\t\t\tconst content = fetchResponse.content;\n\t\t\t\tif (!content?.id) {\n\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t// pre-0.58 error message: ODSP CreateFile call returned no item ID\n\t\t\t\t\t\t\"ODSP CreateFile call returned no item ID (for empty file)\",\n\t\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t\t{ driverVersion },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tevent.end({\n\t\t\t\t\t...fetchResponse.propsToLog,\n\t\t\t\t});\n\t\t\t\treturn content.id;\n\t\t\t},\n\t\t\t{ end: true, cancel: \"error\" },\n\t\t);\n\t});\n}\n\nexport async function createNewFluidFileFromSummary(\n\tgetAuthHeader: InstrumentedStorageTokenFetcher,\n\tnewFileInfo: INewFileInfo,\n\tlogger: ITelemetryLoggerExt,\n\tcreateNewSummary: ISummaryTree,\n\tepochTracker: EpochTracker,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n): Promise<ICreateFileResponse> {\n\tconst filePath = encodeFilePath(newFileInfo.filePath);\n\tconst encodedFilename = encodeURIComponent(newFileInfo.filename);\n\tconst baseUrl =\n\t\t`${getApiRoot(new URL(newFileInfo.siteUrl))}/drives/${newFileInfo.driveId}/items/root:` +\n\t\t`${filePath}/${encodedFilename}`;\n\n\tconst containerSnapshot = convertSummaryIntoContainerSnapshot(createNewSummary);\n\n\t// Build share link parameter based on the createLinkType provided so that the\n\t// snapshot api can create and return the share link along with creation of file in the response.\n\tconst createShareLinkParam = buildOdspShareLinkReqParams(newFileInfo.createLinkType);\n\tconst initialUrl = `${baseUrl}:/opStream/snapshots/snapshot${\n\t\tcreateShareLinkParam ? `?${createShareLinkParam}` : \"\"\n\t}`;\n\n\treturn createNewFluidContainerCore<ICreateFileResponse>({\n\t\tcontainerSnapshot,\n\t\tgetAuthHeader,\n\t\tlogger,\n\t\tinitialUrl,\n\t\tforceAccessTokenViaAuthorizationHeader,\n\t\tepochTracker,\n\t\ttelemetryName: \"CreateNewFile\",\n\t\tfetchType: \"createFile\",\n\t\tvalidateResponseCallback: (content) => {\n\t\t\tif (!content?.itemId) {\n\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\"ODSP CreateFile call returned no item ID\",\n\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t{ driverVersion },\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t});\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { IOdspUrlParts, OdspResourceTokenFetchOptions, TokenFetcher } from "@fluidframework/odsp-driver-definitions/internal";
5
+ import { OdspResourceTokenFetchOptions, TokenFetcher, type IOdspResolvedUrl } from "@fluidframework/odsp-driver-definitions/internal";
6
6
  import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
7
7
  /**
8
8
  * Returns file link for a file with given drive and item ids.
@@ -15,5 +15,5 @@ import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
15
15
  * @param logger - used to log results of operation, including any error
16
16
  * @returns Promise which resolves to file link url when successful; otherwise, undefined.
17
17
  */
18
- export declare function getFileLink(getToken: TokenFetcher<OdspResourceTokenFetchOptions>, odspUrlParts: IOdspUrlParts, logger: ITelemetryLoggerExt): Promise<string>;
18
+ export declare function getFileLink(getToken: TokenFetcher<OdspResourceTokenFetchOptions>, resolvedUrl: IOdspResolvedUrl, logger: ITelemetryLoggerExt): Promise<string>;
19
19
  //# sourceMappingURL=getFileLink.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getFileLink.d.ts","sourceRoot":"","sources":["../src/getFileLink.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EACN,aAAa,EAEb,6BAA6B,EAC7B,YAAY,EACZ,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EACN,mBAAmB,EAGnB,MAAM,0CAA0C,CAAC;AAclD;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAChC,QAAQ,EAAE,YAAY,CAAC,6BAA6B,CAAC,EACrD,YAAY,EAAE,aAAa,EAC3B,MAAM,EAAE,mBAAmB,GACzB,OAAO,CAAC,MAAM,CAAC,CAqDjB"}
1
+ {"version":3,"file":"getFileLink.d.ts","sourceRoot":"","sources":["../src/getFileLink.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAGN,6BAA6B,EAC7B,YAAY,EACZ,KAAK,gBAAgB,EACrB,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EACN,mBAAmB,EAGnB,MAAM,0CAA0C,CAAC;AAclD;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAChC,QAAQ,EAAE,YAAY,CAAC,6BAA6B,CAAC,EACrD,WAAW,EAAE,gBAAgB,EAC7B,MAAM,EAAE,mBAAmB,GACzB,OAAO,CAAC,MAAM,CAAC,CAqDjB"}
@@ -7,9 +7,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.getFileLink = void 0;
8
8
  const internal_1 = require("@fluidframework/core-utils/internal");
9
9
  const internal_2 = require("@fluidframework/driver-utils/internal");
10
- const internal_3 = require("@fluidframework/odsp-doclib-utils/internal");
11
- const internal_4 = require("@fluidframework/odsp-driver-definitions/internal");
12
- const internal_5 = require("@fluidframework/telemetry-utils/internal");
10
+ const internal_3 = require("@fluidframework/odsp-driver-definitions/internal");
11
+ const internal_4 = require("@fluidframework/telemetry-utils/internal");
13
12
  const getUrlAndHeadersWithAuth_js_1 = require("./getUrlAndHeadersWithAuth.js");
14
13
  const odspUtils_js_1 = require("./odspUtils.js");
15
14
  const packageVersion_js_1 = require("./packageVersion.js");
@@ -27,8 +26,8 @@ const fileLinkCache = new Map();
27
26
  * @param logger - used to log results of operation, including any error
28
27
  * @returns Promise which resolves to file link url when successful; otherwise, undefined.
29
28
  */
30
- async function getFileLink(getToken, odspUrlParts, logger) {
31
- const cacheKey = `${odspUrlParts.siteUrl}_${odspUrlParts.driveId}_${odspUrlParts.itemId}`;
29
+ async function getFileLink(getToken, resolvedUrl, logger) {
30
+ const cacheKey = `${resolvedUrl.siteUrl}_${resolvedUrl.driveId}_${resolvedUrl.itemId}`;
32
31
  const maybeFileLinkCacheEntry = fileLinkCache.get(cacheKey);
33
32
  if (maybeFileLinkCacheEntry !== undefined) {
34
33
  return maybeFileLinkCacheEntry;
@@ -37,7 +36,7 @@ async function getFileLink(getToken, odspUrlParts, logger) {
37
36
  let fileLinkCore;
38
37
  try {
39
38
  let retryCount = 0;
40
- fileLinkCore = await (0, internal_2.runWithRetry)(async () => (0, retryUtils_js_1.runWithRetry)(async () => getFileLinkWithLocationRedirectionHandling(getToken, odspUrlParts, logger), "getFileLinkCore", logger), "getShareLink", logger, {
39
+ fileLinkCore = await (0, internal_2.runWithRetry)(async () => (0, retryUtils_js_1.runWithRetry)(async () => getFileLinkWithLocationRedirectionHandling(getToken, resolvedUrl, logger), "getFileLinkCore", logger), "getShareLink", logger, {
41
40
  // TODO: use a stronger type
42
41
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
42
  onRetry(delayInMs, error) {
@@ -79,29 +78,38 @@ exports.getFileLink = getFileLink;
79
78
  * @returns Response from the API call.
80
79
  * @alpha
81
80
  */
82
- async function getFileLinkWithLocationRedirectionHandling(getToken, odspUrlParts, logger) {
81
+ async function getFileLinkWithLocationRedirectionHandling(getToken, resolvedUrl, logger) {
83
82
  // We can have chains of location redirection one after the other, so have a for loop
84
83
  // so that we can keep handling the same type of error. Set max number of redirection to 5.
85
84
  let lastError;
85
+ let locationRedirected = false;
86
86
  for (let count = 1; count <= 5; count++) {
87
87
  try {
88
- return await getFileLinkCore(getToken, odspUrlParts, logger);
89
- }
90
- catch (error) {
91
- lastError = error;
92
- if ((0, internal_5.isFluidError)(error) &&
93
- error.errorType === internal_4.OdspErrorTypes.fileNotFoundOrAccessDeniedError &&
94
- (0, internal_3.hasRedirectionLocation)(error) &&
95
- error.redirectLocation !== undefined) {
96
- const redirectLocation = error.redirectLocation;
88
+ const fileItem = await getFileItemLite(getToken, resolvedUrl, logger, true);
89
+ // Sometimes the siteUrl in the actual file is different from the siteUrl in the resolvedUrl due to location
90
+ // redirection. This creates issues in the getSharingInformation call. So we need to update the siteUrl in the
91
+ // resolvedUrl to the siteUrl in the fileItem which is the updated siteUrl.
92
+ const oldSiteDomain = new URL(resolvedUrl.siteUrl).origin;
93
+ const newSiteDomain = new URL(fileItem.sharepointIds.siteUrl).origin;
94
+ if (oldSiteDomain !== newSiteDomain) {
95
+ locationRedirected = true;
97
96
  logger.sendTelemetryEvent({
98
97
  eventName: "LocationRedirectionErrorForGetOdspFileLink",
99
98
  retryCount: count,
100
99
  });
101
- // Generate the new SiteUrl from the redirection location.
102
- const newSiteDomain = new URL(redirectLocation).origin;
103
- const newSiteUrl = `${newSiteDomain}${new URL(odspUrlParts.siteUrl).pathname}`;
104
- odspUrlParts.siteUrl = newSiteUrl;
100
+ renameTenantInOdspResolvedUrl(resolvedUrl, newSiteDomain);
101
+ }
102
+ return await getFileLinkCore(getToken, resolvedUrl, logger, fileItem);
103
+ }
104
+ catch (error) {
105
+ lastError = error;
106
+ // If the getSharingLink call fails with the 401/403/404 error, then it could be due to that the file has moved
107
+ // to another location. This could occur in case we have more than 1 tenant rename. In that case, we should retry
108
+ // the getFileItemLite call to get the updated fileItem.
109
+ if ((0, internal_4.isFluidError)(error) &&
110
+ locationRedirected &&
111
+ (error.errorType === internal_3.OdspErrorTypes.fileNotFoundOrAccessDeniedError ||
112
+ error.errorType === internal_3.OdspErrorTypes.authorizationError)) {
105
113
  continue;
106
114
  }
107
115
  throw error;
@@ -109,10 +117,9 @@ async function getFileLinkWithLocationRedirectionHandling(getToken, odspUrlParts
109
117
  }
110
118
  throw lastError;
111
119
  }
112
- async function getFileLinkCore(getToken, odspUrlParts, logger) {
113
- const fileItem = await getFileItemLite(getToken, odspUrlParts, logger, true);
120
+ async function getFileLinkCore(getToken, odspUrlParts, logger, fileItem) {
114
121
  // ODSP link requires extra call to return link that is resistant to file being renamed or moved to different folder
115
- return internal_5.PerformanceEvent.timedExecAsync(logger, { eventName: "odspFileLink", requestName: "getSharingInformation" }, async (event) => {
122
+ return internal_4.PerformanceEvent.timedExecAsync(logger, { eventName: "odspFileLink", requestName: "getSharingInformation" }, async (event) => {
116
123
  let attempts = 0;
117
124
  let additionalProps;
118
125
  const fileLink = await (0, odspUtils_js_1.getWithRetryForTokenRefresh)(async (options) => {
@@ -133,7 +140,6 @@ async function getFileLinkCore(getToken, odspUrlParts, logger) {
133
140
  headers: {
134
141
  "Content-Type": "application/json;odata=verbose",
135
142
  "Accept": "application/json;odata=verbose",
136
- "redirect": "manual",
137
143
  ...headers,
138
144
  },
139
145
  };
@@ -145,7 +151,7 @@ async function getFileLinkCore(getToken, odspUrlParts, logger) {
145
151
  const directUrl = sharingInfo?.d?.directUrl;
146
152
  if (typeof directUrl !== "string") {
147
153
  // This will retry once in getWithRetryForTokenRefresh
148
- throw new internal_2.NonRetryableError("Malformed GetSharingInformation response", internal_4.OdspErrorTypes.incorrectServerResponse, { driverVersion: packageVersion_js_1.pkgVersion });
154
+ throw new internal_2.NonRetryableError("Malformed GetSharingInformation response", internal_3.OdspErrorTypes.incorrectServerResponse, { driverVersion: packageVersion_js_1.pkgVersion });
149
155
  }
150
156
  return directUrl;
151
157
  });
@@ -159,7 +165,7 @@ const isFileItemLite = (maybeFileItemLite) => typeof maybeFileItemLite.webUrl ==
159
165
  // TODO: stronger check
160
166
  typeof maybeFileItemLite.sharepointIds === "object";
161
167
  async function getFileItemLite(getToken, odspUrlParts, logger, forceAccessTokenViaAuthorizationHeader) {
162
- return internal_5.PerformanceEvent.timedExecAsync(logger, { eventName: "odspFileLink", requestName: "getFileItemLite" }, async (event) => {
168
+ return internal_4.PerformanceEvent.timedExecAsync(logger, { eventName: "odspFileLink", requestName: "getFileItemLite" }, async (event) => {
163
169
  let attempts = 0;
164
170
  let additionalProps;
165
171
  const fileItem = await (0, odspUtils_js_1.getWithRetryForTokenRefresh)(async (options) => {
@@ -171,14 +177,13 @@ async function getFileItemLite(getToken, odspUrlParts, logger, forceAccessTokenV
171
177
  const authHeader = await getAuthHeader({ ...options, request: { url, method } }, "GetFileItemLite");
172
178
  (0, internal_1.assert)(authHeader !== null, 0x2bc /* "Instrumented token fetcher with throwOnNullToken =true should never return null" */);
173
179
  const headers = (0, getUrlAndHeadersWithAuth_js_1.getHeadersWithAuth)(authHeader);
174
- headers.redirect = "manual";
175
180
  const requestInit = { method, headers };
176
181
  const response = await (0, odspUtils_js_1.fetchHelper)(url, requestInit);
177
182
  additionalProps = response.propsToLog;
178
183
  const responseJson = await response.content.json();
179
184
  if (!isFileItemLite(responseJson)) {
180
185
  // This will retry once in getWithRetryForTokenRefresh
181
- throw new internal_2.NonRetryableError("Malformed getFileItemLite response", internal_4.OdspErrorTypes.incorrectServerResponse, { driverVersion: packageVersion_js_1.pkgVersion });
186
+ throw new internal_2.NonRetryableError("Malformed getFileItemLite response", internal_3.OdspErrorTypes.incorrectServerResponse, { driverVersion: packageVersion_js_1.pkgVersion });
182
187
  }
183
188
  return responseJson;
184
189
  });
@@ -186,4 +191,25 @@ async function getFileItemLite(getToken, odspUrlParts, logger, forceAccessTokenV
186
191
  return fileItem;
187
192
  });
188
193
  }
194
+ /**
195
+ * It takes a resolved url with old siteUrl and patches resolved url with updated site url domain.
196
+ * @param odspResolvedUrl - Previous odsp resolved url with older site url.
197
+ * @param newSiteDomain - New site domain after the tenant rename.
198
+ */
199
+ function renameTenantInOdspResolvedUrl(odspResolvedUrl, newSiteDomain) {
200
+ const newSiteUrl = `${newSiteDomain}${new URL(odspResolvedUrl.siteUrl).pathname}`;
201
+ odspResolvedUrl.siteUrl = newSiteUrl;
202
+ if (odspResolvedUrl.endpoints.attachmentGETStorageUrl) {
203
+ odspResolvedUrl.endpoints.attachmentGETStorageUrl = `${newSiteDomain}${new URL(odspResolvedUrl.endpoints.attachmentGETStorageUrl).pathname}`;
204
+ }
205
+ if (odspResolvedUrl.endpoints.attachmentPOSTStorageUrl) {
206
+ odspResolvedUrl.endpoints.attachmentPOSTStorageUrl = `${newSiteDomain}${new URL(odspResolvedUrl.endpoints.attachmentPOSTStorageUrl).pathname}`;
207
+ }
208
+ if (odspResolvedUrl.endpoints.deltaStorageUrl) {
209
+ odspResolvedUrl.endpoints.deltaStorageUrl = `${newSiteDomain}${new URL(odspResolvedUrl.endpoints.deltaStorageUrl).pathname}`;
210
+ }
211
+ if (odspResolvedUrl.endpoints.snapshotStorageUrl) {
212
+ odspResolvedUrl.endpoints.snapshotStorageUrl = `${newSiteDomain}${new URL(odspResolvedUrl.endpoints.snapshotStorageUrl).pathname}`;
213
+ }
214
+ }
189
215
  //# sourceMappingURL=getFileLink.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"getFileLink.js","sourceRoot":"","sources":["../src/getFileLink.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ITelemetryBaseProperties } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { NonRetryableError, runWithRetry } from \"@fluidframework/driver-utils/internal\";\nimport { hasRedirectionLocation } from \"@fluidframework/odsp-doclib-utils/internal\";\nimport {\n\tIOdspUrlParts,\n\tOdspErrorTypes,\n\tOdspResourceTokenFetchOptions,\n\tTokenFetcher,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tPerformanceEvent,\n\tisFluidError,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { getHeadersWithAuth } from \"./getUrlAndHeadersWithAuth.js\";\nimport {\n\tfetchHelper,\n\tgetWithRetryForTokenRefresh,\n\ttoInstrumentedOdspStorageTokenFetcher,\n} from \"./odspUtils.js\";\nimport { pkgVersion as driverVersion } from \"./packageVersion.js\";\nimport { runWithRetry as runWithRetryForCoherencyAndServiceReadOnlyErrors } from \"./retryUtils.js\";\n\n// Store cached responses for the lifetime of web session as file link remains the same for given file item\nconst fileLinkCache = new Map<string, Promise<string>>();\n\n/**\n * Returns file link for a file with given drive and item ids.\n * Scope needed: files.readwrite.all.\n * This function keeps retrying if it gets a retriable error or wait for some delay if it gets a\n * throttling error. In future, we are thinking of app allowing to pass some cancel token, with which\n * we would be able to stop retrying.\n * @param getToken - used to fetch access tokens needed to execute operation\n * @param odspUrlParts - object describing file storage identity\n * @param logger - used to log results of operation, including any error\n * @returns Promise which resolves to file link url when successful; otherwise, undefined.\n */\nexport async function getFileLink(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\todspUrlParts: IOdspUrlParts,\n\tlogger: ITelemetryLoggerExt,\n): Promise<string> {\n\tconst cacheKey = `${odspUrlParts.siteUrl}_${odspUrlParts.driveId}_${odspUrlParts.itemId}`;\n\tconst maybeFileLinkCacheEntry = fileLinkCache.get(cacheKey);\n\tif (maybeFileLinkCacheEntry !== undefined) {\n\t\treturn maybeFileLinkCacheEntry;\n\t}\n\n\tconst fileLinkGenerator = async function (): Promise<string> {\n\t\tlet fileLinkCore: string;\n\t\ttry {\n\t\t\tlet retryCount = 0;\n\t\t\tfileLinkCore = await runWithRetry(\n\t\t\t\tasync () =>\n\t\t\t\t\trunWithRetryForCoherencyAndServiceReadOnlyErrors(\n\t\t\t\t\t\tasync () =>\n\t\t\t\t\t\t\tgetFileLinkWithLocationRedirectionHandling(getToken, odspUrlParts, logger),\n\t\t\t\t\t\t\"getFileLinkCore\",\n\t\t\t\t\t\tlogger,\n\t\t\t\t\t),\n\t\t\t\t\"getShareLink\",\n\t\t\t\tlogger,\n\t\t\t\t{\n\t\t\t\t\t// TODO: use a stronger type\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\t\t\tonRetry(delayInMs: number, error: any) {\n\t\t\t\t\t\tretryCount++;\n\t\t\t\t\t\tif (retryCount === 5) {\n\t\t\t\t\t\t\tif (error !== undefined && typeof error === \"object\") {\n\t\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\t\t\t\t\t\terror.canRetry = false;\n\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// Delete from the cache to permit retrying later.\n\t\t\tfileLinkCache.delete(cacheKey);\n\t\t\tthrow error;\n\t\t}\n\n\t\t// We are guaranteed to run the getFileLinkCore at least once with successful result (which must be a string)\n\t\tassert(\n\t\t\tfileLinkCore !== undefined,\n\t\t\t0x292 /* \"Unexpected undefined result from getFileLinkCore\" */,\n\t\t);\n\t\treturn fileLinkCore;\n\t};\n\tconst fileLink = fileLinkGenerator();\n\tfileLinkCache.set(cacheKey, fileLink);\n\treturn fileLink;\n}\n\n/**\n * Handles location redirection while fulfilling the getFilelink call. We don't want browser to handle\n * the redirection as the browser will retry with same auth token which will not work as we need app\n * to regenerate tokens for the new site domain. So when we will make the network calls below we will set\n * the redirect:manual header to manually handle these redirects.\n * Refer: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308\n * @param getToken - token fetcher to fetch the token.\n * @param odspUrlParts - parts of odsp resolved url.\n * @param logger - logger to send events.\n * @returns Response from the API call.\n * @alpha\n */\nasync function getFileLinkWithLocationRedirectionHandling(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\todspUrlParts: IOdspUrlParts,\n\tlogger: ITelemetryLoggerExt,\n): Promise<string> {\n\t// We can have chains of location redirection one after the other, so have a for loop\n\t// so that we can keep handling the same type of error. Set max number of redirection to 5.\n\tlet lastError: unknown;\n\tfor (let count = 1; count <= 5; count++) {\n\t\ttry {\n\t\t\treturn await getFileLinkCore(getToken, odspUrlParts, logger);\n\t\t} catch (error: unknown) {\n\t\t\tlastError = error;\n\t\t\tif (\n\t\t\t\tisFluidError(error) &&\n\t\t\t\terror.errorType === OdspErrorTypes.fileNotFoundOrAccessDeniedError &&\n\t\t\t\thasRedirectionLocation(error) &&\n\t\t\t\terror.redirectLocation !== undefined\n\t\t\t) {\n\t\t\t\tconst redirectLocation = error.redirectLocation;\n\t\t\t\tlogger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"LocationRedirectionErrorForGetOdspFileLink\",\n\t\t\t\t\tretryCount: count,\n\t\t\t\t});\n\t\t\t\t// Generate the new SiteUrl from the redirection location.\n\t\t\t\tconst newSiteDomain = new URL(redirectLocation).origin;\n\t\t\t\tconst newSiteUrl = `${newSiteDomain}${new URL(odspUrlParts.siteUrl).pathname}`;\n\t\t\t\todspUrlParts.siteUrl = newSiteUrl;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\tthrow lastError;\n}\n\nasync function getFileLinkCore(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\todspUrlParts: IOdspUrlParts,\n\tlogger: ITelemetryLoggerExt,\n): Promise<string> {\n\tconst fileItem = await getFileItemLite(getToken, odspUrlParts, logger, true);\n\n\t// ODSP link requires extra call to return link that is resistant to file being renamed or moved to different folder\n\treturn PerformanceEvent.timedExecAsync(\n\t\tlogger,\n\t\t{ eventName: \"odspFileLink\", requestName: \"getSharingInformation\" },\n\t\tasync (event) => {\n\t\t\tlet attempts = 0;\n\t\t\tlet additionalProps;\n\t\t\tconst fileLink = await getWithRetryForTokenRefresh(async (options) => {\n\t\t\t\tattempts++;\n\t\t\t\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\t\t\t\tlogger,\n\t\t\t\t\todspUrlParts,\n\t\t\t\t\tgetToken,\n\t\t\t\t);\n\n\t\t\t\t// IMPORTANT: In past we were using GetFileByUrl() API to get to the list item that was corresponding\n\t\t\t\t// to the file. This was intentionally replaced with GetFileById() to solve the following issue:\n\t\t\t\t// GetFileByUrl() uses webDavUrl to locate list item. This API does not work for Consumer scenarios\n\t\t\t\t// where webDavUrl is constructed using legacy ODC format for backward compatibility reasons.\n\t\t\t\t// GetFileByUrl() does not understand that format and thus fails. GetFileById() relies on file item\n\t\t\t\t// unique guid (sharepointIds.listItemUniqueId) and it works uniformly across Consumer and Commercial.\n\t\t\t\tconst url = `${\n\t\t\t\t\todspUrlParts.siteUrl\n\t\t\t\t}/_api/web/GetFileById(@a1)/ListItemAllFields/GetSharingInformation?@a1=guid${encodeURIComponent(\n\t\t\t\t\t`'${fileItem.sharepointIds.listItemUniqueId}'`,\n\t\t\t\t)}`;\n\t\t\t\tconst method = \"POST\";\n\t\t\t\tconst authHeader = await getAuthHeader(\n\t\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\t\t\"GetFileLinkCore\",\n\t\t\t\t);\n\t\t\t\tconst headers = getHeadersWithAuth(authHeader);\n\t\t\t\tconst requestInit = {\n\t\t\t\t\tmethod,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json;odata=verbose\",\n\t\t\t\t\t\t\"Accept\": \"application/json;odata=verbose\",\n\t\t\t\t\t\t\"redirect\": \"manual\",\n\t\t\t\t\t\t...headers,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\tconst response = await fetchHelper(url, requestInit);\n\t\t\t\tadditionalProps = response.propsToLog;\n\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\t\t\t\tconst sharingInfo = await response.content.json();\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access\n\t\t\t\tconst directUrl = sharingInfo?.d?.directUrl;\n\t\t\t\tif (typeof directUrl !== \"string\") {\n\t\t\t\t\t// This will retry once in getWithRetryForTokenRefresh\n\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t\"Malformed GetSharingInformation response\",\n\t\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t\t{ driverVersion },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn directUrl;\n\t\t\t});\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n\t\t\tevent.end({ ...additionalProps, attempts });\n\t\t\treturn fileLink;\n\t\t},\n\t);\n}\n\n/**\n * Sharepoint Ids Interface\n */\ninterface IGraphSharepointIds {\n\tlistId: string;\n\tlistItemId: string;\n\tlistItemUniqueId: string;\n\tsiteId: string;\n\tsiteUrl: string;\n\twebId: string;\n}\n\n/**\n * This represents a lite version of file item containing only select file properties\n */\ninterface FileItemLite {\n\twebUrl: string;\n\twebDavUrl: string;\n\tsharepointIds: IGraphSharepointIds;\n}\n\nconst isFileItemLite = (maybeFileItemLite: unknown): maybeFileItemLite is FileItemLite =>\n\ttypeof (maybeFileItemLite as Partial<FileItemLite>).webUrl === \"string\" &&\n\ttypeof (maybeFileItemLite as Partial<FileItemLite>).webDavUrl === \"string\" &&\n\t// TODO: stronger check\n\ttypeof (maybeFileItemLite as Partial<FileItemLite>).sharepointIds === \"object\";\n\nasync function getFileItemLite(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\todspUrlParts: IOdspUrlParts,\n\tlogger: ITelemetryLoggerExt,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n): Promise<FileItemLite> {\n\treturn PerformanceEvent.timedExecAsync(\n\t\tlogger,\n\t\t{ eventName: \"odspFileLink\", requestName: \"getFileItemLite\" },\n\t\tasync (event) => {\n\t\t\tlet attempts = 0;\n\t\t\tlet additionalProps: ITelemetryBaseProperties | undefined;\n\t\t\tconst fileItem = await getWithRetryForTokenRefresh(async (options) => {\n\t\t\t\tattempts++;\n\t\t\t\tconst { siteUrl, driveId, itemId } = odspUrlParts;\n\t\t\t\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\t\t\t\tlogger,\n\t\t\t\t\todspUrlParts,\n\t\t\t\t\tgetToken,\n\t\t\t\t);\n\t\t\t\tconst url = `${siteUrl}/_api/v2.0/drives/${driveId}/items/${itemId}?select=webUrl,webDavUrl,sharepointIds`;\n\t\t\t\tconst method = \"GET\";\n\t\t\t\tconst authHeader = await getAuthHeader(\n\t\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\t\t\"GetFileItemLite\",\n\t\t\t\t);\n\t\t\t\tassert(\n\t\t\t\t\tauthHeader !== null,\n\t\t\t\t\t0x2bc /* \"Instrumented token fetcher with throwOnNullToken =true should never return null\" */,\n\t\t\t\t);\n\n\t\t\t\tconst headers = getHeadersWithAuth(authHeader);\n\t\t\t\theaders.redirect = \"manual\";\n\t\t\t\tconst requestInit = { method, headers };\n\t\t\t\tconst response = await fetchHelper(url, requestInit);\n\t\t\t\tadditionalProps = response.propsToLog;\n\n\t\t\t\tconst responseJson: unknown = await response.content.json();\n\t\t\t\tif (!isFileItemLite(responseJson)) {\n\t\t\t\t\t// This will retry once in getWithRetryForTokenRefresh\n\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t\"Malformed getFileItemLite response\",\n\t\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t\t{ driverVersion },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn responseJson;\n\t\t\t});\n\t\t\tevent.end({ ...additionalProps, attempts });\n\t\t\treturn fileItem;\n\t\t},\n\t);\n}\n"]}
1
+ {"version":3,"file":"getFileLink.js","sourceRoot":"","sources":["../src/getFileLink.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ITelemetryBaseProperties } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { NonRetryableError, runWithRetry } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tIOdspUrlParts,\n\tOdspErrorTypes,\n\tOdspResourceTokenFetchOptions,\n\tTokenFetcher,\n\ttype IOdspResolvedUrl,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tPerformanceEvent,\n\tisFluidError,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { getHeadersWithAuth } from \"./getUrlAndHeadersWithAuth.js\";\nimport {\n\tfetchHelper,\n\tgetWithRetryForTokenRefresh,\n\ttoInstrumentedOdspStorageTokenFetcher,\n} from \"./odspUtils.js\";\nimport { pkgVersion as driverVersion } from \"./packageVersion.js\";\nimport { runWithRetry as runWithRetryForCoherencyAndServiceReadOnlyErrors } from \"./retryUtils.js\";\n\n// Store cached responses for the lifetime of web session as file link remains the same for given file item\nconst fileLinkCache = new Map<string, Promise<string>>();\n\n/**\n * Returns file link for a file with given drive and item ids.\n * Scope needed: files.readwrite.all.\n * This function keeps retrying if it gets a retriable error or wait for some delay if it gets a\n * throttling error. In future, we are thinking of app allowing to pass some cancel token, with which\n * we would be able to stop retrying.\n * @param getToken - used to fetch access tokens needed to execute operation\n * @param odspUrlParts - object describing file storage identity\n * @param logger - used to log results of operation, including any error\n * @returns Promise which resolves to file link url when successful; otherwise, undefined.\n */\nexport async function getFileLink(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\tresolvedUrl: IOdspResolvedUrl,\n\tlogger: ITelemetryLoggerExt,\n): Promise<string> {\n\tconst cacheKey = `${resolvedUrl.siteUrl}_${resolvedUrl.driveId}_${resolvedUrl.itemId}`;\n\tconst maybeFileLinkCacheEntry = fileLinkCache.get(cacheKey);\n\tif (maybeFileLinkCacheEntry !== undefined) {\n\t\treturn maybeFileLinkCacheEntry;\n\t}\n\n\tconst fileLinkGenerator = async function (): Promise<string> {\n\t\tlet fileLinkCore: string;\n\t\ttry {\n\t\t\tlet retryCount = 0;\n\t\t\tfileLinkCore = await runWithRetry(\n\t\t\t\tasync () =>\n\t\t\t\t\trunWithRetryForCoherencyAndServiceReadOnlyErrors(\n\t\t\t\t\t\tasync () =>\n\t\t\t\t\t\t\tgetFileLinkWithLocationRedirectionHandling(getToken, resolvedUrl, logger),\n\t\t\t\t\t\t\"getFileLinkCore\",\n\t\t\t\t\t\tlogger,\n\t\t\t\t\t),\n\t\t\t\t\"getShareLink\",\n\t\t\t\tlogger,\n\t\t\t\t{\n\t\t\t\t\t// TODO: use a stronger type\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\t\t\tonRetry(delayInMs: number, error: any) {\n\t\t\t\t\t\tretryCount++;\n\t\t\t\t\t\tif (retryCount === 5) {\n\t\t\t\t\t\t\tif (error !== undefined && typeof error === \"object\") {\n\t\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\t\t\t\t\t\terror.canRetry = false;\n\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// Delete from the cache to permit retrying later.\n\t\t\tfileLinkCache.delete(cacheKey);\n\t\t\tthrow error;\n\t\t}\n\n\t\t// We are guaranteed to run the getFileLinkCore at least once with successful result (which must be a string)\n\t\tassert(\n\t\t\tfileLinkCore !== undefined,\n\t\t\t0x292 /* \"Unexpected undefined result from getFileLinkCore\" */,\n\t\t);\n\t\treturn fileLinkCore;\n\t};\n\tconst fileLink = fileLinkGenerator();\n\tfileLinkCache.set(cacheKey, fileLink);\n\treturn fileLink;\n}\n\n/**\n * Handles location redirection while fulfilling the getFilelink call. We don't want browser to handle\n * the redirection as the browser will retry with same auth token which will not work as we need app\n * to regenerate tokens for the new site domain. So when we will make the network calls below we will set\n * the redirect:manual header to manually handle these redirects.\n * Refer: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308\n * @param getToken - token fetcher to fetch the token.\n * @param odspUrlParts - parts of odsp resolved url.\n * @param logger - logger to send events.\n * @returns Response from the API call.\n * @alpha\n */\nasync function getFileLinkWithLocationRedirectionHandling(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\tresolvedUrl: IOdspResolvedUrl,\n\tlogger: ITelemetryLoggerExt,\n): Promise<string> {\n\t// We can have chains of location redirection one after the other, so have a for loop\n\t// so that we can keep handling the same type of error. Set max number of redirection to 5.\n\tlet lastError: unknown;\n\tlet locationRedirected = false;\n\tfor (let count = 1; count <= 5; count++) {\n\t\ttry {\n\t\t\tconst fileItem = await getFileItemLite(getToken, resolvedUrl, logger, true);\n\t\t\t// Sometimes the siteUrl in the actual file is different from the siteUrl in the resolvedUrl due to location\n\t\t\t// redirection. This creates issues in the getSharingInformation call. So we need to update the siteUrl in the\n\t\t\t// resolvedUrl to the siteUrl in the fileItem which is the updated siteUrl.\n\t\t\tconst oldSiteDomain = new URL(resolvedUrl.siteUrl).origin;\n\t\t\tconst newSiteDomain = new URL(fileItem.sharepointIds.siteUrl).origin;\n\t\t\tif (oldSiteDomain !== newSiteDomain) {\n\t\t\t\tlocationRedirected = true;\n\t\t\t\tlogger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"LocationRedirectionErrorForGetOdspFileLink\",\n\t\t\t\t\tretryCount: count,\n\t\t\t\t});\n\t\t\t\trenameTenantInOdspResolvedUrl(resolvedUrl, newSiteDomain);\n\t\t\t}\n\t\t\treturn await getFileLinkCore(getToken, resolvedUrl, logger, fileItem);\n\t\t} catch (error: unknown) {\n\t\t\tlastError = error;\n\t\t\t// If the getSharingLink call fails with the 401/403/404 error, then it could be due to that the file has moved\n\t\t\t// to another location. This could occur in case we have more than 1 tenant rename. In that case, we should retry\n\t\t\t// the getFileItemLite call to get the updated fileItem.\n\t\t\tif (\n\t\t\t\tisFluidError(error) &&\n\t\t\t\tlocationRedirected &&\n\t\t\t\t(error.errorType === OdspErrorTypes.fileNotFoundOrAccessDeniedError ||\n\t\t\t\t\terror.errorType === OdspErrorTypes.authorizationError)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\tthrow lastError;\n}\n\nasync function getFileLinkCore(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\todspUrlParts: IOdspUrlParts,\n\tlogger: ITelemetryLoggerExt,\n\tfileItem: FileItemLite,\n): Promise<string> {\n\t// ODSP link requires extra call to return link that is resistant to file being renamed or moved to different folder\n\treturn PerformanceEvent.timedExecAsync(\n\t\tlogger,\n\t\t{ eventName: \"odspFileLink\", requestName: \"getSharingInformation\" },\n\t\tasync (event) => {\n\t\t\tlet attempts = 0;\n\t\t\tlet additionalProps;\n\t\t\tconst fileLink = await getWithRetryForTokenRefresh(async (options) => {\n\t\t\t\tattempts++;\n\t\t\t\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\t\t\t\tlogger,\n\t\t\t\t\todspUrlParts,\n\t\t\t\t\tgetToken,\n\t\t\t\t);\n\n\t\t\t\t// IMPORTANT: In past we were using GetFileByUrl() API to get to the list item that was corresponding\n\t\t\t\t// to the file. This was intentionally replaced with GetFileById() to solve the following issue:\n\t\t\t\t// GetFileByUrl() uses webDavUrl to locate list item. This API does not work for Consumer scenarios\n\t\t\t\t// where webDavUrl is constructed using legacy ODC format for backward compatibility reasons.\n\t\t\t\t// GetFileByUrl() does not understand that format and thus fails. GetFileById() relies on file item\n\t\t\t\t// unique guid (sharepointIds.listItemUniqueId) and it works uniformly across Consumer and Commercial.\n\t\t\t\tconst url = `${\n\t\t\t\t\todspUrlParts.siteUrl\n\t\t\t\t}/_api/web/GetFileById(@a1)/ListItemAllFields/GetSharingInformation?@a1=guid${encodeURIComponent(\n\t\t\t\t\t`'${fileItem.sharepointIds.listItemUniqueId}'`,\n\t\t\t\t)}`;\n\t\t\t\tconst method = \"POST\";\n\t\t\t\tconst authHeader = await getAuthHeader(\n\t\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\t\t\"GetFileLinkCore\",\n\t\t\t\t);\n\t\t\t\tconst headers = getHeadersWithAuth(authHeader);\n\t\t\t\tconst requestInit = {\n\t\t\t\t\tmethod,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json;odata=verbose\",\n\t\t\t\t\t\t\"Accept\": \"application/json;odata=verbose\",\n\t\t\t\t\t\t...headers,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\tconst response = await fetchHelper(url, requestInit);\n\t\t\t\tadditionalProps = response.propsToLog;\n\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\t\t\t\tconst sharingInfo = await response.content.json();\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access\n\t\t\t\tconst directUrl = sharingInfo?.d?.directUrl;\n\t\t\t\tif (typeof directUrl !== \"string\") {\n\t\t\t\t\t// This will retry once in getWithRetryForTokenRefresh\n\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t\"Malformed GetSharingInformation response\",\n\t\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t\t{ driverVersion },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn directUrl;\n\t\t\t});\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n\t\t\tevent.end({ ...additionalProps, attempts });\n\t\t\treturn fileLink;\n\t\t},\n\t);\n}\n\n/**\n * Sharepoint Ids Interface\n */\ninterface IGraphSharepointIds {\n\tlistId: string;\n\tlistItemId: string;\n\tlistItemUniqueId: string;\n\tsiteId: string;\n\tsiteUrl: string;\n\twebId: string;\n}\n\n/**\n * This represents a lite version of file item containing only select file properties\n */\ninterface FileItemLite {\n\twebUrl: string;\n\twebDavUrl: string;\n\tsharepointIds: IGraphSharepointIds;\n}\n\nconst isFileItemLite = (maybeFileItemLite: unknown): maybeFileItemLite is FileItemLite =>\n\ttypeof (maybeFileItemLite as Partial<FileItemLite>).webUrl === \"string\" &&\n\ttypeof (maybeFileItemLite as Partial<FileItemLite>).webDavUrl === \"string\" &&\n\t// TODO: stronger check\n\ttypeof (maybeFileItemLite as Partial<FileItemLite>).sharepointIds === \"object\";\n\nasync function getFileItemLite(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\todspUrlParts: IOdspUrlParts,\n\tlogger: ITelemetryLoggerExt,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n): Promise<FileItemLite> {\n\treturn PerformanceEvent.timedExecAsync(\n\t\tlogger,\n\t\t{ eventName: \"odspFileLink\", requestName: \"getFileItemLite\" },\n\t\tasync (event) => {\n\t\t\tlet attempts = 0;\n\t\t\tlet additionalProps: ITelemetryBaseProperties | undefined;\n\t\t\tconst fileItem = await getWithRetryForTokenRefresh(async (options) => {\n\t\t\t\tattempts++;\n\t\t\t\tconst { siteUrl, driveId, itemId } = odspUrlParts;\n\t\t\t\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\t\t\t\tlogger,\n\t\t\t\t\todspUrlParts,\n\t\t\t\t\tgetToken,\n\t\t\t\t);\n\t\t\t\tconst url = `${siteUrl}/_api/v2.0/drives/${driveId}/items/${itemId}?select=webUrl,webDavUrl,sharepointIds`;\n\t\t\t\tconst method = \"GET\";\n\t\t\t\tconst authHeader = await getAuthHeader(\n\t\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\t\t\"GetFileItemLite\",\n\t\t\t\t);\n\t\t\t\tassert(\n\t\t\t\t\tauthHeader !== null,\n\t\t\t\t\t0x2bc /* \"Instrumented token fetcher with throwOnNullToken =true should never return null\" */,\n\t\t\t\t);\n\n\t\t\t\tconst headers = getHeadersWithAuth(authHeader);\n\t\t\t\tconst requestInit = { method, headers };\n\t\t\t\tconst response = await fetchHelper(url, requestInit);\n\t\t\t\tadditionalProps = response.propsToLog;\n\n\t\t\t\tconst responseJson: unknown = await response.content.json();\n\t\t\t\tif (!isFileItemLite(responseJson)) {\n\t\t\t\t\t// This will retry once in getWithRetryForTokenRefresh\n\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t\"Malformed getFileItemLite response\",\n\t\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t\t{ driverVersion },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn responseJson;\n\t\t\t});\n\t\t\tevent.end({ ...additionalProps, attempts });\n\t\t\treturn fileItem;\n\t\t},\n\t);\n}\n\n/**\n * It takes a resolved url with old siteUrl and patches resolved url with updated site url domain.\n * @param odspResolvedUrl - Previous odsp resolved url with older site url.\n * @param newSiteDomain - New site domain after the tenant rename.\n */\nfunction renameTenantInOdspResolvedUrl(\n\todspResolvedUrl: IOdspResolvedUrl,\n\tnewSiteDomain: string,\n): void {\n\tconst newSiteUrl = `${newSiteDomain}${new URL(odspResolvedUrl.siteUrl).pathname}`;\n\todspResolvedUrl.siteUrl = newSiteUrl;\n\n\tif (odspResolvedUrl.endpoints.attachmentGETStorageUrl) {\n\t\todspResolvedUrl.endpoints.attachmentGETStorageUrl = `${newSiteDomain}${\n\t\t\tnew URL(odspResolvedUrl.endpoints.attachmentGETStorageUrl).pathname\n\t\t}`;\n\t}\n\tif (odspResolvedUrl.endpoints.attachmentPOSTStorageUrl) {\n\t\todspResolvedUrl.endpoints.attachmentPOSTStorageUrl = `${newSiteDomain}${\n\t\t\tnew URL(odspResolvedUrl.endpoints.attachmentPOSTStorageUrl).pathname\n\t\t}`;\n\t}\n\tif (odspResolvedUrl.endpoints.deltaStorageUrl) {\n\t\todspResolvedUrl.endpoints.deltaStorageUrl = `${newSiteDomain}${\n\t\t\tnew URL(odspResolvedUrl.endpoints.deltaStorageUrl).pathname\n\t\t}`;\n\t}\n\tif (odspResolvedUrl.endpoints.snapshotStorageUrl) {\n\t\todspResolvedUrl.endpoints.snapshotStorageUrl = `${newSiteDomain}${\n\t\t\tnew URL(odspResolvedUrl.endpoints.snapshotStorageUrl).pathname\n\t\t}`;\n\t}\n}\n"]}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/odsp-driver";
8
- export declare const pkgVersion = "2.0.7";
8
+ export declare const pkgVersion = "2.0.9";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -8,5 +8,5 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.pkgVersion = exports.pkgName = void 0;
10
10
  exports.pkgName = "@fluidframework/odsp-driver";
11
- exports.pkgVersion = "2.0.7";
11
+ exports.pkgVersion = "2.0.9";
12
12
  //# sourceMappingURL=packageVersion.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,6BAA6B,CAAC;AACxC,QAAA,UAAU,GAAG,OAAO,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 = \"2.0.7\";\n"]}
1
+ {"version":3,"file":"packageVersion.js","sourceRoot":"","sources":["../src/packageVersion.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;;AAEU,QAAA,OAAO,GAAG,6BAA6B,CAAC;AACxC,QAAA,UAAU,GAAG,OAAO,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 = \"2.0.9\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"createFile.d.ts","sourceRoot":"","sources":["../src/createFile.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAGlE,OAAO,EACN,UAAU,EACV,gBAAgB,EAChB,+BAA+B,EAG/B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EACN,mBAAmB,EAGnB,MAAM,0CAA0C,CAAC;AAElD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAQrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIjD,OAAO,EACN,YAAY,EAKZ,MAAM,gBAAgB,CAAC;AASxB;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,aAAa,EAAE,+BAA+B,EAC9C,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,mBAAmB,EAC3B,gBAAgB,EAAE,YAAY,GAAG,SAAS,EAC1C,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAE,UAAU,EACrB,gBAAgB,EAAE,OAAO,EACzB,sCAAsC,EAAE,OAAO,EAC/C,iBAAiB,CAAC,EAAE,OAAO,EAC3B,yCAAyC,CAAC,EAAE,OAAO,GACjD,OAAO,CAAC,gBAAgB,CAAC,CA2D3B;AA2CD,wBAAsB,uBAAuB,CAC5C,aAAa,EAAE,+BAA+B,EAC9C,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,mBAAmB,EAC3B,YAAY,EAAE,YAAY,GACxB,OAAO,CAAC,MAAM,CAAC,CAuDjB;AAED,wBAAsB,6BAA6B,CAClD,aAAa,EAAE,+BAA+B,EAC9C,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,mBAAmB,EAC3B,gBAAgB,EAAE,YAAY,EAC9B,YAAY,EAAE,YAAY,EAC1B,sCAAsC,EAAE,OAAO,GAC7C,OAAO,CAAC,mBAAmB,CAAC,CAmC9B"}
1
+ {"version":3,"file":"createFile.d.ts","sourceRoot":"","sources":["../src/createFile.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAGlE,OAAO,EACN,UAAU,EACV,gBAAgB,EAChB,+BAA+B,EAG/B,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EACN,mBAAmB,EAGnB,MAAM,0CAA0C,CAAC;AAElD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAQrD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAIjD,OAAO,EACN,YAAY,EAKZ,MAAM,gBAAgB,CAAC;AASxB;;;GAGG;AACH,wBAAsB,kBAAkB,CACvC,aAAa,EAAE,+BAA+B,EAC9C,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,mBAAmB,EAC3B,gBAAgB,EAAE,YAAY,GAAG,SAAS,EAC1C,YAAY,EAAE,YAAY,EAC1B,SAAS,EAAE,UAAU,EACrB,gBAAgB,EAAE,OAAO,EACzB,sCAAsC,EAAE,OAAO,EAC/C,iBAAiB,CAAC,EAAE,OAAO,EAC3B,yCAAyC,CAAC,EAAE,OAAO,GACjD,OAAO,CAAC,gBAAgB,CAAC,CA2D3B;AAoDD,wBAAsB,uBAAuB,CAC5C,aAAa,EAAE,+BAA+B,EAC9C,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,mBAAmB,EAC3B,YAAY,EAAE,YAAY,GACxB,OAAO,CAAC,MAAM,CAAC,CAuDjB;AAED,wBAAsB,6BAA6B,CAClD,aAAa,EAAE,+BAA+B,EAC9C,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,mBAAmB,EAC3B,gBAAgB,EAAE,YAAY,EAC9B,YAAY,EAAE,YAAY,EAC1B,sCAAsC,EAAE,OAAO,GAC7C,OAAO,CAAC,mBAAmB,CAAC,CAmC9B"}
package/lib/createFile.js CHANGED
@@ -97,8 +97,16 @@ function extractShareLinkData(response, enableSingleRequestForShareLinkWithCreat
97
97
  }
98
98
  return shareLinkInfo;
99
99
  }
100
+ /**
101
+ * Encodes file path so it can be embedded in the request url
102
+ * @param path - path to encode
103
+ * @returns encoded path or "" if path is undefined
104
+ */
105
+ function encodeFilePath(path) {
106
+ return path ? encodeURIComponent(path.startsWith("/") ? path : `/${path}`) : "";
107
+ }
100
108
  export async function createNewEmptyFluidFile(getAuthHeader, newFileInfo, logger, epochTracker) {
101
- const filePath = newFileInfo.filePath ? encodeURIComponent(`/${newFileInfo.filePath}`) : "";
109
+ const filePath = encodeFilePath(newFileInfo.filePath);
102
110
  // add .tmp extension to empty file (host is expected to rename)
103
111
  const encodedFilename = encodeURIComponent(`${newFileInfo.filename}.tmp`);
104
112
  const initialUrl = `${getApiRoot(new URL(newFileInfo.siteUrl))}/drives/${newFileInfo.driveId}/items/root:/${filePath}/${encodedFilename}:/content?@name.conflictBehavior=rename&select=id,name,parentReference`;
@@ -128,7 +136,7 @@ export async function createNewEmptyFluidFile(getAuthHeader, newFileInfo, logger
128
136
  });
129
137
  }
130
138
  export async function createNewFluidFileFromSummary(getAuthHeader, newFileInfo, logger, createNewSummary, epochTracker, forceAccessTokenViaAuthorizationHeader) {
131
- const filePath = newFileInfo.filePath ? encodeURIComponent(`/${newFileInfo.filePath}`) : "";
139
+ const filePath = encodeFilePath(newFileInfo.filePath);
132
140
  const encodedFilename = encodeURIComponent(newFileInfo.filename);
133
141
  const baseUrl = `${getApiRoot(new URL(newFileInfo.siteUrl))}/drives/${newFileInfo.driveId}/items/root:` +
134
142
  `${filePath}/${encodedFilename}`;
@@ -1 +1 @@
1
- {"version":3,"file":"createFile.js","sourceRoot":"","sources":["../src/createFile.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { ISummaryTree } from \"@fluidframework/driver-definitions\";\nimport { ISnapshot } from \"@fluidframework/driver-definitions/internal\";\nimport { NonRetryableError } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tIFileEntry,\n\tIOdspResolvedUrl,\n\tInstrumentedStorageTokenFetcher,\n\tOdspErrorTypes,\n\tShareLinkInfoType,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tloggerToMonitoringContext,\n\tPerformanceEvent,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { ICreateFileResponse } from \"./contracts.js\";\nimport { ClpCompliantAppHeader } from \"./contractsPublic.js\";\nimport {\n\tconvertCreateNewSummaryTreeToTreeAndBlobs,\n\tconvertSummaryIntoContainerSnapshot,\n\tcreateNewFluidContainerCore,\n} from \"./createNewUtils.js\";\nimport { createOdspUrl } from \"./createOdspUrl.js\";\nimport { EpochTracker } from \"./epochTracker.js\";\nimport { getHeadersWithAuth } from \"./getUrlAndHeadersWithAuth.js\";\nimport { OdspDriverUrlResolver } from \"./odspDriverUrlResolver.js\";\nimport { getApiRoot } from \"./odspUrlHelper.js\";\nimport {\n\tINewFileInfo,\n\tbuildOdspShareLinkReqParams,\n\tcreateCacheSnapshotKey,\n\tgetWithRetryForTokenRefresh,\n\tsnapshotWithLoadingGroupIdSupported,\n} from \"./odspUtils.js\";\nimport { pkgVersion as driverVersion } from \"./packageVersion.js\";\nimport { runWithRetry } from \"./retryUtils.js\";\n\nconst isInvalidFileName = (fileName: string): boolean => {\n\tconst invalidCharsRegex = /[\"*/:<>?\\\\|]+/g;\n\treturn invalidCharsRegex.test(fileName);\n};\n\n/**\n * Creates a new Fluid file.\n * Returns resolved url\n */\nexport async function createNewFluidFile(\n\tgetAuthHeader: InstrumentedStorageTokenFetcher,\n\tnewFileInfo: INewFileInfo,\n\tlogger: ITelemetryLoggerExt,\n\tcreateNewSummary: ISummaryTree | undefined,\n\tepochTracker: EpochTracker,\n\tfileEntry: IFileEntry,\n\tcreateNewCaching: boolean,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n\tisClpCompliantApp?: boolean,\n\tenableSingleRequestForShareLinkWithCreate?: boolean,\n): Promise<IOdspResolvedUrl> {\n\t// Check for valid filename before the request to create file is actually made.\n\tif (isInvalidFileName(newFileInfo.filename)) {\n\t\tthrow new NonRetryableError(\n\t\t\t// pre-0.58 error message: Invalid filename\n\t\t\t\"Invalid filename for createNew\",\n\t\t\tOdspErrorTypes.invalidFileNameError,\n\t\t\t{ driverVersion },\n\t\t);\n\t}\n\n\tlet itemId: string;\n\tlet summaryHandle: string = \"\";\n\tlet shareLinkInfo: ShareLinkInfoType | undefined;\n\tif (createNewSummary === undefined) {\n\t\titemId = await createNewEmptyFluidFile(getAuthHeader, newFileInfo, logger, epochTracker);\n\t} else {\n\t\tconst content = await createNewFluidFileFromSummary(\n\t\t\tgetAuthHeader,\n\t\t\tnewFileInfo,\n\t\t\tlogger,\n\t\t\tcreateNewSummary,\n\t\t\tepochTracker,\n\t\t\tforceAccessTokenViaAuthorizationHeader,\n\t\t);\n\t\titemId = content.itemId;\n\t\tsummaryHandle = content.id;\n\n\t\tshareLinkInfo = extractShareLinkData(content, enableSingleRequestForShareLinkWithCreate);\n\t}\n\n\tconst odspUrl = createOdspUrl({ ...newFileInfo, itemId, dataStorePath: \"/\" });\n\tconst resolver = new OdspDriverUrlResolver();\n\tconst odspResolvedUrl = await resolver.resolve({\n\t\turl: odspUrl,\n\t\theaders: { [ClpCompliantAppHeader.isClpCompliantApp]: isClpCompliantApp },\n\t});\n\tfileEntry.docId = odspResolvedUrl.hashedDocumentId;\n\tfileEntry.resolvedUrl = odspResolvedUrl;\n\n\todspResolvedUrl.shareLinkInfo = shareLinkInfo;\n\n\tif (createNewSummary !== undefined && createNewCaching) {\n\t\tassert(summaryHandle !== undefined, 0x203 /* \"Summary handle is undefined\" */);\n\t\t// converting summary and getting sequence number\n\t\tconst snapshot: ISnapshot = convertCreateNewSummaryTreeToTreeAndBlobs(\n\t\t\tcreateNewSummary,\n\t\t\tsummaryHandle,\n\t\t);\n\t\t// caching the converted summary\n\t\tawait epochTracker.put(\n\t\t\tcreateCacheSnapshotKey(\n\t\t\t\todspResolvedUrl,\n\t\t\t\tsnapshotWithLoadingGroupIdSupported(loggerToMonitoringContext(logger).config),\n\t\t\t),\n\t\t\tsnapshot,\n\t\t);\n\t}\n\treturn odspResolvedUrl;\n}\n\n/**\n * If user requested creation of a sharing link along with the creation of the file by providing\n * createLinkScope in the request parameters then extract and save the sharing link information from\n * the response if it is available.\n * In case there was an error in creation of the sharing link, error is provided back in the response,\n * and does not impact the creation of file in ODSP.\n * @param requestedSharingLinkKind - Kind of sharing link requested to be created along with the creation of file.\n * @param response - Response object received from the /snapshot api call\n * @returns Sharing link information received in the response from a successful creation of a file.\n */\nfunction extractShareLinkData(\n\tresponse: ICreateFileResponse,\n\tenableSingleRequestForShareLinkWithCreate?: boolean,\n): ShareLinkInfoType | undefined {\n\tlet shareLinkInfo: ShareLinkInfoType | undefined;\n\tif (enableSingleRequestForShareLinkWithCreate) {\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\t\tconst { sharing } = response;\n\t\tif (!sharing) {\n\t\t\treturn;\n\t\t}\n\t\t/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */\n\t\tshareLinkInfo = {\n\t\t\tcreateLink: {\n\t\t\t\tlink: sharing.sharingLink\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tscope: sharing.sharingLink.scope,\n\t\t\t\t\t\t\trole: sharing.sharingLink.type,\n\t\t\t\t\t\t\twebUrl: sharing.sharingLink.webUrl,\n\t\t\t\t\t\t\t...sharing.sharingLink,\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\t\terror: sharing.error,\n\t\t\t\tshareId: sharing.shareId,\n\t\t\t},\n\t\t};\n\t\t/* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */\n\t}\n\treturn shareLinkInfo;\n}\n\nexport async function createNewEmptyFluidFile(\n\tgetAuthHeader: InstrumentedStorageTokenFetcher,\n\tnewFileInfo: INewFileInfo,\n\tlogger: ITelemetryLoggerExt,\n\tepochTracker: EpochTracker,\n): Promise<string> {\n\tconst filePath = newFileInfo.filePath ? encodeURIComponent(`/${newFileInfo.filePath}`) : \"\";\n\t// add .tmp extension to empty file (host is expected to rename)\n\tconst encodedFilename = encodeURIComponent(`${newFileInfo.filename}.tmp`);\n\tconst initialUrl = `${getApiRoot(new URL(newFileInfo.siteUrl))}/drives/${\n\t\tnewFileInfo.driveId\n\t}/items/root:/${filePath}/${encodedFilename}:/content?@name.conflictBehavior=rename&select=id,name,parentReference`;\n\n\treturn getWithRetryForTokenRefresh(async (options) => {\n\t\tconst url = initialUrl;\n\t\tconst method = \"PUT\";\n\t\tconst authHeader = await getAuthHeader(\n\t\t\t{ ...options, request: { url, method } },\n\t\t\t\"CreateNewFile\",\n\t\t);\n\n\t\treturn PerformanceEvent.timedExecAsync(\n\t\t\tlogger,\n\t\t\t{ eventName: \"createNewEmptyFile\" },\n\t\t\tasync (event) => {\n\t\t\t\tconst headers = getHeadersWithAuth(authHeader);\n\t\t\t\theaders[\"Content-Type\"] = \"application/json\";\n\n\t\t\t\tconst fetchResponse = await runWithRetry(\n\t\t\t\t\tasync () =>\n\t\t\t\t\t\tepochTracker.fetchAndParseAsJSON<ICreateFileResponse>(\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbody: undefined,\n\t\t\t\t\t\t\t\theaders,\n\t\t\t\t\t\t\t\tmethod,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"createFile\",\n\t\t\t\t\t\t),\n\t\t\t\t\t\"createFile\",\n\t\t\t\t\tlogger,\n\t\t\t\t);\n\n\t\t\t\tconst content = fetchResponse.content;\n\t\t\t\tif (!content?.id) {\n\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t// pre-0.58 error message: ODSP CreateFile call returned no item ID\n\t\t\t\t\t\t\"ODSP CreateFile call returned no item ID (for empty file)\",\n\t\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t\t{ driverVersion },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tevent.end({\n\t\t\t\t\t...fetchResponse.propsToLog,\n\t\t\t\t});\n\t\t\t\treturn content.id;\n\t\t\t},\n\t\t\t{ end: true, cancel: \"error\" },\n\t\t);\n\t});\n}\n\nexport async function createNewFluidFileFromSummary(\n\tgetAuthHeader: InstrumentedStorageTokenFetcher,\n\tnewFileInfo: INewFileInfo,\n\tlogger: ITelemetryLoggerExt,\n\tcreateNewSummary: ISummaryTree,\n\tepochTracker: EpochTracker,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n): Promise<ICreateFileResponse> {\n\tconst filePath = newFileInfo.filePath ? encodeURIComponent(`/${newFileInfo.filePath}`) : \"\";\n\tconst encodedFilename = encodeURIComponent(newFileInfo.filename);\n\tconst baseUrl =\n\t\t`${getApiRoot(new URL(newFileInfo.siteUrl))}/drives/${newFileInfo.driveId}/items/root:` +\n\t\t`${filePath}/${encodedFilename}`;\n\n\tconst containerSnapshot = convertSummaryIntoContainerSnapshot(createNewSummary);\n\n\t// Build share link parameter based on the createLinkType provided so that the\n\t// snapshot api can create and return the share link along with creation of file in the response.\n\tconst createShareLinkParam = buildOdspShareLinkReqParams(newFileInfo.createLinkType);\n\tconst initialUrl = `${baseUrl}:/opStream/snapshots/snapshot${\n\t\tcreateShareLinkParam ? `?${createShareLinkParam}` : \"\"\n\t}`;\n\n\treturn createNewFluidContainerCore<ICreateFileResponse>({\n\t\tcontainerSnapshot,\n\t\tgetAuthHeader,\n\t\tlogger,\n\t\tinitialUrl,\n\t\tforceAccessTokenViaAuthorizationHeader,\n\t\tepochTracker,\n\t\ttelemetryName: \"CreateNewFile\",\n\t\tfetchType: \"createFile\",\n\t\tvalidateResponseCallback: (content) => {\n\t\t\tif (!content?.itemId) {\n\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\"ODSP CreateFile call returned no item ID\",\n\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t{ driverVersion },\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t});\n}\n"]}
1
+ {"version":3,"file":"createFile.js","sourceRoot":"","sources":["../src/createFile.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { ISummaryTree } from \"@fluidframework/driver-definitions\";\nimport { ISnapshot } from \"@fluidframework/driver-definitions/internal\";\nimport { NonRetryableError } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tIFileEntry,\n\tIOdspResolvedUrl,\n\tInstrumentedStorageTokenFetcher,\n\tOdspErrorTypes,\n\tShareLinkInfoType,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tloggerToMonitoringContext,\n\tPerformanceEvent,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { ICreateFileResponse } from \"./contracts.js\";\nimport { ClpCompliantAppHeader } from \"./contractsPublic.js\";\nimport {\n\tconvertCreateNewSummaryTreeToTreeAndBlobs,\n\tconvertSummaryIntoContainerSnapshot,\n\tcreateNewFluidContainerCore,\n} from \"./createNewUtils.js\";\nimport { createOdspUrl } from \"./createOdspUrl.js\";\nimport { EpochTracker } from \"./epochTracker.js\";\nimport { getHeadersWithAuth } from \"./getUrlAndHeadersWithAuth.js\";\nimport { OdspDriverUrlResolver } from \"./odspDriverUrlResolver.js\";\nimport { getApiRoot } from \"./odspUrlHelper.js\";\nimport {\n\tINewFileInfo,\n\tbuildOdspShareLinkReqParams,\n\tcreateCacheSnapshotKey,\n\tgetWithRetryForTokenRefresh,\n\tsnapshotWithLoadingGroupIdSupported,\n} from \"./odspUtils.js\";\nimport { pkgVersion as driverVersion } from \"./packageVersion.js\";\nimport { runWithRetry } from \"./retryUtils.js\";\n\nconst isInvalidFileName = (fileName: string): boolean => {\n\tconst invalidCharsRegex = /[\"*/:<>?\\\\|]+/g;\n\treturn invalidCharsRegex.test(fileName);\n};\n\n/**\n * Creates a new Fluid file.\n * Returns resolved url\n */\nexport async function createNewFluidFile(\n\tgetAuthHeader: InstrumentedStorageTokenFetcher,\n\tnewFileInfo: INewFileInfo,\n\tlogger: ITelemetryLoggerExt,\n\tcreateNewSummary: ISummaryTree | undefined,\n\tepochTracker: EpochTracker,\n\tfileEntry: IFileEntry,\n\tcreateNewCaching: boolean,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n\tisClpCompliantApp?: boolean,\n\tenableSingleRequestForShareLinkWithCreate?: boolean,\n): Promise<IOdspResolvedUrl> {\n\t// Check for valid filename before the request to create file is actually made.\n\tif (isInvalidFileName(newFileInfo.filename)) {\n\t\tthrow new NonRetryableError(\n\t\t\t// pre-0.58 error message: Invalid filename\n\t\t\t\"Invalid filename for createNew\",\n\t\t\tOdspErrorTypes.invalidFileNameError,\n\t\t\t{ driverVersion },\n\t\t);\n\t}\n\n\tlet itemId: string;\n\tlet summaryHandle: string = \"\";\n\tlet shareLinkInfo: ShareLinkInfoType | undefined;\n\tif (createNewSummary === undefined) {\n\t\titemId = await createNewEmptyFluidFile(getAuthHeader, newFileInfo, logger, epochTracker);\n\t} else {\n\t\tconst content = await createNewFluidFileFromSummary(\n\t\t\tgetAuthHeader,\n\t\t\tnewFileInfo,\n\t\t\tlogger,\n\t\t\tcreateNewSummary,\n\t\t\tepochTracker,\n\t\t\tforceAccessTokenViaAuthorizationHeader,\n\t\t);\n\t\titemId = content.itemId;\n\t\tsummaryHandle = content.id;\n\n\t\tshareLinkInfo = extractShareLinkData(content, enableSingleRequestForShareLinkWithCreate);\n\t}\n\n\tconst odspUrl = createOdspUrl({ ...newFileInfo, itemId, dataStorePath: \"/\" });\n\tconst resolver = new OdspDriverUrlResolver();\n\tconst odspResolvedUrl = await resolver.resolve({\n\t\turl: odspUrl,\n\t\theaders: { [ClpCompliantAppHeader.isClpCompliantApp]: isClpCompliantApp },\n\t});\n\tfileEntry.docId = odspResolvedUrl.hashedDocumentId;\n\tfileEntry.resolvedUrl = odspResolvedUrl;\n\n\todspResolvedUrl.shareLinkInfo = shareLinkInfo;\n\n\tif (createNewSummary !== undefined && createNewCaching) {\n\t\tassert(summaryHandle !== undefined, 0x203 /* \"Summary handle is undefined\" */);\n\t\t// converting summary and getting sequence number\n\t\tconst snapshot: ISnapshot = convertCreateNewSummaryTreeToTreeAndBlobs(\n\t\t\tcreateNewSummary,\n\t\t\tsummaryHandle,\n\t\t);\n\t\t// caching the converted summary\n\t\tawait epochTracker.put(\n\t\t\tcreateCacheSnapshotKey(\n\t\t\t\todspResolvedUrl,\n\t\t\t\tsnapshotWithLoadingGroupIdSupported(loggerToMonitoringContext(logger).config),\n\t\t\t),\n\t\t\tsnapshot,\n\t\t);\n\t}\n\treturn odspResolvedUrl;\n}\n\n/**\n * If user requested creation of a sharing link along with the creation of the file by providing\n * createLinkScope in the request parameters then extract and save the sharing link information from\n * the response if it is available.\n * In case there was an error in creation of the sharing link, error is provided back in the response,\n * and does not impact the creation of file in ODSP.\n * @param requestedSharingLinkKind - Kind of sharing link requested to be created along with the creation of file.\n * @param response - Response object received from the /snapshot api call\n * @returns Sharing link information received in the response from a successful creation of a file.\n */\nfunction extractShareLinkData(\n\tresponse: ICreateFileResponse,\n\tenableSingleRequestForShareLinkWithCreate?: boolean,\n): ShareLinkInfoType | undefined {\n\tlet shareLinkInfo: ShareLinkInfoType | undefined;\n\tif (enableSingleRequestForShareLinkWithCreate) {\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\t\tconst { sharing } = response;\n\t\tif (!sharing) {\n\t\t\treturn;\n\t\t}\n\t\t/* eslint-disable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */\n\t\tshareLinkInfo = {\n\t\t\tcreateLink: {\n\t\t\t\tlink: sharing.sharingLink\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tscope: sharing.sharingLink.scope,\n\t\t\t\t\t\t\trole: sharing.sharingLink.type,\n\t\t\t\t\t\t\twebUrl: sharing.sharingLink.webUrl,\n\t\t\t\t\t\t\t...sharing.sharingLink,\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\t\terror: sharing.error,\n\t\t\t\tshareId: sharing.shareId,\n\t\t\t},\n\t\t};\n\t\t/* eslint-enable @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */\n\t}\n\treturn shareLinkInfo;\n}\n\n/**\n * Encodes file path so it can be embedded in the request url\n * @param path - path to encode\n * @returns encoded path or \"\" if path is undefined\n */\nfunction encodeFilePath(path: string | undefined): string {\n\treturn path ? encodeURIComponent(path.startsWith(\"/\") ? path : `/${path}`) : \"\";\n}\n\nexport async function createNewEmptyFluidFile(\n\tgetAuthHeader: InstrumentedStorageTokenFetcher,\n\tnewFileInfo: INewFileInfo,\n\tlogger: ITelemetryLoggerExt,\n\tepochTracker: EpochTracker,\n): Promise<string> {\n\tconst filePath = encodeFilePath(newFileInfo.filePath);\n\t// add .tmp extension to empty file (host is expected to rename)\n\tconst encodedFilename = encodeURIComponent(`${newFileInfo.filename}.tmp`);\n\tconst initialUrl = `${getApiRoot(new URL(newFileInfo.siteUrl))}/drives/${\n\t\tnewFileInfo.driveId\n\t}/items/root:/${filePath}/${encodedFilename}:/content?@name.conflictBehavior=rename&select=id,name,parentReference`;\n\n\treturn getWithRetryForTokenRefresh(async (options) => {\n\t\tconst url = initialUrl;\n\t\tconst method = \"PUT\";\n\t\tconst authHeader = await getAuthHeader(\n\t\t\t{ ...options, request: { url, method } },\n\t\t\t\"CreateNewFile\",\n\t\t);\n\n\t\treturn PerformanceEvent.timedExecAsync(\n\t\t\tlogger,\n\t\t\t{ eventName: \"createNewEmptyFile\" },\n\t\t\tasync (event) => {\n\t\t\t\tconst headers = getHeadersWithAuth(authHeader);\n\t\t\t\theaders[\"Content-Type\"] = \"application/json\";\n\n\t\t\t\tconst fetchResponse = await runWithRetry(\n\t\t\t\t\tasync () =>\n\t\t\t\t\t\tepochTracker.fetchAndParseAsJSON<ICreateFileResponse>(\n\t\t\t\t\t\t\turl,\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tbody: undefined,\n\t\t\t\t\t\t\t\theaders,\n\t\t\t\t\t\t\t\tmethod,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\"createFile\",\n\t\t\t\t\t\t),\n\t\t\t\t\t\"createFile\",\n\t\t\t\t\tlogger,\n\t\t\t\t);\n\n\t\t\t\tconst content = fetchResponse.content;\n\t\t\t\tif (!content?.id) {\n\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t// pre-0.58 error message: ODSP CreateFile call returned no item ID\n\t\t\t\t\t\t\"ODSP CreateFile call returned no item ID (for empty file)\",\n\t\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t\t{ driverVersion },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tevent.end({\n\t\t\t\t\t...fetchResponse.propsToLog,\n\t\t\t\t});\n\t\t\t\treturn content.id;\n\t\t\t},\n\t\t\t{ end: true, cancel: \"error\" },\n\t\t);\n\t});\n}\n\nexport async function createNewFluidFileFromSummary(\n\tgetAuthHeader: InstrumentedStorageTokenFetcher,\n\tnewFileInfo: INewFileInfo,\n\tlogger: ITelemetryLoggerExt,\n\tcreateNewSummary: ISummaryTree,\n\tepochTracker: EpochTracker,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n): Promise<ICreateFileResponse> {\n\tconst filePath = encodeFilePath(newFileInfo.filePath);\n\tconst encodedFilename = encodeURIComponent(newFileInfo.filename);\n\tconst baseUrl =\n\t\t`${getApiRoot(new URL(newFileInfo.siteUrl))}/drives/${newFileInfo.driveId}/items/root:` +\n\t\t`${filePath}/${encodedFilename}`;\n\n\tconst containerSnapshot = convertSummaryIntoContainerSnapshot(createNewSummary);\n\n\t// Build share link parameter based on the createLinkType provided so that the\n\t// snapshot api can create and return the share link along with creation of file in the response.\n\tconst createShareLinkParam = buildOdspShareLinkReqParams(newFileInfo.createLinkType);\n\tconst initialUrl = `${baseUrl}:/opStream/snapshots/snapshot${\n\t\tcreateShareLinkParam ? `?${createShareLinkParam}` : \"\"\n\t}`;\n\n\treturn createNewFluidContainerCore<ICreateFileResponse>({\n\t\tcontainerSnapshot,\n\t\tgetAuthHeader,\n\t\tlogger,\n\t\tinitialUrl,\n\t\tforceAccessTokenViaAuthorizationHeader,\n\t\tepochTracker,\n\t\ttelemetryName: \"CreateNewFile\",\n\t\tfetchType: \"createFile\",\n\t\tvalidateResponseCallback: (content) => {\n\t\t\tif (!content?.itemId) {\n\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\"ODSP CreateFile call returned no item ID\",\n\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t{ driverVersion },\n\t\t\t\t);\n\t\t\t}\n\t\t},\n\t});\n}\n"]}
@@ -2,7 +2,7 @@
2
2
  * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
3
  * Licensed under the MIT License.
4
4
  */
5
- import { IOdspUrlParts, OdspResourceTokenFetchOptions, TokenFetcher } from "@fluidframework/odsp-driver-definitions/internal";
5
+ import { OdspResourceTokenFetchOptions, TokenFetcher, type IOdspResolvedUrl } from "@fluidframework/odsp-driver-definitions/internal";
6
6
  import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
7
7
  /**
8
8
  * Returns file link for a file with given drive and item ids.
@@ -15,5 +15,5 @@ import { ITelemetryLoggerExt } from "@fluidframework/telemetry-utils/internal";
15
15
  * @param logger - used to log results of operation, including any error
16
16
  * @returns Promise which resolves to file link url when successful; otherwise, undefined.
17
17
  */
18
- export declare function getFileLink(getToken: TokenFetcher<OdspResourceTokenFetchOptions>, odspUrlParts: IOdspUrlParts, logger: ITelemetryLoggerExt): Promise<string>;
18
+ export declare function getFileLink(getToken: TokenFetcher<OdspResourceTokenFetchOptions>, resolvedUrl: IOdspResolvedUrl, logger: ITelemetryLoggerExt): Promise<string>;
19
19
  //# sourceMappingURL=getFileLink.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getFileLink.d.ts","sourceRoot":"","sources":["../src/getFileLink.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EACN,aAAa,EAEb,6BAA6B,EAC7B,YAAY,EACZ,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EACN,mBAAmB,EAGnB,MAAM,0CAA0C,CAAC;AAclD;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAChC,QAAQ,EAAE,YAAY,CAAC,6BAA6B,CAAC,EACrD,YAAY,EAAE,aAAa,EAC3B,MAAM,EAAE,mBAAmB,GACzB,OAAO,CAAC,MAAM,CAAC,CAqDjB"}
1
+ {"version":3,"file":"getFileLink.d.ts","sourceRoot":"","sources":["../src/getFileLink.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAGN,6BAA6B,EAC7B,YAAY,EACZ,KAAK,gBAAgB,EACrB,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EACN,mBAAmB,EAGnB,MAAM,0CAA0C,CAAC;AAclD;;;;;;;;;;GAUG;AACH,wBAAsB,WAAW,CAChC,QAAQ,EAAE,YAAY,CAAC,6BAA6B,CAAC,EACrD,WAAW,EAAE,gBAAgB,EAC7B,MAAM,EAAE,mBAAmB,GACzB,OAAO,CAAC,MAAM,CAAC,CAqDjB"}
@@ -4,7 +4,6 @@
4
4
  */
5
5
  import { assert } from "@fluidframework/core-utils/internal";
6
6
  import { NonRetryableError, runWithRetry } from "@fluidframework/driver-utils/internal";
7
- import { hasRedirectionLocation } from "@fluidframework/odsp-doclib-utils/internal";
8
7
  import { OdspErrorTypes, } from "@fluidframework/odsp-driver-definitions/internal";
9
8
  import { PerformanceEvent, isFluidError, } from "@fluidframework/telemetry-utils/internal";
10
9
  import { getHeadersWithAuth } from "./getUrlAndHeadersWithAuth.js";
@@ -24,8 +23,8 @@ const fileLinkCache = new Map();
24
23
  * @param logger - used to log results of operation, including any error
25
24
  * @returns Promise which resolves to file link url when successful; otherwise, undefined.
26
25
  */
27
- export async function getFileLink(getToken, odspUrlParts, logger) {
28
- const cacheKey = `${odspUrlParts.siteUrl}_${odspUrlParts.driveId}_${odspUrlParts.itemId}`;
26
+ export async function getFileLink(getToken, resolvedUrl, logger) {
27
+ const cacheKey = `${resolvedUrl.siteUrl}_${resolvedUrl.driveId}_${resolvedUrl.itemId}`;
29
28
  const maybeFileLinkCacheEntry = fileLinkCache.get(cacheKey);
30
29
  if (maybeFileLinkCacheEntry !== undefined) {
31
30
  return maybeFileLinkCacheEntry;
@@ -34,7 +33,7 @@ export async function getFileLink(getToken, odspUrlParts, logger) {
34
33
  let fileLinkCore;
35
34
  try {
36
35
  let retryCount = 0;
37
- fileLinkCore = await runWithRetry(async () => runWithRetryForCoherencyAndServiceReadOnlyErrors(async () => getFileLinkWithLocationRedirectionHandling(getToken, odspUrlParts, logger), "getFileLinkCore", logger), "getShareLink", logger, {
36
+ fileLinkCore = await runWithRetry(async () => runWithRetryForCoherencyAndServiceReadOnlyErrors(async () => getFileLinkWithLocationRedirectionHandling(getToken, resolvedUrl, logger), "getFileLinkCore", logger), "getShareLink", logger, {
38
37
  // TODO: use a stronger type
39
38
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
40
39
  onRetry(delayInMs, error) {
@@ -75,29 +74,38 @@ export async function getFileLink(getToken, odspUrlParts, logger) {
75
74
  * @returns Response from the API call.
76
75
  * @alpha
77
76
  */
78
- async function getFileLinkWithLocationRedirectionHandling(getToken, odspUrlParts, logger) {
77
+ async function getFileLinkWithLocationRedirectionHandling(getToken, resolvedUrl, logger) {
79
78
  // We can have chains of location redirection one after the other, so have a for loop
80
79
  // so that we can keep handling the same type of error. Set max number of redirection to 5.
81
80
  let lastError;
81
+ let locationRedirected = false;
82
82
  for (let count = 1; count <= 5; count++) {
83
83
  try {
84
- return await getFileLinkCore(getToken, odspUrlParts, logger);
85
- }
86
- catch (error) {
87
- lastError = error;
88
- if (isFluidError(error) &&
89
- error.errorType === OdspErrorTypes.fileNotFoundOrAccessDeniedError &&
90
- hasRedirectionLocation(error) &&
91
- error.redirectLocation !== undefined) {
92
- const redirectLocation = error.redirectLocation;
84
+ const fileItem = await getFileItemLite(getToken, resolvedUrl, logger, true);
85
+ // Sometimes the siteUrl in the actual file is different from the siteUrl in the resolvedUrl due to location
86
+ // redirection. This creates issues in the getSharingInformation call. So we need to update the siteUrl in the
87
+ // resolvedUrl to the siteUrl in the fileItem which is the updated siteUrl.
88
+ const oldSiteDomain = new URL(resolvedUrl.siteUrl).origin;
89
+ const newSiteDomain = new URL(fileItem.sharepointIds.siteUrl).origin;
90
+ if (oldSiteDomain !== newSiteDomain) {
91
+ locationRedirected = true;
93
92
  logger.sendTelemetryEvent({
94
93
  eventName: "LocationRedirectionErrorForGetOdspFileLink",
95
94
  retryCount: count,
96
95
  });
97
- // Generate the new SiteUrl from the redirection location.
98
- const newSiteDomain = new URL(redirectLocation).origin;
99
- const newSiteUrl = `${newSiteDomain}${new URL(odspUrlParts.siteUrl).pathname}`;
100
- odspUrlParts.siteUrl = newSiteUrl;
96
+ renameTenantInOdspResolvedUrl(resolvedUrl, newSiteDomain);
97
+ }
98
+ return await getFileLinkCore(getToken, resolvedUrl, logger, fileItem);
99
+ }
100
+ catch (error) {
101
+ lastError = error;
102
+ // If the getSharingLink call fails with the 401/403/404 error, then it could be due to that the file has moved
103
+ // to another location. This could occur in case we have more than 1 tenant rename. In that case, we should retry
104
+ // the getFileItemLite call to get the updated fileItem.
105
+ if (isFluidError(error) &&
106
+ locationRedirected &&
107
+ (error.errorType === OdspErrorTypes.fileNotFoundOrAccessDeniedError ||
108
+ error.errorType === OdspErrorTypes.authorizationError)) {
101
109
  continue;
102
110
  }
103
111
  throw error;
@@ -105,8 +113,7 @@ async function getFileLinkWithLocationRedirectionHandling(getToken, odspUrlParts
105
113
  }
106
114
  throw lastError;
107
115
  }
108
- async function getFileLinkCore(getToken, odspUrlParts, logger) {
109
- const fileItem = await getFileItemLite(getToken, odspUrlParts, logger, true);
116
+ async function getFileLinkCore(getToken, odspUrlParts, logger, fileItem) {
110
117
  // ODSP link requires extra call to return link that is resistant to file being renamed or moved to different folder
111
118
  return PerformanceEvent.timedExecAsync(logger, { eventName: "odspFileLink", requestName: "getSharingInformation" }, async (event) => {
112
119
  let attempts = 0;
@@ -129,7 +136,6 @@ async function getFileLinkCore(getToken, odspUrlParts, logger) {
129
136
  headers: {
130
137
  "Content-Type": "application/json;odata=verbose",
131
138
  "Accept": "application/json;odata=verbose",
132
- "redirect": "manual",
133
139
  ...headers,
134
140
  },
135
141
  };
@@ -167,7 +173,6 @@ async function getFileItemLite(getToken, odspUrlParts, logger, forceAccessTokenV
167
173
  const authHeader = await getAuthHeader({ ...options, request: { url, method } }, "GetFileItemLite");
168
174
  assert(authHeader !== null, 0x2bc /* "Instrumented token fetcher with throwOnNullToken =true should never return null" */);
169
175
  const headers = getHeadersWithAuth(authHeader);
170
- headers.redirect = "manual";
171
176
  const requestInit = { method, headers };
172
177
  const response = await fetchHelper(url, requestInit);
173
178
  additionalProps = response.propsToLog;
@@ -182,4 +187,25 @@ async function getFileItemLite(getToken, odspUrlParts, logger, forceAccessTokenV
182
187
  return fileItem;
183
188
  });
184
189
  }
190
+ /**
191
+ * It takes a resolved url with old siteUrl and patches resolved url with updated site url domain.
192
+ * @param odspResolvedUrl - Previous odsp resolved url with older site url.
193
+ * @param newSiteDomain - New site domain after the tenant rename.
194
+ */
195
+ function renameTenantInOdspResolvedUrl(odspResolvedUrl, newSiteDomain) {
196
+ const newSiteUrl = `${newSiteDomain}${new URL(odspResolvedUrl.siteUrl).pathname}`;
197
+ odspResolvedUrl.siteUrl = newSiteUrl;
198
+ if (odspResolvedUrl.endpoints.attachmentGETStorageUrl) {
199
+ odspResolvedUrl.endpoints.attachmentGETStorageUrl = `${newSiteDomain}${new URL(odspResolvedUrl.endpoints.attachmentGETStorageUrl).pathname}`;
200
+ }
201
+ if (odspResolvedUrl.endpoints.attachmentPOSTStorageUrl) {
202
+ odspResolvedUrl.endpoints.attachmentPOSTStorageUrl = `${newSiteDomain}${new URL(odspResolvedUrl.endpoints.attachmentPOSTStorageUrl).pathname}`;
203
+ }
204
+ if (odspResolvedUrl.endpoints.deltaStorageUrl) {
205
+ odspResolvedUrl.endpoints.deltaStorageUrl = `${newSiteDomain}${new URL(odspResolvedUrl.endpoints.deltaStorageUrl).pathname}`;
206
+ }
207
+ if (odspResolvedUrl.endpoints.snapshotStorageUrl) {
208
+ odspResolvedUrl.endpoints.snapshotStorageUrl = `${newSiteDomain}${new URL(odspResolvedUrl.endpoints.snapshotStorageUrl).pathname}`;
209
+ }
210
+ }
185
211
  //# sourceMappingURL=getFileLink.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"getFileLink.js","sourceRoot":"","sources":["../src/getFileLink.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACxF,OAAO,EAAE,sBAAsB,EAAE,MAAM,4CAA4C,CAAC;AACpF,OAAO,EAEN,cAAc,GAGd,MAAM,kDAAkD,CAAC;AAC1D,OAAO,EAEN,gBAAgB,EAChB,YAAY,GACZ,MAAM,0CAA0C,CAAC;AAElD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EACN,WAAW,EACX,2BAA2B,EAC3B,qCAAqC,GACrC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,UAAU,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAClE,OAAO,EAAE,YAAY,IAAI,gDAAgD,EAAE,MAAM,iBAAiB,CAAC;AAEnG,2GAA2G;AAC3G,MAAM,aAAa,GAAG,IAAI,GAAG,EAA2B,CAAC;AAEzD;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAChC,QAAqD,EACrD,YAA2B,EAC3B,MAA2B;IAE3B,MAAM,QAAQ,GAAG,GAAG,YAAY,CAAC,OAAO,IAAI,YAAY,CAAC,OAAO,IAAI,YAAY,CAAC,MAAM,EAAE,CAAC;IAC1F,MAAM,uBAAuB,GAAG,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC5D,IAAI,uBAAuB,KAAK,SAAS,EAAE,CAAC;QAC3C,OAAO,uBAAuB,CAAC;IAChC,CAAC;IAED,MAAM,iBAAiB,GAAG,KAAK;QAC9B,IAAI,YAAoB,CAAC;QACzB,IAAI,CAAC;YACJ,IAAI,UAAU,GAAG,CAAC,CAAC;YACnB,YAAY,GAAG,MAAM,YAAY,CAChC,KAAK,IAAI,EAAE,CACV,gDAAgD,CAC/C,KAAK,IAAI,EAAE,CACV,0CAA0C,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,CAAC,EAC3E,iBAAiB,EACjB,MAAM,CACN,EACF,cAAc,EACd,MAAM,EACN;gBACC,4BAA4B;gBAC5B,8DAA8D;gBAC9D,OAAO,CAAC,SAAiB,EAAE,KAAU;oBACpC,UAAU,EAAE,CAAC;oBACb,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;wBACtB,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;4BACtD,sEAAsE;4BACtE,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC;4BACvB,MAAM,KAAK,CAAC;wBACb,CAAC;wBACD,MAAM,KAAK,CAAC;oBACb,CAAC;gBACF,CAAC;aACD,CACD,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,kDAAkD;YAClD,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC/B,MAAM,KAAK,CAAC;QACb,CAAC;QAED,6GAA6G;QAC7G,MAAM,CACL,YAAY,KAAK,SAAS,EAC1B,KAAK,CAAC,wDAAwD,CAC9D,CAAC;QACF,OAAO,YAAY,CAAC;IACrB,CAAC,CAAC;IACF,MAAM,QAAQ,GAAG,iBAAiB,EAAE,CAAC;IACrC,aAAa,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACtC,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,KAAK,UAAU,0CAA0C,CACxD,QAAqD,EACrD,YAA2B,EAC3B,MAA2B;IAE3B,qFAAqF;IACrF,2FAA2F;IAC3F,IAAI,SAAkB,CAAC;IACvB,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACzC,IAAI,CAAC;YACJ,OAAO,MAAM,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACzB,SAAS,GAAG,KAAK,CAAC;YAClB,IACC,YAAY,CAAC,KAAK,CAAC;gBACnB,KAAK,CAAC,SAAS,KAAK,cAAc,CAAC,+BAA+B;gBAClE,sBAAsB,CAAC,KAAK,CAAC;gBAC7B,KAAK,CAAC,gBAAgB,KAAK,SAAS,EACnC,CAAC;gBACF,MAAM,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,CAAC;gBAChD,MAAM,CAAC,kBAAkB,CAAC;oBACzB,SAAS,EAAE,4CAA4C;oBACvD,UAAU,EAAE,KAAK;iBACjB,CAAC,CAAC;gBACH,0DAA0D;gBAC1D,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,gBAAgB,CAAC,CAAC,MAAM,CAAC;gBACvD,MAAM,UAAU,GAAG,GAAG,aAAa,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC/E,YAAY,CAAC,OAAO,GAAG,UAAU,CAAC;gBAClC,SAAS;YACV,CAAC;YACD,MAAM,KAAK,CAAC;QACb,CAAC;IACF,CAAC;IACD,MAAM,SAAS,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,eAAe,CAC7B,QAAqD,EACrD,YAA2B,EAC3B,MAA2B;IAE3B,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,QAAQ,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAE7E,oHAAoH;IACpH,OAAO,gBAAgB,CAAC,cAAc,CACrC,MAAM,EACN,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,uBAAuB,EAAE,EACnE,KAAK,EAAE,KAAK,EAAE,EAAE;QACf,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,IAAI,eAAe,CAAC;QACpB,MAAM,QAAQ,GAAG,MAAM,2BAA2B,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YACpE,QAAQ,EAAE,CAAC;YACX,MAAM,aAAa,GAAG,qCAAqC,CAC1D,MAAM,EACN,YAAY,EACZ,QAAQ,CACR,CAAC;YAEF,qGAAqG;YACrG,gGAAgG;YAChG,mGAAmG;YACnG,6FAA6F;YAC7F,mGAAmG;YACnG,sGAAsG;YACtG,MAAM,GAAG,GAAG,GACX,YAAY,CAAC,OACd,8EAA8E,kBAAkB,CAC/F,IAAI,QAAQ,CAAC,aAAa,CAAC,gBAAgB,GAAG,CAC9C,EAAE,CAAC;YACJ,MAAM,MAAM,GAAG,MAAM,CAAC;YACtB,MAAM,UAAU,GAAG,MAAM,aAAa,CACrC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EACxC,iBAAiB,CACjB,CAAC;YACF,MAAM,OAAO,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;YAC/C,MAAM,WAAW,GAAG;gBACnB,MAAM;gBACN,OAAO,EAAE;oBACR,cAAc,EAAE,gCAAgC;oBAChD,QAAQ,EAAE,gCAAgC;oBAC1C,UAAU,EAAE,QAAQ;oBACpB,GAAG,OAAO;iBACV;aACD,CAAC;YACF,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;YACrD,eAAe,GAAG,QAAQ,CAAC,UAAU,CAAC;YAEtC,mEAAmE;YACnE,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAClD,+GAA+G;YAC/G,MAAM,SAAS,GAAG,WAAW,EAAE,CAAC,EAAE,SAAS,CAAC;YAC5C,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;gBACnC,sDAAsD;gBACtD,MAAM,IAAI,iBAAiB,CAC1B,0CAA0C,EAC1C,cAAc,CAAC,uBAAuB,EACtC,EAAE,aAAa,EAAE,CACjB,CAAC;YACH,CAAC;YACD,OAAO,SAAS,CAAC;QAClB,CAAC,CAAC,CAAC;QACH,iEAAiE;QACjE,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,eAAe,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC5C,OAAO,QAAQ,CAAC;IACjB,CAAC,CACD,CAAC;AACH,CAAC;AAuBD,MAAM,cAAc,GAAG,CAAC,iBAA0B,EAAqC,EAAE,CACxF,OAAQ,iBAA2C,CAAC,MAAM,KAAK,QAAQ;IACvE,OAAQ,iBAA2C,CAAC,SAAS,KAAK,QAAQ;IAC1E,uBAAuB;IACvB,OAAQ,iBAA2C,CAAC,aAAa,KAAK,QAAQ,CAAC;AAEhF,KAAK,UAAU,eAAe,CAC7B,QAAqD,EACrD,YAA2B,EAC3B,MAA2B,EAC3B,sCAA+C;IAE/C,OAAO,gBAAgB,CAAC,cAAc,CACrC,MAAM,EACN,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,EAC7D,KAAK,EAAE,KAAK,EAAE,EAAE;QACf,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,IAAI,eAAqD,CAAC;QAC1D,MAAM,QAAQ,GAAG,MAAM,2BAA2B,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YACpE,QAAQ,EAAE,CAAC;YACX,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC;YAClD,MAAM,aAAa,GAAG,qCAAqC,CAC1D,MAAM,EACN,YAAY,EACZ,QAAQ,CACR,CAAC;YACF,MAAM,GAAG,GAAG,GAAG,OAAO,qBAAqB,OAAO,UAAU,MAAM,wCAAwC,CAAC;YAC3G,MAAM,MAAM,GAAG,KAAK,CAAC;YACrB,MAAM,UAAU,GAAG,MAAM,aAAa,CACrC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EACxC,iBAAiB,CACjB,CAAC;YACF,MAAM,CACL,UAAU,KAAK,IAAI,EACnB,KAAK,CAAC,uFAAuF,CAC7F,CAAC;YAEF,MAAM,OAAO,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;YAC/C,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAC5B,MAAM,WAAW,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;YACxC,MAAM,QAAQ,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;YACrD,eAAe,GAAG,QAAQ,CAAC,UAAU,CAAC;YAEtC,MAAM,YAAY,GAAY,MAAM,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAC5D,IAAI,CAAC,cAAc,CAAC,YAAY,CAAC,EAAE,CAAC;gBACnC,sDAAsD;gBACtD,MAAM,IAAI,iBAAiB,CAC1B,oCAAoC,EACpC,cAAc,CAAC,uBAAuB,EACtC,EAAE,aAAa,EAAE,CACjB,CAAC;YACH,CAAC;YACD,OAAO,YAAY,CAAC;QACrB,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,eAAe,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC5C,OAAO,QAAQ,CAAC;IACjB,CAAC,CACD,CAAC;AACH,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ITelemetryBaseProperties } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { NonRetryableError, runWithRetry } from \"@fluidframework/driver-utils/internal\";\nimport { hasRedirectionLocation } from \"@fluidframework/odsp-doclib-utils/internal\";\nimport {\n\tIOdspUrlParts,\n\tOdspErrorTypes,\n\tOdspResourceTokenFetchOptions,\n\tTokenFetcher,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tPerformanceEvent,\n\tisFluidError,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { getHeadersWithAuth } from \"./getUrlAndHeadersWithAuth.js\";\nimport {\n\tfetchHelper,\n\tgetWithRetryForTokenRefresh,\n\ttoInstrumentedOdspStorageTokenFetcher,\n} from \"./odspUtils.js\";\nimport { pkgVersion as driverVersion } from \"./packageVersion.js\";\nimport { runWithRetry as runWithRetryForCoherencyAndServiceReadOnlyErrors } from \"./retryUtils.js\";\n\n// Store cached responses for the lifetime of web session as file link remains the same for given file item\nconst fileLinkCache = new Map<string, Promise<string>>();\n\n/**\n * Returns file link for a file with given drive and item ids.\n * Scope needed: files.readwrite.all.\n * This function keeps retrying if it gets a retriable error or wait for some delay if it gets a\n * throttling error. In future, we are thinking of app allowing to pass some cancel token, with which\n * we would be able to stop retrying.\n * @param getToken - used to fetch access tokens needed to execute operation\n * @param odspUrlParts - object describing file storage identity\n * @param logger - used to log results of operation, including any error\n * @returns Promise which resolves to file link url when successful; otherwise, undefined.\n */\nexport async function getFileLink(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\todspUrlParts: IOdspUrlParts,\n\tlogger: ITelemetryLoggerExt,\n): Promise<string> {\n\tconst cacheKey = `${odspUrlParts.siteUrl}_${odspUrlParts.driveId}_${odspUrlParts.itemId}`;\n\tconst maybeFileLinkCacheEntry = fileLinkCache.get(cacheKey);\n\tif (maybeFileLinkCacheEntry !== undefined) {\n\t\treturn maybeFileLinkCacheEntry;\n\t}\n\n\tconst fileLinkGenerator = async function (): Promise<string> {\n\t\tlet fileLinkCore: string;\n\t\ttry {\n\t\t\tlet retryCount = 0;\n\t\t\tfileLinkCore = await runWithRetry(\n\t\t\t\tasync () =>\n\t\t\t\t\trunWithRetryForCoherencyAndServiceReadOnlyErrors(\n\t\t\t\t\t\tasync () =>\n\t\t\t\t\t\t\tgetFileLinkWithLocationRedirectionHandling(getToken, odspUrlParts, logger),\n\t\t\t\t\t\t\"getFileLinkCore\",\n\t\t\t\t\t\tlogger,\n\t\t\t\t\t),\n\t\t\t\t\"getShareLink\",\n\t\t\t\tlogger,\n\t\t\t\t{\n\t\t\t\t\t// TODO: use a stronger type\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\t\t\tonRetry(delayInMs: number, error: any) {\n\t\t\t\t\t\tretryCount++;\n\t\t\t\t\t\tif (retryCount === 5) {\n\t\t\t\t\t\t\tif (error !== undefined && typeof error === \"object\") {\n\t\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\t\t\t\t\t\terror.canRetry = false;\n\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// Delete from the cache to permit retrying later.\n\t\t\tfileLinkCache.delete(cacheKey);\n\t\t\tthrow error;\n\t\t}\n\n\t\t// We are guaranteed to run the getFileLinkCore at least once with successful result (which must be a string)\n\t\tassert(\n\t\t\tfileLinkCore !== undefined,\n\t\t\t0x292 /* \"Unexpected undefined result from getFileLinkCore\" */,\n\t\t);\n\t\treturn fileLinkCore;\n\t};\n\tconst fileLink = fileLinkGenerator();\n\tfileLinkCache.set(cacheKey, fileLink);\n\treturn fileLink;\n}\n\n/**\n * Handles location redirection while fulfilling the getFilelink call. We don't want browser to handle\n * the redirection as the browser will retry with same auth token which will not work as we need app\n * to regenerate tokens for the new site domain. So when we will make the network calls below we will set\n * the redirect:manual header to manually handle these redirects.\n * Refer: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308\n * @param getToken - token fetcher to fetch the token.\n * @param odspUrlParts - parts of odsp resolved url.\n * @param logger - logger to send events.\n * @returns Response from the API call.\n * @alpha\n */\nasync function getFileLinkWithLocationRedirectionHandling(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\todspUrlParts: IOdspUrlParts,\n\tlogger: ITelemetryLoggerExt,\n): Promise<string> {\n\t// We can have chains of location redirection one after the other, so have a for loop\n\t// so that we can keep handling the same type of error. Set max number of redirection to 5.\n\tlet lastError: unknown;\n\tfor (let count = 1; count <= 5; count++) {\n\t\ttry {\n\t\t\treturn await getFileLinkCore(getToken, odspUrlParts, logger);\n\t\t} catch (error: unknown) {\n\t\t\tlastError = error;\n\t\t\tif (\n\t\t\t\tisFluidError(error) &&\n\t\t\t\terror.errorType === OdspErrorTypes.fileNotFoundOrAccessDeniedError &&\n\t\t\t\thasRedirectionLocation(error) &&\n\t\t\t\terror.redirectLocation !== undefined\n\t\t\t) {\n\t\t\t\tconst redirectLocation = error.redirectLocation;\n\t\t\t\tlogger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"LocationRedirectionErrorForGetOdspFileLink\",\n\t\t\t\t\tretryCount: count,\n\t\t\t\t});\n\t\t\t\t// Generate the new SiteUrl from the redirection location.\n\t\t\t\tconst newSiteDomain = new URL(redirectLocation).origin;\n\t\t\t\tconst newSiteUrl = `${newSiteDomain}${new URL(odspUrlParts.siteUrl).pathname}`;\n\t\t\t\todspUrlParts.siteUrl = newSiteUrl;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\tthrow lastError;\n}\n\nasync function getFileLinkCore(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\todspUrlParts: IOdspUrlParts,\n\tlogger: ITelemetryLoggerExt,\n): Promise<string> {\n\tconst fileItem = await getFileItemLite(getToken, odspUrlParts, logger, true);\n\n\t// ODSP link requires extra call to return link that is resistant to file being renamed or moved to different folder\n\treturn PerformanceEvent.timedExecAsync(\n\t\tlogger,\n\t\t{ eventName: \"odspFileLink\", requestName: \"getSharingInformation\" },\n\t\tasync (event) => {\n\t\t\tlet attempts = 0;\n\t\t\tlet additionalProps;\n\t\t\tconst fileLink = await getWithRetryForTokenRefresh(async (options) => {\n\t\t\t\tattempts++;\n\t\t\t\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\t\t\t\tlogger,\n\t\t\t\t\todspUrlParts,\n\t\t\t\t\tgetToken,\n\t\t\t\t);\n\n\t\t\t\t// IMPORTANT: In past we were using GetFileByUrl() API to get to the list item that was corresponding\n\t\t\t\t// to the file. This was intentionally replaced with GetFileById() to solve the following issue:\n\t\t\t\t// GetFileByUrl() uses webDavUrl to locate list item. This API does not work for Consumer scenarios\n\t\t\t\t// where webDavUrl is constructed using legacy ODC format for backward compatibility reasons.\n\t\t\t\t// GetFileByUrl() does not understand that format and thus fails. GetFileById() relies on file item\n\t\t\t\t// unique guid (sharepointIds.listItemUniqueId) and it works uniformly across Consumer and Commercial.\n\t\t\t\tconst url = `${\n\t\t\t\t\todspUrlParts.siteUrl\n\t\t\t\t}/_api/web/GetFileById(@a1)/ListItemAllFields/GetSharingInformation?@a1=guid${encodeURIComponent(\n\t\t\t\t\t`'${fileItem.sharepointIds.listItemUniqueId}'`,\n\t\t\t\t)}`;\n\t\t\t\tconst method = \"POST\";\n\t\t\t\tconst authHeader = await getAuthHeader(\n\t\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\t\t\"GetFileLinkCore\",\n\t\t\t\t);\n\t\t\t\tconst headers = getHeadersWithAuth(authHeader);\n\t\t\t\tconst requestInit = {\n\t\t\t\t\tmethod,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json;odata=verbose\",\n\t\t\t\t\t\t\"Accept\": \"application/json;odata=verbose\",\n\t\t\t\t\t\t\"redirect\": \"manual\",\n\t\t\t\t\t\t...headers,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\tconst response = await fetchHelper(url, requestInit);\n\t\t\t\tadditionalProps = response.propsToLog;\n\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\t\t\t\tconst sharingInfo = await response.content.json();\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access\n\t\t\t\tconst directUrl = sharingInfo?.d?.directUrl;\n\t\t\t\tif (typeof directUrl !== \"string\") {\n\t\t\t\t\t// This will retry once in getWithRetryForTokenRefresh\n\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t\"Malformed GetSharingInformation response\",\n\t\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t\t{ driverVersion },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn directUrl;\n\t\t\t});\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n\t\t\tevent.end({ ...additionalProps, attempts });\n\t\t\treturn fileLink;\n\t\t},\n\t);\n}\n\n/**\n * Sharepoint Ids Interface\n */\ninterface IGraphSharepointIds {\n\tlistId: string;\n\tlistItemId: string;\n\tlistItemUniqueId: string;\n\tsiteId: string;\n\tsiteUrl: string;\n\twebId: string;\n}\n\n/**\n * This represents a lite version of file item containing only select file properties\n */\ninterface FileItemLite {\n\twebUrl: string;\n\twebDavUrl: string;\n\tsharepointIds: IGraphSharepointIds;\n}\n\nconst isFileItemLite = (maybeFileItemLite: unknown): maybeFileItemLite is FileItemLite =>\n\ttypeof (maybeFileItemLite as Partial<FileItemLite>).webUrl === \"string\" &&\n\ttypeof (maybeFileItemLite as Partial<FileItemLite>).webDavUrl === \"string\" &&\n\t// TODO: stronger check\n\ttypeof (maybeFileItemLite as Partial<FileItemLite>).sharepointIds === \"object\";\n\nasync function getFileItemLite(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\todspUrlParts: IOdspUrlParts,\n\tlogger: ITelemetryLoggerExt,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n): Promise<FileItemLite> {\n\treturn PerformanceEvent.timedExecAsync(\n\t\tlogger,\n\t\t{ eventName: \"odspFileLink\", requestName: \"getFileItemLite\" },\n\t\tasync (event) => {\n\t\t\tlet attempts = 0;\n\t\t\tlet additionalProps: ITelemetryBaseProperties | undefined;\n\t\t\tconst fileItem = await getWithRetryForTokenRefresh(async (options) => {\n\t\t\t\tattempts++;\n\t\t\t\tconst { siteUrl, driveId, itemId } = odspUrlParts;\n\t\t\t\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\t\t\t\tlogger,\n\t\t\t\t\todspUrlParts,\n\t\t\t\t\tgetToken,\n\t\t\t\t);\n\t\t\t\tconst url = `${siteUrl}/_api/v2.0/drives/${driveId}/items/${itemId}?select=webUrl,webDavUrl,sharepointIds`;\n\t\t\t\tconst method = \"GET\";\n\t\t\t\tconst authHeader = await getAuthHeader(\n\t\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\t\t\"GetFileItemLite\",\n\t\t\t\t);\n\t\t\t\tassert(\n\t\t\t\t\tauthHeader !== null,\n\t\t\t\t\t0x2bc /* \"Instrumented token fetcher with throwOnNullToken =true should never return null\" */,\n\t\t\t\t);\n\n\t\t\t\tconst headers = getHeadersWithAuth(authHeader);\n\t\t\t\theaders.redirect = \"manual\";\n\t\t\t\tconst requestInit = { method, headers };\n\t\t\t\tconst response = await fetchHelper(url, requestInit);\n\t\t\t\tadditionalProps = response.propsToLog;\n\n\t\t\t\tconst responseJson: unknown = await response.content.json();\n\t\t\t\tif (!isFileItemLite(responseJson)) {\n\t\t\t\t\t// This will retry once in getWithRetryForTokenRefresh\n\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t\"Malformed getFileItemLite response\",\n\t\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t\t{ driverVersion },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn responseJson;\n\t\t\t});\n\t\t\tevent.end({ ...additionalProps, attempts });\n\t\t\treturn fileItem;\n\t\t},\n\t);\n}\n"]}
1
+ {"version":3,"file":"getFileLink.js","sourceRoot":"","sources":["../src/getFileLink.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type { ITelemetryBaseProperties } from \"@fluidframework/core-interfaces\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport { NonRetryableError, runWithRetry } from \"@fluidframework/driver-utils/internal\";\nimport {\n\tIOdspUrlParts,\n\tOdspErrorTypes,\n\tOdspResourceTokenFetchOptions,\n\tTokenFetcher,\n\ttype IOdspResolvedUrl,\n} from \"@fluidframework/odsp-driver-definitions/internal\";\nimport {\n\tITelemetryLoggerExt,\n\tPerformanceEvent,\n\tisFluidError,\n} from \"@fluidframework/telemetry-utils/internal\";\n\nimport { getHeadersWithAuth } from \"./getUrlAndHeadersWithAuth.js\";\nimport {\n\tfetchHelper,\n\tgetWithRetryForTokenRefresh,\n\ttoInstrumentedOdspStorageTokenFetcher,\n} from \"./odspUtils.js\";\nimport { pkgVersion as driverVersion } from \"./packageVersion.js\";\nimport { runWithRetry as runWithRetryForCoherencyAndServiceReadOnlyErrors } from \"./retryUtils.js\";\n\n// Store cached responses for the lifetime of web session as file link remains the same for given file item\nconst fileLinkCache = new Map<string, Promise<string>>();\n\n/**\n * Returns file link for a file with given drive and item ids.\n * Scope needed: files.readwrite.all.\n * This function keeps retrying if it gets a retriable error or wait for some delay if it gets a\n * throttling error. In future, we are thinking of app allowing to pass some cancel token, with which\n * we would be able to stop retrying.\n * @param getToken - used to fetch access tokens needed to execute operation\n * @param odspUrlParts - object describing file storage identity\n * @param logger - used to log results of operation, including any error\n * @returns Promise which resolves to file link url when successful; otherwise, undefined.\n */\nexport async function getFileLink(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\tresolvedUrl: IOdspResolvedUrl,\n\tlogger: ITelemetryLoggerExt,\n): Promise<string> {\n\tconst cacheKey = `${resolvedUrl.siteUrl}_${resolvedUrl.driveId}_${resolvedUrl.itemId}`;\n\tconst maybeFileLinkCacheEntry = fileLinkCache.get(cacheKey);\n\tif (maybeFileLinkCacheEntry !== undefined) {\n\t\treturn maybeFileLinkCacheEntry;\n\t}\n\n\tconst fileLinkGenerator = async function (): Promise<string> {\n\t\tlet fileLinkCore: string;\n\t\ttry {\n\t\t\tlet retryCount = 0;\n\t\t\tfileLinkCore = await runWithRetry(\n\t\t\t\tasync () =>\n\t\t\t\t\trunWithRetryForCoherencyAndServiceReadOnlyErrors(\n\t\t\t\t\t\tasync () =>\n\t\t\t\t\t\t\tgetFileLinkWithLocationRedirectionHandling(getToken, resolvedUrl, logger),\n\t\t\t\t\t\t\"getFileLinkCore\",\n\t\t\t\t\t\tlogger,\n\t\t\t\t\t),\n\t\t\t\t\"getShareLink\",\n\t\t\t\tlogger,\n\t\t\t\t{\n\t\t\t\t\t// TODO: use a stronger type\n\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\t\t\tonRetry(delayInMs: number, error: any) {\n\t\t\t\t\t\tretryCount++;\n\t\t\t\t\t\tif (retryCount === 5) {\n\t\t\t\t\t\t\tif (error !== undefined && typeof error === \"object\") {\n\t\t\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n\t\t\t\t\t\t\t\terror.canRetry = false;\n\t\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tthrow error;\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t);\n\t\t} catch (error) {\n\t\t\t// Delete from the cache to permit retrying later.\n\t\t\tfileLinkCache.delete(cacheKey);\n\t\t\tthrow error;\n\t\t}\n\n\t\t// We are guaranteed to run the getFileLinkCore at least once with successful result (which must be a string)\n\t\tassert(\n\t\t\tfileLinkCore !== undefined,\n\t\t\t0x292 /* \"Unexpected undefined result from getFileLinkCore\" */,\n\t\t);\n\t\treturn fileLinkCore;\n\t};\n\tconst fileLink = fileLinkGenerator();\n\tfileLinkCache.set(cacheKey, fileLink);\n\treturn fileLink;\n}\n\n/**\n * Handles location redirection while fulfilling the getFilelink call. We don't want browser to handle\n * the redirection as the browser will retry with same auth token which will not work as we need app\n * to regenerate tokens for the new site domain. So when we will make the network calls below we will set\n * the redirect:manual header to manually handle these redirects.\n * Refer: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308\n * @param getToken - token fetcher to fetch the token.\n * @param odspUrlParts - parts of odsp resolved url.\n * @param logger - logger to send events.\n * @returns Response from the API call.\n * @alpha\n */\nasync function getFileLinkWithLocationRedirectionHandling(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\tresolvedUrl: IOdspResolvedUrl,\n\tlogger: ITelemetryLoggerExt,\n): Promise<string> {\n\t// We can have chains of location redirection one after the other, so have a for loop\n\t// so that we can keep handling the same type of error. Set max number of redirection to 5.\n\tlet lastError: unknown;\n\tlet locationRedirected = false;\n\tfor (let count = 1; count <= 5; count++) {\n\t\ttry {\n\t\t\tconst fileItem = await getFileItemLite(getToken, resolvedUrl, logger, true);\n\t\t\t// Sometimes the siteUrl in the actual file is different from the siteUrl in the resolvedUrl due to location\n\t\t\t// redirection. This creates issues in the getSharingInformation call. So we need to update the siteUrl in the\n\t\t\t// resolvedUrl to the siteUrl in the fileItem which is the updated siteUrl.\n\t\t\tconst oldSiteDomain = new URL(resolvedUrl.siteUrl).origin;\n\t\t\tconst newSiteDomain = new URL(fileItem.sharepointIds.siteUrl).origin;\n\t\t\tif (oldSiteDomain !== newSiteDomain) {\n\t\t\t\tlocationRedirected = true;\n\t\t\t\tlogger.sendTelemetryEvent({\n\t\t\t\t\teventName: \"LocationRedirectionErrorForGetOdspFileLink\",\n\t\t\t\t\tretryCount: count,\n\t\t\t\t});\n\t\t\t\trenameTenantInOdspResolvedUrl(resolvedUrl, newSiteDomain);\n\t\t\t}\n\t\t\treturn await getFileLinkCore(getToken, resolvedUrl, logger, fileItem);\n\t\t} catch (error: unknown) {\n\t\t\tlastError = error;\n\t\t\t// If the getSharingLink call fails with the 401/403/404 error, then it could be due to that the file has moved\n\t\t\t// to another location. This could occur in case we have more than 1 tenant rename. In that case, we should retry\n\t\t\t// the getFileItemLite call to get the updated fileItem.\n\t\t\tif (\n\t\t\t\tisFluidError(error) &&\n\t\t\t\tlocationRedirected &&\n\t\t\t\t(error.errorType === OdspErrorTypes.fileNotFoundOrAccessDeniedError ||\n\t\t\t\t\terror.errorType === OdspErrorTypes.authorizationError)\n\t\t\t) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\tthrow lastError;\n}\n\nasync function getFileLinkCore(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\todspUrlParts: IOdspUrlParts,\n\tlogger: ITelemetryLoggerExt,\n\tfileItem: FileItemLite,\n): Promise<string> {\n\t// ODSP link requires extra call to return link that is resistant to file being renamed or moved to different folder\n\treturn PerformanceEvent.timedExecAsync(\n\t\tlogger,\n\t\t{ eventName: \"odspFileLink\", requestName: \"getSharingInformation\" },\n\t\tasync (event) => {\n\t\t\tlet attempts = 0;\n\t\t\tlet additionalProps;\n\t\t\tconst fileLink = await getWithRetryForTokenRefresh(async (options) => {\n\t\t\t\tattempts++;\n\t\t\t\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\t\t\t\tlogger,\n\t\t\t\t\todspUrlParts,\n\t\t\t\t\tgetToken,\n\t\t\t\t);\n\n\t\t\t\t// IMPORTANT: In past we were using GetFileByUrl() API to get to the list item that was corresponding\n\t\t\t\t// to the file. This was intentionally replaced with GetFileById() to solve the following issue:\n\t\t\t\t// GetFileByUrl() uses webDavUrl to locate list item. This API does not work for Consumer scenarios\n\t\t\t\t// where webDavUrl is constructed using legacy ODC format for backward compatibility reasons.\n\t\t\t\t// GetFileByUrl() does not understand that format and thus fails. GetFileById() relies on file item\n\t\t\t\t// unique guid (sharepointIds.listItemUniqueId) and it works uniformly across Consumer and Commercial.\n\t\t\t\tconst url = `${\n\t\t\t\t\todspUrlParts.siteUrl\n\t\t\t\t}/_api/web/GetFileById(@a1)/ListItemAllFields/GetSharingInformation?@a1=guid${encodeURIComponent(\n\t\t\t\t\t`'${fileItem.sharepointIds.listItemUniqueId}'`,\n\t\t\t\t)}`;\n\t\t\t\tconst method = \"POST\";\n\t\t\t\tconst authHeader = await getAuthHeader(\n\t\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\t\t\"GetFileLinkCore\",\n\t\t\t\t);\n\t\t\t\tconst headers = getHeadersWithAuth(authHeader);\n\t\t\t\tconst requestInit = {\n\t\t\t\t\tmethod,\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json;odata=verbose\",\n\t\t\t\t\t\t\"Accept\": \"application/json;odata=verbose\",\n\t\t\t\t\t\t...headers,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t\tconst response = await fetchHelper(url, requestInit);\n\t\t\t\tadditionalProps = response.propsToLog;\n\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n\t\t\t\tconst sharingInfo = await response.content.json();\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access\n\t\t\t\tconst directUrl = sharingInfo?.d?.directUrl;\n\t\t\t\tif (typeof directUrl !== \"string\") {\n\t\t\t\t\t// This will retry once in getWithRetryForTokenRefresh\n\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t\"Malformed GetSharingInformation response\",\n\t\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t\t{ driverVersion },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn directUrl;\n\t\t\t});\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-argument\n\t\t\tevent.end({ ...additionalProps, attempts });\n\t\t\treturn fileLink;\n\t\t},\n\t);\n}\n\n/**\n * Sharepoint Ids Interface\n */\ninterface IGraphSharepointIds {\n\tlistId: string;\n\tlistItemId: string;\n\tlistItemUniqueId: string;\n\tsiteId: string;\n\tsiteUrl: string;\n\twebId: string;\n}\n\n/**\n * This represents a lite version of file item containing only select file properties\n */\ninterface FileItemLite {\n\twebUrl: string;\n\twebDavUrl: string;\n\tsharepointIds: IGraphSharepointIds;\n}\n\nconst isFileItemLite = (maybeFileItemLite: unknown): maybeFileItemLite is FileItemLite =>\n\ttypeof (maybeFileItemLite as Partial<FileItemLite>).webUrl === \"string\" &&\n\ttypeof (maybeFileItemLite as Partial<FileItemLite>).webDavUrl === \"string\" &&\n\t// TODO: stronger check\n\ttypeof (maybeFileItemLite as Partial<FileItemLite>).sharepointIds === \"object\";\n\nasync function getFileItemLite(\n\tgetToken: TokenFetcher<OdspResourceTokenFetchOptions>,\n\todspUrlParts: IOdspUrlParts,\n\tlogger: ITelemetryLoggerExt,\n\tforceAccessTokenViaAuthorizationHeader: boolean,\n): Promise<FileItemLite> {\n\treturn PerformanceEvent.timedExecAsync(\n\t\tlogger,\n\t\t{ eventName: \"odspFileLink\", requestName: \"getFileItemLite\" },\n\t\tasync (event) => {\n\t\t\tlet attempts = 0;\n\t\t\tlet additionalProps: ITelemetryBaseProperties | undefined;\n\t\t\tconst fileItem = await getWithRetryForTokenRefresh(async (options) => {\n\t\t\t\tattempts++;\n\t\t\t\tconst { siteUrl, driveId, itemId } = odspUrlParts;\n\t\t\t\tconst getAuthHeader = toInstrumentedOdspStorageTokenFetcher(\n\t\t\t\t\tlogger,\n\t\t\t\t\todspUrlParts,\n\t\t\t\t\tgetToken,\n\t\t\t\t);\n\t\t\t\tconst url = `${siteUrl}/_api/v2.0/drives/${driveId}/items/${itemId}?select=webUrl,webDavUrl,sharepointIds`;\n\t\t\t\tconst method = \"GET\";\n\t\t\t\tconst authHeader = await getAuthHeader(\n\t\t\t\t\t{ ...options, request: { url, method } },\n\t\t\t\t\t\"GetFileItemLite\",\n\t\t\t\t);\n\t\t\t\tassert(\n\t\t\t\t\tauthHeader !== null,\n\t\t\t\t\t0x2bc /* \"Instrumented token fetcher with throwOnNullToken =true should never return null\" */,\n\t\t\t\t);\n\n\t\t\t\tconst headers = getHeadersWithAuth(authHeader);\n\t\t\t\tconst requestInit = { method, headers };\n\t\t\t\tconst response = await fetchHelper(url, requestInit);\n\t\t\t\tadditionalProps = response.propsToLog;\n\n\t\t\t\tconst responseJson: unknown = await response.content.json();\n\t\t\t\tif (!isFileItemLite(responseJson)) {\n\t\t\t\t\t// This will retry once in getWithRetryForTokenRefresh\n\t\t\t\t\tthrow new NonRetryableError(\n\t\t\t\t\t\t\"Malformed getFileItemLite response\",\n\t\t\t\t\t\tOdspErrorTypes.incorrectServerResponse,\n\t\t\t\t\t\t{ driverVersion },\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn responseJson;\n\t\t\t});\n\t\t\tevent.end({ ...additionalProps, attempts });\n\t\t\treturn fileItem;\n\t\t},\n\t);\n}\n\n/**\n * It takes a resolved url with old siteUrl and patches resolved url with updated site url domain.\n * @param odspResolvedUrl - Previous odsp resolved url with older site url.\n * @param newSiteDomain - New site domain after the tenant rename.\n */\nfunction renameTenantInOdspResolvedUrl(\n\todspResolvedUrl: IOdspResolvedUrl,\n\tnewSiteDomain: string,\n): void {\n\tconst newSiteUrl = `${newSiteDomain}${new URL(odspResolvedUrl.siteUrl).pathname}`;\n\todspResolvedUrl.siteUrl = newSiteUrl;\n\n\tif (odspResolvedUrl.endpoints.attachmentGETStorageUrl) {\n\t\todspResolvedUrl.endpoints.attachmentGETStorageUrl = `${newSiteDomain}${\n\t\t\tnew URL(odspResolvedUrl.endpoints.attachmentGETStorageUrl).pathname\n\t\t}`;\n\t}\n\tif (odspResolvedUrl.endpoints.attachmentPOSTStorageUrl) {\n\t\todspResolvedUrl.endpoints.attachmentPOSTStorageUrl = `${newSiteDomain}${\n\t\t\tnew URL(odspResolvedUrl.endpoints.attachmentPOSTStorageUrl).pathname\n\t\t}`;\n\t}\n\tif (odspResolvedUrl.endpoints.deltaStorageUrl) {\n\t\todspResolvedUrl.endpoints.deltaStorageUrl = `${newSiteDomain}${\n\t\t\tnew URL(odspResolvedUrl.endpoints.deltaStorageUrl).pathname\n\t\t}`;\n\t}\n\tif (odspResolvedUrl.endpoints.snapshotStorageUrl) {\n\t\todspResolvedUrl.endpoints.snapshotStorageUrl = `${newSiteDomain}${\n\t\t\tnew URL(odspResolvedUrl.endpoints.snapshotStorageUrl).pathname\n\t\t}`;\n\t}\n}\n"]}
@@ -5,5 +5,5 @@
5
5
  * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY
6
6
  */
7
7
  export declare const pkgName = "@fluidframework/odsp-driver";
8
- export declare const pkgVersion = "2.0.7";
8
+ export declare const pkgVersion = "2.0.9";
9
9
  //# sourceMappingURL=packageVersion.d.ts.map
@@ -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 = "2.0.7";
8
+ export const pkgVersion = "2.0.9";
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,OAAO,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 = \"2.0.7\";\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,OAAO,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 = \"2.0.9\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluidframework/odsp-driver",
3
- "version": "2.0.7",
3
+ "version": "2.0.9",
4
4
  "description": "Socket storage implementation for SPO and ODC",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -77,15 +77,15 @@
77
77
  "temp-directory": "nyc/.nyc_output"
78
78
  },
79
79
  "dependencies": {
80
- "@fluid-internal/client-utils": "~2.0.7",
81
- "@fluidframework/core-interfaces": "~2.0.7",
82
- "@fluidframework/core-utils": "~2.0.7",
83
- "@fluidframework/driver-base": "~2.0.7",
84
- "@fluidframework/driver-definitions": "~2.0.7",
85
- "@fluidframework/driver-utils": "~2.0.7",
86
- "@fluidframework/odsp-doclib-utils": "~2.0.7",
87
- "@fluidframework/odsp-driver-definitions": "~2.0.7",
88
- "@fluidframework/telemetry-utils": "~2.0.7",
80
+ "@fluid-internal/client-utils": "~2.0.9",
81
+ "@fluidframework/core-interfaces": "~2.0.9",
82
+ "@fluidframework/core-utils": "~2.0.9",
83
+ "@fluidframework/driver-base": "~2.0.9",
84
+ "@fluidframework/driver-definitions": "~2.0.9",
85
+ "@fluidframework/driver-utils": "~2.0.9",
86
+ "@fluidframework/odsp-doclib-utils": "~2.0.9",
87
+ "@fluidframework/odsp-driver-definitions": "~2.0.9",
88
+ "@fluidframework/telemetry-utils": "~2.0.9",
89
89
  "node-fetch": "^2.6.9",
90
90
  "socket.io-client": "^4.7.3",
91
91
  "uuid": "^9.0.0"
@@ -93,7 +93,7 @@
93
93
  "devDependencies": {
94
94
  "@arethetypeswrong/cli": "^0.15.2",
95
95
  "@biomejs/biome": "^1.7.3",
96
- "@fluid-internal/mocha-test-setup": "~2.0.7",
96
+ "@fluid-internal/mocha-test-setup": "~2.0.9",
97
97
  "@fluid-tools/build-cli": "^0.39.0",
98
98
  "@fluidframework/build-common": "^2.0.3",
99
99
  "@fluidframework/build-tools": "^0.39.0",
package/src/createFile.ts CHANGED
@@ -164,13 +164,22 @@ function extractShareLinkData(
164
164
  return shareLinkInfo;
165
165
  }
166
166
 
167
+ /**
168
+ * Encodes file path so it can be embedded in the request url
169
+ * @param path - path to encode
170
+ * @returns encoded path or "" if path is undefined
171
+ */
172
+ function encodeFilePath(path: string | undefined): string {
173
+ return path ? encodeURIComponent(path.startsWith("/") ? path : `/${path}`) : "";
174
+ }
175
+
167
176
  export async function createNewEmptyFluidFile(
168
177
  getAuthHeader: InstrumentedStorageTokenFetcher,
169
178
  newFileInfo: INewFileInfo,
170
179
  logger: ITelemetryLoggerExt,
171
180
  epochTracker: EpochTracker,
172
181
  ): Promise<string> {
173
- const filePath = newFileInfo.filePath ? encodeURIComponent(`/${newFileInfo.filePath}`) : "";
182
+ const filePath = encodeFilePath(newFileInfo.filePath);
174
183
  // add .tmp extension to empty file (host is expected to rename)
175
184
  const encodedFilename = encodeURIComponent(`${newFileInfo.filename}.tmp`);
176
185
  const initialUrl = `${getApiRoot(new URL(newFileInfo.siteUrl))}/drives/${
@@ -234,7 +243,7 @@ export async function createNewFluidFileFromSummary(
234
243
  epochTracker: EpochTracker,
235
244
  forceAccessTokenViaAuthorizationHeader: boolean,
236
245
  ): Promise<ICreateFileResponse> {
237
- const filePath = newFileInfo.filePath ? encodeURIComponent(`/${newFileInfo.filePath}`) : "";
246
+ const filePath = encodeFilePath(newFileInfo.filePath);
238
247
  const encodedFilename = encodeURIComponent(newFileInfo.filename);
239
248
  const baseUrl =
240
249
  `${getApiRoot(new URL(newFileInfo.siteUrl))}/drives/${newFileInfo.driveId}/items/root:` +
@@ -6,12 +6,12 @@
6
6
  import type { ITelemetryBaseProperties } from "@fluidframework/core-interfaces";
7
7
  import { assert } from "@fluidframework/core-utils/internal";
8
8
  import { NonRetryableError, runWithRetry } from "@fluidframework/driver-utils/internal";
9
- import { hasRedirectionLocation } from "@fluidframework/odsp-doclib-utils/internal";
10
9
  import {
11
10
  IOdspUrlParts,
12
11
  OdspErrorTypes,
13
12
  OdspResourceTokenFetchOptions,
14
13
  TokenFetcher,
14
+ type IOdspResolvedUrl,
15
15
  } from "@fluidframework/odsp-driver-definitions/internal";
16
16
  import {
17
17
  ITelemetryLoggerExt,
@@ -44,10 +44,10 @@ const fileLinkCache = new Map<string, Promise<string>>();
44
44
  */
45
45
  export async function getFileLink(
46
46
  getToken: TokenFetcher<OdspResourceTokenFetchOptions>,
47
- odspUrlParts: IOdspUrlParts,
47
+ resolvedUrl: IOdspResolvedUrl,
48
48
  logger: ITelemetryLoggerExt,
49
49
  ): Promise<string> {
50
- const cacheKey = `${odspUrlParts.siteUrl}_${odspUrlParts.driveId}_${odspUrlParts.itemId}`;
50
+ const cacheKey = `${resolvedUrl.siteUrl}_${resolvedUrl.driveId}_${resolvedUrl.itemId}`;
51
51
  const maybeFileLinkCacheEntry = fileLinkCache.get(cacheKey);
52
52
  if (maybeFileLinkCacheEntry !== undefined) {
53
53
  return maybeFileLinkCacheEntry;
@@ -61,7 +61,7 @@ export async function getFileLink(
61
61
  async () =>
62
62
  runWithRetryForCoherencyAndServiceReadOnlyErrors(
63
63
  async () =>
64
- getFileLinkWithLocationRedirectionHandling(getToken, odspUrlParts, logger),
64
+ getFileLinkWithLocationRedirectionHandling(getToken, resolvedUrl, logger),
65
65
  "getFileLinkCore",
66
66
  logger,
67
67
  ),
@@ -115,32 +115,41 @@ export async function getFileLink(
115
115
  */
116
116
  async function getFileLinkWithLocationRedirectionHandling(
117
117
  getToken: TokenFetcher<OdspResourceTokenFetchOptions>,
118
- odspUrlParts: IOdspUrlParts,
118
+ resolvedUrl: IOdspResolvedUrl,
119
119
  logger: ITelemetryLoggerExt,
120
120
  ): Promise<string> {
121
121
  // We can have chains of location redirection one after the other, so have a for loop
122
122
  // so that we can keep handling the same type of error. Set max number of redirection to 5.
123
123
  let lastError: unknown;
124
+ let locationRedirected = false;
124
125
  for (let count = 1; count <= 5; count++) {
125
126
  try {
126
- return await getFileLinkCore(getToken, odspUrlParts, logger);
127
+ const fileItem = await getFileItemLite(getToken, resolvedUrl, logger, true);
128
+ // Sometimes the siteUrl in the actual file is different from the siteUrl in the resolvedUrl due to location
129
+ // redirection. This creates issues in the getSharingInformation call. So we need to update the siteUrl in the
130
+ // resolvedUrl to the siteUrl in the fileItem which is the updated siteUrl.
131
+ const oldSiteDomain = new URL(resolvedUrl.siteUrl).origin;
132
+ const newSiteDomain = new URL(fileItem.sharepointIds.siteUrl).origin;
133
+ if (oldSiteDomain !== newSiteDomain) {
134
+ locationRedirected = true;
135
+ logger.sendTelemetryEvent({
136
+ eventName: "LocationRedirectionErrorForGetOdspFileLink",
137
+ retryCount: count,
138
+ });
139
+ renameTenantInOdspResolvedUrl(resolvedUrl, newSiteDomain);
140
+ }
141
+ return await getFileLinkCore(getToken, resolvedUrl, logger, fileItem);
127
142
  } catch (error: unknown) {
128
143
  lastError = error;
144
+ // If the getSharingLink call fails with the 401/403/404 error, then it could be due to that the file has moved
145
+ // to another location. This could occur in case we have more than 1 tenant rename. In that case, we should retry
146
+ // the getFileItemLite call to get the updated fileItem.
129
147
  if (
130
148
  isFluidError(error) &&
131
- error.errorType === OdspErrorTypes.fileNotFoundOrAccessDeniedError &&
132
- hasRedirectionLocation(error) &&
133
- error.redirectLocation !== undefined
149
+ locationRedirected &&
150
+ (error.errorType === OdspErrorTypes.fileNotFoundOrAccessDeniedError ||
151
+ error.errorType === OdspErrorTypes.authorizationError)
134
152
  ) {
135
- const redirectLocation = error.redirectLocation;
136
- logger.sendTelemetryEvent({
137
- eventName: "LocationRedirectionErrorForGetOdspFileLink",
138
- retryCount: count,
139
- });
140
- // Generate the new SiteUrl from the redirection location.
141
- const newSiteDomain = new URL(redirectLocation).origin;
142
- const newSiteUrl = `${newSiteDomain}${new URL(odspUrlParts.siteUrl).pathname}`;
143
- odspUrlParts.siteUrl = newSiteUrl;
144
153
  continue;
145
154
  }
146
155
  throw error;
@@ -153,9 +162,8 @@ async function getFileLinkCore(
153
162
  getToken: TokenFetcher<OdspResourceTokenFetchOptions>,
154
163
  odspUrlParts: IOdspUrlParts,
155
164
  logger: ITelemetryLoggerExt,
165
+ fileItem: FileItemLite,
156
166
  ): Promise<string> {
157
- const fileItem = await getFileItemLite(getToken, odspUrlParts, logger, true);
158
-
159
167
  // ODSP link requires extra call to return link that is resistant to file being renamed or moved to different folder
160
168
  return PerformanceEvent.timedExecAsync(
161
169
  logger,
@@ -193,7 +201,6 @@ async function getFileLinkCore(
193
201
  headers: {
194
202
  "Content-Type": "application/json;odata=verbose",
195
203
  "Accept": "application/json;odata=verbose",
196
- "redirect": "manual",
197
204
  ...headers,
198
205
  },
199
206
  };
@@ -280,7 +287,6 @@ async function getFileItemLite(
280
287
  );
281
288
 
282
289
  const headers = getHeadersWithAuth(authHeader);
283
- headers.redirect = "manual";
284
290
  const requestInit = { method, headers };
285
291
  const response = await fetchHelper(url, requestInit);
286
292
  additionalProps = response.propsToLog;
@@ -301,3 +307,37 @@ async function getFileItemLite(
301
307
  },
302
308
  );
303
309
  }
310
+
311
+ /**
312
+ * It takes a resolved url with old siteUrl and patches resolved url with updated site url domain.
313
+ * @param odspResolvedUrl - Previous odsp resolved url with older site url.
314
+ * @param newSiteDomain - New site domain after the tenant rename.
315
+ */
316
+ function renameTenantInOdspResolvedUrl(
317
+ odspResolvedUrl: IOdspResolvedUrl,
318
+ newSiteDomain: string,
319
+ ): void {
320
+ const newSiteUrl = `${newSiteDomain}${new URL(odspResolvedUrl.siteUrl).pathname}`;
321
+ odspResolvedUrl.siteUrl = newSiteUrl;
322
+
323
+ if (odspResolvedUrl.endpoints.attachmentGETStorageUrl) {
324
+ odspResolvedUrl.endpoints.attachmentGETStorageUrl = `${newSiteDomain}${
325
+ new URL(odspResolvedUrl.endpoints.attachmentGETStorageUrl).pathname
326
+ }`;
327
+ }
328
+ if (odspResolvedUrl.endpoints.attachmentPOSTStorageUrl) {
329
+ odspResolvedUrl.endpoints.attachmentPOSTStorageUrl = `${newSiteDomain}${
330
+ new URL(odspResolvedUrl.endpoints.attachmentPOSTStorageUrl).pathname
331
+ }`;
332
+ }
333
+ if (odspResolvedUrl.endpoints.deltaStorageUrl) {
334
+ odspResolvedUrl.endpoints.deltaStorageUrl = `${newSiteDomain}${
335
+ new URL(odspResolvedUrl.endpoints.deltaStorageUrl).pathname
336
+ }`;
337
+ }
338
+ if (odspResolvedUrl.endpoints.snapshotStorageUrl) {
339
+ odspResolvedUrl.endpoints.snapshotStorageUrl = `${newSiteDomain}${
340
+ new URL(odspResolvedUrl.endpoints.snapshotStorageUrl).pathname
341
+ }`;
342
+ }
343
+ }
@@ -6,4 +6,4 @@
6
6
  */
7
7
 
8
8
  export const pkgName = "@fluidframework/odsp-driver";
9
- export const pkgVersion = "2.0.7";
9
+ export const pkgVersion = "2.0.9";