1+ 'use client' ;
12
23import {
34 Federation ,
67 ObjectList ,
78 TokenPermission
89} from "./types"
10+ import {
11+ cloneDeep
12+ } from 'lodash-es' ;
913import {
1014 fetchNamespace ,
1115 getObjectToken ,
@@ -24,14 +28,14 @@ import sessionObject, {ProxiedValue} from "./sessionObject";
2428
2529export default class Client {
2630
27- federations : Record < string , Federation >
31+ federations : ProxiedValue < Record < string , Federation > >
2832 prefixToNamespace : Record < string , string >
2933 requestQueue : ProxiedValue < QueuedRequest [ ] >
3034 codeVerifier : ProxiedValue < string >
3135
3236 constructor ( ) {
3337 // Set up and load/initialize session storage objects
34- this . federations = sessionObject < Record < string , Federation > > ( "federations" )
38+ this . federations = sessionObject < ProxiedValue < Record < string , Federation > > > ( "federations" , { value : { } } )
3539 this . prefixToNamespace = sessionObject < Record < string , string > > ( 'prefixToNamespace' )
3640 this . requestQueue = sessionObject < ProxiedValue < QueuedRequest [ ] > > ( "requestQueue" , { value : [ ] } )
3741 this . codeVerifier = sessionObject < ProxiedValue < string > > ( "codeVerifier" , { value : generateCodeVerifier ( ) } )
@@ -48,8 +52,8 @@ export default class Client {
4852
4953 const { federationHostname, objectPath} = parsePelicanObjectUrl ( objectUrl )
5054 const federation = await this . getFederation ( federationHostname )
51- const namespace = await this . getNamespace ( objectPath , federation )
52- const token = await getObjectToken ( objectPath , namespace )
55+ const namespace = await this . getNamespace ( objectUrl , federation )
56+ const token = await getObjectToken ( namespace )
5357
5458 const objectHttpUrl = new URL ( `${ federation . configuration . director_endpoint } ${ objectPath } ` )
5559 const response = await fetch ( objectHttpUrl , {
@@ -63,17 +67,17 @@ export default class Client {
6367
6468 // If we get a 403, queue this request call it after getting a token
6569 } else if ( response . status === 403 && ! token ) {
66- await this . queueRequestAndStartFlow ( objectPath , federation )
70+ await this . queueRequestAndStartFlow ( objectUrl , federation )
6771 } else {
6872 throw new Error ( `Could not get object: ${ response . status } ${ response . statusText } ` )
6973 }
7074 }
7175
72- async list ( federationPath : string ) : Promise < ObjectList [ ] | undefined > {
73- const { federationHostname, objectPath} = parsePelicanObjectUrl ( federationPath )
76+ async list ( collectionUrl : string ) : Promise < ObjectList [ ] | undefined > {
77+ const { federationHostname, objectPath} = parsePelicanObjectUrl ( collectionUrl )
7478 const federation = await this . getFederation ( federationHostname )
75- const namespace = await this . getNamespace ( objectPath , federation )
76- const token = await getObjectToken ( objectPath , namespace )
79+ const namespace = await this . getNamespace ( collectionUrl , federation )
80+ const token = await getObjectToken ( namespace )
7781
7882 const objectHttpUrl = new URL ( `${ federation . configuration . director_endpoint } ${ objectPath } ` )
7983 const response = await fetch ( objectHttpUrl , {
@@ -89,7 +93,7 @@ export default class Client {
8993
9094 // If we get a 403, queue this request call it after getting a token
9195 } else if ( response . status === 403 ) {
92- await this . queueRequestAndStartFlow ( objectPath , federation )
96+ await this . queueRequestAndStartFlow ( collectionUrl , federation )
9397 } else {
9498 throw new Error ( `Could not list directory: ${ response . status } ${ response . statusText } ` )
9599 }
@@ -99,8 +103,8 @@ export default class Client {
99103
100104 const { federationHostname, objectPath } = parsePelicanObjectUrl ( objectUrl )
101105 const federation = await this . getFederation ( federationHostname )
102- const namespace = await this . getNamespace ( objectPath , federation )
103- const token = await getObjectToken ( objectPath , namespace )
106+ const namespace = await this . getNamespace ( objectUrl , federation )
107+ const token = await getObjectToken ( namespace )
104108
105109 const objectHttpUrl = new URL ( `${ federation . configuration . director_endpoint } ${ objectPath } ` )
106110 const response = await fetch ( objectHttpUrl , {
@@ -114,7 +118,7 @@ export default class Client {
114118 if ( response . status === 200 || response . status === 201 ) {
115119 return
116120 } else if ( response . status === 403 ) {
117- await this . queueRequestAndStartFlow ( objectPath , federation )
121+ await this . queueRequestAndStartFlow ( objectUrl , federation )
118122 } else {
119123 throw new Error ( `Could not upload object: ${ response . status } ${ response . statusText } ` )
120124 }
@@ -125,10 +129,10 @@ export default class Client {
125129 * @param objectUrl
126130 */
127131 async permissions ( objectUrl : string ) : Promise < TokenPermission [ ] > {
128- const { federationHostname, objectPath } = parsePelicanObjectUrl ( objectUrl )
132+ const { federationHostname } = parsePelicanObjectUrl ( objectUrl )
129133 const federation = await this . getFederation ( federationHostname )
130- const namespace = await this . getNamespace ( objectPath , federation )
131- const token = await getObjectToken ( objectPath , namespace )
134+ const namespace = await this . getNamespace ( objectUrl , federation )
135+ const token = await getObjectToken ( namespace )
132136
133137 if ( ! token ) return [ ]
134138
@@ -148,13 +152,15 @@ export default class Client {
148152 try {
149153 // Get the namespace and federation from the state parameter
150154 const { federation : federationHostname , namespace : namespacePrefix } = parseOauthState ( new URL ( window . location . href ) )
151- const namespace = this . federations [ federationHostname ] ?. namespaces [ namespacePrefix ]
155+ const namespace = this . federations . value [ federationHostname ] ?. namespaces [ namespacePrefix ]
152156
153157 // Check if we have an auth code to exchange for a token
154158 const token = await getToken ( namespace . oidcConfiguration , this . codeVerifier . value , namespace . clientId , namespace . clientSecret , authCode )
155159
156160 // Save the token to the namespace
157- this . federations [ federationHostname ] . namespaces [ namespacePrefix ] . token = token . accessToken
161+ const federationsClone = cloneDeep ( this . federations . value )
162+ federationsClone [ federationHostname ] . namespaces [ namespacePrefix ] . token = token . accessToken
163+ this . federations . value = federationsClone
158164 } catch { }
159165
160166 // Clean up the window
@@ -166,20 +172,23 @@ export default class Client {
166172 * @param federationHostname
167173 */
168174 async getFederation ( federationHostname : string ) : Promise < Federation > {
169- if ( ! this . federations ?. [ federationHostname ] ) {
170- this . federations [ federationHostname ] = await fetchFederationConfiguration ( federationHostname )
175+ if ( ! this . federations . value ?. [ federationHostname ] ) {
176+ this . federations . value = {
177+ ...this . federations . value ,
178+ [ federationHostname ] : await fetchFederationConfiguration ( federationHostname )
179+ }
171180 }
172- return this . federations [ federationHostname ]
181+ return this . federations . value [ federationHostname ]
173182 }
174183
175184 /**
176185 * Get a namespace's configuration from the federation and save it to session cache
177- * @param objectPath
186+ * @param objectUrl
178187 * @param federation
179188 */
180- async getNamespace ( objectPath : string , federation : Federation ) : Promise < Namespace > {
189+ async getNamespace ( objectUrl : string , federation : Federation ) : Promise < Namespace > {
181190
182- const { objectPrefix} = parsePelicanObjectUrl ( objectPath )
191+ const { objectPrefix, objectPath } = parsePelicanObjectUrl ( objectUrl )
183192
184193 // Check if we have already cached the namespace for this prefix
185194 if ( this . prefixToNamespace ?. [ objectPrefix ] ) {
@@ -192,33 +201,37 @@ export default class Client {
192201 // Fetch and save the namespace information
193202 const requestNamespace = await fetchNamespace ( objectPath , federation )
194203 this . prefixToNamespace [ objectPrefix ] = requestNamespace . prefix
195- this . federations [ federation . hostname ] . namespaces [ requestNamespace . prefix ] = requestNamespace
204+ const federationsClone = cloneDeep ( this . federations . value )
205+ federationsClone [ federation . hostname ] . namespaces [ requestNamespace . prefix ] = requestNamespace
206+ this . federations . value = federationsClone
207+
196208 return requestNamespace
197209 }
198210
199211 /**
200212 * Queue a request that needs authorization and start the authorization code flow
201- * @param objectPath
213+ * @param objectUrl
202214 * @param federation
203215 */
204- async queueRequestAndStartFlow ( objectPath : string , federation : Federation ) : Promise < void > {
216+ async queueRequestAndStartFlow ( objectUrl : string , federation : Federation ) : Promise < void > {
205217
206218 // Fetch and save the namespace information
207- const requestNamespace = await fetchNamespace ( objectPath , federation )
208- this . federations [ federation . hostname ] . namespaces [ requestNamespace . prefix ] = requestNamespace
219+ const namespace = await this . getNamespace ( objectUrl , federation )
220+
221+ const { objectPath} = parsePelicanObjectUrl ( objectUrl )
209222
210223 // Queue the file request
211224 this . requestQueue . value . push ( {
212- objectUrl : `pelican:// ${ federation . hostname } ${ objectPath } ` ,
225+ objectUrl,
213226 federationHostname : federation . hostname ,
214227 path : objectPath ,
215- namespace : requestNamespace . prefix ,
228+ namespace : namespace . prefix ,
216229 type : "GET" ,
217230 createdAt : new Date ( ) . getTime ( )
218231 } )
219232
220233 // Start the authorization code flow
221- await this . startAuthorizationCodeFlow ( objectPath , requestNamespace , federation )
234+ await this . startAuthorizationCodeFlow ( objectPath , namespace , federation )
222235 }
223236
224237 /**
0 commit comments