1+ import { ApiConfig , DataverseApiAuthMechanism } from '../../../core/infra/repositories/ApiConfig'
2+ import { WriteError } from '../../../core/domain/repositories/WriteError'
3+ import { ApiConstants } from '../../../core/infra/repositories/ApiConstants'
14import { ApiRepository } from '../../../core/infra/repositories/ApiRepository'
5+ import {
6+ buildRequestConfig ,
7+ buildRequestUrl
8+ } from '../../../core/infra/repositories/apiConfigBuilders'
29import { GuestbookResponseDTO } from '../../domain/dtos/GuestbookResponseDTO'
310import { IAccessRepository } from '../../domain/repositories/IAccessRepository'
411
@@ -13,14 +20,7 @@ export class AccessRepository extends ApiRepository implements IAccessRepository
1320 const endpoint = this . buildApiEndpoint ( `${ this . accessResourceName } /datafile` , undefined , fileId )
1421 const queryParams = format ? { signed : true , format } : { signed : true }
1522
16- return this . doPost ( endpoint , guestbookResponse , queryParams )
17- . then ( ( response ) => {
18- const signedUrl = response . data . data . signedUrl
19- return signedUrl
20- } )
21- . catch ( ( error ) => {
22- throw error
23- } )
23+ return await this . submitGuestbookDownload ( endpoint , guestbookResponse , queryParams )
2424 }
2525
2626 public async submitGuestbookForDatafilesDownload (
@@ -30,21 +30,14 @@ export class AccessRepository extends ApiRepository implements IAccessRepository
3030 ) : Promise < string > {
3131 const queryParams = format ? { signed : true , format } : { signed : true }
3232
33- return this . doPost (
33+ return await this . submitGuestbookDownload (
3434 this . buildApiEndpoint (
3535 this . accessResourceName ,
3636 `datafiles/${ Array . isArray ( fileIds ) ? fileIds . join ( ',' ) : fileIds } `
3737 ) ,
3838 guestbookResponse ,
3939 queryParams
4040 )
41- . then ( ( response ) => {
42- const signedUrl = response . data . data . signedUrl
43- return signedUrl
44- } )
45- . catch ( ( error ) => {
46- throw error
47- } )
4841 }
4942
5043 public async submitGuestbookForDatasetDownload (
@@ -59,14 +52,7 @@ export class AccessRepository extends ApiRepository implements IAccessRepository
5952 )
6053 const queryParams = format ? { signed : true , format } : { signed : true }
6154
62- return this . doPost ( endpoint , guestbookResponse , queryParams )
63- . then ( ( response ) => {
64- const signedUrl = response . data . data . signedUrl
65- return signedUrl
66- } )
67- . catch ( ( error ) => {
68- throw error
69- } )
55+ return await this . submitGuestbookDownload ( endpoint , guestbookResponse , queryParams )
7056 }
7157
7258 public async submitGuestbookForDatasetVersionDownload (
@@ -82,13 +68,112 @@ export class AccessRepository extends ApiRepository implements IAccessRepository
8268 )
8369 const queryParams = format ? { signed : true , format } : { signed : true }
8470
85- return this . doPost ( endpoint , guestbookResponse , queryParams )
86- . then ( ( response ) => {
87- const signedUrl = response . data . data . signedUrl
88- return signedUrl
89- } )
90- . catch ( ( error ) => {
91- throw error
92- } )
71+ return await this . submitGuestbookDownload ( endpoint , guestbookResponse , queryParams )
72+ }
73+
74+ private async submitGuestbookDownload (
75+ apiEndpoint : string ,
76+ guestbookResponse : GuestbookResponseDTO ,
77+ queryParams : object
78+ ) : Promise < string > {
79+ const requestConfig = buildRequestConfig (
80+ true ,
81+ queryParams ,
82+ ApiConstants . CONTENT_TYPE_APPLICATION_JSON
83+ )
84+ const response = await fetch (
85+ this . buildUrlWithQueryParams ( buildRequestUrl ( apiEndpoint ) , queryParams ) ,
86+ {
87+ method : 'POST' ,
88+ headers : this . buildFetchHeaders ( requestConfig . headers ) ,
89+ credentials : this . getFetchCredentials ( requestConfig . withCredentials ) ,
90+ body : JSON . stringify ( guestbookResponse )
91+ }
92+ ) . catch ( ( error ) => {
93+ throw new WriteError ( error instanceof Error ? error . message : String ( error ) )
94+ } )
95+
96+ const responseData = await this . parseResponseBody ( response )
97+
98+ if ( ! response . ok ) {
99+ throw new WriteError ( this . buildFetchErrorMessage ( response . status , responseData ) )
100+ }
101+
102+ return this . getSignedUrlOrThrow ( responseData )
103+ }
104+
105+ private getFetchCredentials ( withCredentials ?: boolean ) : RequestCredentials | undefined {
106+ if ( ApiConfig . dataverseApiAuthMechanism === DataverseApiAuthMechanism . BEARER_TOKEN ) {
107+ return 'omit'
108+ }
109+
110+ if ( withCredentials ) {
111+ return 'include'
112+ }
113+
114+ return undefined
115+ }
116+
117+ private buildUrlWithQueryParams ( requestUrl : string , queryParams : object ) : string {
118+ const url = new URL ( requestUrl )
119+
120+ Object . entries ( queryParams ) . forEach ( ( [ key , value ] ) => {
121+ if ( value !== undefined && value !== null ) {
122+ url . searchParams . append ( key , String ( value ) )
123+ }
124+ } )
125+
126+ return url . toString ( )
127+ }
128+
129+ private buildFetchHeaders ( headers ?: Record < string , unknown > ) : Record < string , string > {
130+ const fetchHeaders : Record < string , string > = { }
131+
132+ if ( ! headers ) {
133+ return fetchHeaders
134+ }
135+
136+ Object . entries ( headers ) . forEach ( ( [ key , value ] ) => {
137+ if ( value !== undefined ) {
138+ fetchHeaders [ key ] = String ( value )
139+ }
140+ } )
141+
142+ return fetchHeaders
143+ }
144+
145+ private async parseResponseBody ( response : Response ) : Promise < any > {
146+ const contentType = response . headers . get ( 'content-type' ) ?? ''
147+
148+ if ( contentType . includes ( 'application/json' ) ) {
149+ return await response . json ( )
150+ }
151+
152+ const responseText = await response . text ( )
153+
154+ try {
155+ return JSON . parse ( responseText )
156+ } catch {
157+ return responseText
158+ }
159+ }
160+
161+ private buildFetchErrorMessage ( status : number , responseData : any ) : string {
162+ const message =
163+ typeof responseData === 'string'
164+ ? responseData
165+ : responseData ?. message || responseData ?. data ?. message || 'unknown error'
166+
167+ return `[${ status } ] ${ message } `
168+ }
169+
170+ private getSignedUrlOrThrow ( responseData : any ) : string {
171+ const signedUrl = responseData ?. data ?. signedUrl
172+
173+ if ( typeof signedUrl !== 'string' || signedUrl . length === 0 ) {
174+ throw new WriteError ( 'Missing signedUrl in access download response.' )
175+ }
176+
177+ return signedUrl
93178 }
94179}
0 commit comments