66import type { ITelemetryBaseProperties } from "@fluidframework/core-interfaces" ;
77import { assert } from "@fluidframework/core-utils/internal" ;
88import { NonRetryableError , runWithRetry } from "@fluidframework/driver-utils/internal" ;
9- import { hasRedirectionLocation } from "@fluidframework/odsp-doclib-utils/internal" ;
109import {
1110 IOdspUrlParts ,
1211 OdspErrorTypes ,
1312 OdspResourceTokenFetchOptions ,
1413 TokenFetcher ,
14+ type IOdspResolvedUrl ,
1515} from "@fluidframework/odsp-driver-definitions/internal" ;
1616import {
1717 ITelemetryLoggerExt ,
@@ -44,10 +44,10 @@ const fileLinkCache = new Map<string, Promise<string>>();
4444 */
4545export async function getFileLink (
4646 getToken : TokenFetcher < OdspResourceTokenFetchOptions > ,
47- odspUrlParts : IOdspUrlParts ,
47+ resolvedUrl : IOdspResolvedUrl ,
4848 logger : ITelemetryLoggerExt ,
4949) : Promise < string > {
50- const cacheKey = `${ odspUrlParts . siteUrl } _${ odspUrlParts . driveId } _${ odspUrlParts . itemId } ` ;
50+ const cacheKey = `${ resolvedUrl . siteUrl } _${ resolvedUrl . driveId } _${ resolvedUrl . itemId } ` ;
5151 const maybeFileLinkCacheEntry = fileLinkCache . get ( cacheKey ) ;
5252 if ( maybeFileLinkCacheEntry !== undefined ) {
5353 return maybeFileLinkCacheEntry ;
@@ -61,7 +61,7 @@ export async function getFileLink(
6161 async ( ) =>
6262 runWithRetryForCoherencyAndServiceReadOnlyErrors (
6363 async ( ) =>
64- getFileLinkWithLocationRedirectionHandling ( getToken , odspUrlParts , logger ) ,
64+ getFileLinkWithLocationRedirectionHandling ( getToken , resolvedUrl , logger ) ,
6565 "getFileLinkCore" ,
6666 logger ,
6767 ) ,
@@ -116,32 +116,41 @@ export async function getFileLink(
116116 */
117117async function getFileLinkWithLocationRedirectionHandling (
118118 getToken : TokenFetcher < OdspResourceTokenFetchOptions > ,
119- odspUrlParts : IOdspUrlParts ,
119+ resolvedUrl : IOdspResolvedUrl ,
120120 logger : ITelemetryLoggerExt ,
121121) : Promise < string > {
122122 // We can have chains of location redirection one after the other, so have a for loop
123123 // so that we can keep handling the same type of error. Set max number of redirection to 5.
124124 let lastError : unknown ;
125+ let locationRedirected = false ;
125126 for ( let count = 1 ; count <= 5 ; count ++ ) {
126127 try {
127- return await getFileLinkCore ( getToken , odspUrlParts , logger ) ;
128+ const fileItem = await getFileItemLite ( getToken , resolvedUrl , logger , true ) ;
129+ // Sometimes the siteUrl in the actual file is different from the siteUrl in the resolvedUrl due to location
130+ // redirection. This creates issues in the getSharingInformation call. So we need to update the siteUrl in the
131+ // resolvedUrl to the siteUrl in the fileItem which is the updated siteUrl.
132+ const oldSiteDomain = new URL ( resolvedUrl . siteUrl ) . origin ;
133+ const newSiteDomain = new URL ( fileItem . sharepointIds . siteUrl ) . origin ;
134+ if ( oldSiteDomain !== newSiteDomain ) {
135+ locationRedirected = true ;
136+ logger . sendTelemetryEvent ( {
137+ eventName : "LocationRedirectionErrorForGetOdspFileLink" ,
138+ retryCount : count ,
139+ } ) ;
140+ renameTenantInOdspResolvedUrl ( resolvedUrl , newSiteDomain ) ;
141+ }
142+ return await getFileLinkCore ( getToken , resolvedUrl , logger , fileItem ) ;
128143 } catch ( error : unknown ) {
129144 lastError = error ;
145+ // If the getSharingLink call fails with the 401/403/404 error, then it could be due to that the file has moved
146+ // to another location. This could occur in case we have more than 1 tenant rename. In that case, we should retry
147+ // the getFileItemLite call to get the updated fileItem.
130148 if (
131149 isFluidError ( error ) &&
132- error . errorType === OdspErrorTypes . fileNotFoundOrAccessDeniedError &&
133- hasRedirectionLocation ( error ) &&
134- error . redirectLocation !== undefined
150+ locationRedirected &&
151+ ( error . errorType === OdspErrorTypes . fileNotFoundOrAccessDeniedError ||
152+ error . errorType === OdspErrorTypes . authorizationError )
135153 ) {
136- const redirectLocation = error . redirectLocation ;
137- logger . sendTelemetryEvent ( {
138- eventName : "LocationRedirectionErrorForGetOdspFileLink" ,
139- retryCount : count ,
140- } ) ;
141- // Generate the new SiteUrl from the redirection location.
142- const newSiteDomain = new URL ( redirectLocation ) . origin ;
143- const newSiteUrl = `${ newSiteDomain } ${ new URL ( odspUrlParts . siteUrl ) . pathname } ` ;
144- odspUrlParts . siteUrl = newSiteUrl ;
145154 continue ;
146155 }
147156 throw error ;
@@ -154,9 +163,8 @@ async function getFileLinkCore(
154163 getToken : TokenFetcher < OdspResourceTokenFetchOptions > ,
155164 odspUrlParts : IOdspUrlParts ,
156165 logger : ITelemetryLoggerExt ,
166+ fileItem : FileItemLite ,
157167) : Promise < string > {
158- const fileItem = await getFileItemLite ( getToken , odspUrlParts , logger , true ) ;
159-
160168 // ODSP link requires extra call to return link that is resistant to file being renamed or moved to different folder
161169 return PerformanceEvent . timedExecAsync (
162170 logger ,
@@ -194,7 +202,6 @@ async function getFileLinkCore(
194202 headers : {
195203 "Content-Type" : "application/json;odata=verbose" ,
196204 "Accept" : "application/json;odata=verbose" ,
197- "redirect" : "manual" ,
198205 ...headers ,
199206 } ,
200207 } ;
@@ -281,7 +288,6 @@ async function getFileItemLite(
281288 ) ;
282289
283290 const headers = getHeadersWithAuth ( authHeader ) ;
284- headers . redirect = "manual" ;
285291 const requestInit = { method, headers } ;
286292 const response = await fetchHelper ( url , requestInit ) ;
287293 additionalProps = response . propsToLog ;
@@ -302,3 +308,29 @@ async function getFileItemLite(
302308 } ,
303309 ) ;
304310}
311+
312+ /**
313+ * It takes a resolved url with old siteUrl and patches resolved url with updated site url domain.
314+ * @param odspResolvedUrl - Previous odsp resolved url with older site url.
315+ * @param newSiteDomain - New site domain after the tenant rename.
316+ */
317+ function renameTenantInOdspResolvedUrl (
318+ odspResolvedUrl : IOdspResolvedUrl ,
319+ newSiteDomain : string ,
320+ ) : void {
321+ const newSiteUrl = `${ newSiteDomain } ${ new URL ( odspResolvedUrl . siteUrl ) . pathname } ` ;
322+ odspResolvedUrl . siteUrl = newSiteUrl ;
323+
324+ if ( odspResolvedUrl . endpoints . attachmentGETStorageUrl ) {
325+ odspResolvedUrl . endpoints . attachmentGETStorageUrl = `${ newSiteDomain } ${ new URL ( odspResolvedUrl . endpoints . attachmentGETStorageUrl ) . pathname } ` ;
326+ }
327+ if ( odspResolvedUrl . endpoints . attachmentPOSTStorageUrl ) {
328+ odspResolvedUrl . endpoints . attachmentPOSTStorageUrl = `${ newSiteDomain } ${ new URL ( odspResolvedUrl . endpoints . attachmentPOSTStorageUrl ) . pathname } ` ;
329+ }
330+ if ( odspResolvedUrl . endpoints . deltaStorageUrl ) {
331+ odspResolvedUrl . endpoints . deltaStorageUrl = `${ newSiteDomain } ${ new URL ( odspResolvedUrl . endpoints . deltaStorageUrl ) . pathname } ` ;
332+ }
333+ if ( odspResolvedUrl . endpoints . snapshotStorageUrl ) {
334+ odspResolvedUrl . endpoints . snapshotStorageUrl = `${ newSiteDomain } ${ new URL ( odspResolvedUrl . endpoints . snapshotStorageUrl ) . pathname } ` ;
335+ }
336+ }
0 commit comments