1717
1818import { SHOULD_ADD_AUTH_TOKEN } from '@alfresco/adf-core/auth' ;
1919import { Emitters as JsApiEmitters , HttpClient as JsApiHttpClient } from '@alfresco/js-api' ;
20- import {
21- HttpClient ,
22- HttpContext ,
23- HttpErrorResponse ,
24- HttpEvent ,
25- HttpHeaders ,
26- HttpParams ,
27- HttpResponse
28- } from '@angular/common/http' ;
20+ import { HttpClient , HttpContext , HttpErrorResponse , HttpEvent , HttpHeaders , HttpParams , HttpResponse } from '@angular/common/http' ;
2921import { Injectable } from '@angular/core' ;
3022import { Observable , of , Subject , throwError } from 'rxjs' ;
3123import { catchError , map , takeUntil } from 'rxjs/operators' ;
@@ -52,8 +44,7 @@ export interface Emitters {
5244@Injectable ( {
5345 providedIn : 'root'
5446} )
55- export class AdfHttpClient implements ee . Emitter , JsApiHttpClient {
56-
47+ export class AdfHttpClient implements ee . Emitter , JsApiHttpClient {
5748 on : ee . EmitterMethod ;
5849 off : ee . EmitterMethod ;
5950 once : ee . EmitterMethod ;
@@ -107,47 +98,43 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
10798 const params = getQueryParamsWithCustomEncoder ( options . queryParams , new AlfrescoApiParamEncoder ( ) ) ;
10899 const responseType = AdfHttpClient . getResponseType ( options ) ;
109100 const context = new HttpContext ( ) . set ( SHOULD_ADD_AUTH_TOKEN , true ) ;
110- const security : SecurityOptions = { ...this . defaultSecurityOptions , ...sc } ;
101+ const security : SecurityOptions = { ...this . defaultSecurityOptions , ...sc } ;
111102 const headers = this . getHeaders ( options ) ;
112103 if ( ! emitters ) {
113104 emitters = this . getEventEmitters ( ) ;
114105 }
115106
116- const request = this . httpClient . request (
117- options . httpMethod ,
118- url ,
119- {
120- context,
121- ...( body && { body} ) ,
122- ...( responseType && { responseType} ) ,
123- ...security ,
124- ...( params && { params} ) ,
125- headers,
126- observe : 'events' ,
127- reportProgress : true
128- }
129- ) ;
107+ const request = this . httpClient . request ( options . httpMethod , url , {
108+ context,
109+ ...( body && { body } ) ,
110+ ...( responseType && { responseType } ) ,
111+ ...security ,
112+ ...( params && { params } ) ,
113+ headers,
114+ observe : 'events' ,
115+ reportProgress : true
116+ } ) ;
130117
131118 return this . requestWithLegacyEventEmitters < T > ( request , emitters , options . returnType ) ;
132119 }
133120
134121 post < T = any > ( url : string , options ?: RequestOptions , sc ?: SecurityOptions , emitters ?: JsApiEmitters ) : Promise < T > {
135- return this . request < T > ( url , { ...options , httpMethod : 'POST' } , sc , emitters ) ;
122+ return this . request < T > ( url , { ...options , httpMethod : 'POST' } , sc , emitters ) ;
136123 }
137124
138125 put < T = any > ( url : string , options ?: RequestOptions , sc ?: SecurityOptions , emitters ?: JsApiEmitters ) : Promise < T > {
139- return this . request < T > ( url , { ...options , httpMethod : 'PUT' } , sc , emitters ) ;
126+ return this . request < T > ( url , { ...options , httpMethod : 'PUT' } , sc , emitters ) ;
140127 }
141128
142129 get < T = any > ( url : string , options ?: RequestOptions , sc ?: SecurityOptions , emitters ?: JsApiEmitters ) : Promise < T > {
143- return this . request < T > ( url , { ...options , httpMethod : 'GET' } , sc , emitters ) ;
130+ return this . request < T > ( url , { ...options , httpMethod : 'GET' } , sc , emitters ) ;
144131 }
145132
146133 delete < T = void > ( url : string , options ?: RequestOptions , sc ?: SecurityOptions , emitters ?: JsApiEmitters ) : Promise < T > {
147- return this . request < T > ( url , { ...options , httpMethod : 'DELETE' } , sc , emitters ) ;
134+ return this . request < T > ( url , { ...options , httpMethod : 'DELETE' } , sc , emitters ) ;
148135 }
149136
150- private addPromiseListeners < T = any > ( promise : Promise < T > , eventEmitter : any ) {
137+ private addPromiseListeners < T = any > ( promise : Promise < T > , eventEmitter : any ) {
151138 const eventPromise = Object . assign ( promise , {
152139 on ( ) {
153140 // eslint-disable-next-line prefer-spread, prefer-rest-params
@@ -189,58 +176,59 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
189176 }
190177
191178 private requestWithLegacyEventEmitters < T = any > ( request$ : Observable < HttpEvent < T > > , emitters : JsApiEmitters , returnType : any ) : Promise < T > {
192-
193179 const abort$ = new Subject < void > ( ) ;
194- const { eventEmitter, apiClientEmitter} = emitters ;
195-
196- const promise = request$ . pipe (
197- map ( ( res ) => {
198- if ( isHttpUploadProgressEvent ( res ) ) {
199- const percent = Math . round ( ( res . loaded / res . total ) * 100 ) ;
200- eventEmitter . emit ( 'progress' , { loaded : res . loaded , total : res . total , percent} ) ;
201- }
202-
203- if ( isHttpResponseEvent ( res ) ) {
204- eventEmitter . emit ( 'success' , res . body ) ;
205- return AdfHttpClient . deserialize ( res , returnType ) ;
206- }
207- } ) ,
208- catchError ( ( err : HttpErrorResponse ) : Observable < AlfrescoApiResponseError > => {
209-
210- // since we can't always determinate ahead of time if the response is going to be xml or plain text response
211- // we need to handle false positive cases here.
212-
213- if ( err . status === 200 ) {
214- eventEmitter . emit ( 'success' , err . error . text ) ;
215- return of ( err . error . text ) ;
216- }
217-
218- eventEmitter . emit ( 'error' , err ) ;
219- apiClientEmitter . emit ( 'error' , { ...err , response : { req : err } } ) ;
220-
221- if ( err . status === 401 ) {
222- eventEmitter . emit ( 'unauthorized' ) ;
223- apiClientEmitter . emit ( 'unauthorized' ) ;
224- }
225-
226- // for backwards compatibility we need to convert it to error class as the HttpErrorResponse only implements Error interface, not extending it,
227- // and we need to be able to correctly pass instanceof Error conditions used inside repository
228- // we also need to pass error as Stringify string as we are detecting statusCodes using JSON.parse(error.message) in some places
229- const msg = typeof err . error === 'string' ? err . error : JSON . stringify ( err . error ) ;
230-
231- // for backwards compatibility to handle cases in code where we try read response.error.response.body;
232-
233- const error = {
234- ...err , body : err . error
235- } ;
236-
237- const alfrescoApiError = new AlfrescoApiResponseError ( msg , err . status , error ) ;
238- return throwError ( alfrescoApiError ) ;
239- } ) ,
240- takeUntil ( abort$ )
241- ) . toPromise ( ) ;
242-
243- ( promise as any ) . abort = function ( ) {
180+ const { eventEmitter, apiClientEmitter } = emitters ;
181+
182+ const promise = request$
183+ . pipe (
184+ map ( ( res ) => {
185+ if ( isHttpUploadProgressEvent ( res ) ) {
186+ const percent = Math . round ( ( res . loaded / res . total ) * 100 ) ;
187+ eventEmitter . emit ( 'progress' , { loaded : res . loaded , total : res . total , percent } ) ;
188+ }
189+
190+ if ( isHttpResponseEvent ( res ) ) {
191+ eventEmitter . emit ( 'success' , res . body ) ;
192+ return AdfHttpClient . deserialize ( res , returnType ) ;
193+ }
194+ } ) ,
195+ catchError ( ( err : HttpErrorResponse ) : Observable < AlfrescoApiResponseError > => {
196+ // since we can't always determinate ahead of time if the response is going to be xml or plain text response
197+ // we need to handle false positive cases here.
198+
199+ if ( err . status === 200 ) {
200+ eventEmitter . emit ( 'success' , err . error . text ) ;
201+ return of ( err . error . text ) ;
202+ }
203+
204+ eventEmitter . emit ( 'error' , err ) ;
205+ apiClientEmitter . emit ( 'error' , { ...err , response : { req : err } } ) ;
206+
207+ if ( err . status === 401 ) {
208+ eventEmitter . emit ( 'unauthorized' ) ;
209+ apiClientEmitter . emit ( 'unauthorized' ) ;
210+ }
211+
212+ // for backwards compatibility we need to convert it to error class as the HttpErrorResponse only implements Error interface, not extending it,
213+ // and we need to be able to correctly pass instanceof Error conditions used inside repository
214+ // we also need to pass error as Stringify string as we are detecting statusCodes using JSON.parse(error.message) in some places
215+ const msg = typeof err . error === 'string' ? err . error : JSON . stringify ( err . error ) ;
216+
217+ // for backwards compatibility to handle cases in code where we try read response.error.response.body;
218+
219+ const error = {
220+ ...err ,
221+ body : err . error
222+ } ;
223+
224+ const alfrescoApiError = new AlfrescoApiResponseError ( msg , err . status , error ) ;
225+ return throwError ( alfrescoApiError ) ;
226+ } ) ,
227+ takeUntil ( abort$ )
228+ )
229+ . toPromise ( ) ;
230+
231+ ( promise as any ) . abort = function ( ) {
244232 eventEmitter . emit ( 'abort' ) ;
245233 abort$ . next ( ) ;
246234 abort$ . complete ( ) ;
@@ -261,7 +249,7 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
261249 }
262250
263251 if ( isFormUrlEncoded ) {
264- return new HttpParams ( { fromObject : removeNilValues ( options . formParams ) } ) ;
252+ return new HttpParams ( { fromObject : removeNilValues ( options . formParams ) } ) ;
265253 }
266254
267255 return body ;
@@ -273,8 +261,8 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
273261
274262 const optionsHeaders = {
275263 ...options . headerParams ,
276- ...( accept && { Accept : accept } ) ,
277- ...( ( contentType ) && { 'Content-Type' : contentType } )
264+ ...( accept && { Accept : accept } ) ,
265+ ...( contentType && { 'Content-Type' : contentType } )
278266 } ;
279267
280268 if ( ! this . disableCsrf ) {
@@ -319,7 +307,6 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
319307 return Boolean ( contentType ?. match ( / ^ a p p l i c a t i o n \/ j s o n ( ; .* ) ? $ / i) ) ;
320308 }
321309
322-
323310 private setCsrfToken ( optionsHeaders : any ) {
324311 const token = this . createCSRFToken ( ) ;
325312 optionsHeaders [ 'X-CSRF-TOKEN' ] = token ;
@@ -332,12 +319,16 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
332319 }
333320
334321 private createCSRFToken ( a ?: any ) : string {
335- const randomValue = window . crypto . getRandomValues ( new Uint32Array ( 1 ) ) [ 0 ] ;
322+ const randomValue = AdfHttpClient . getSecureRandomValue ( ) ;
336323 return a ? ( a ^ ( ( randomValue * 16 ) >> ( a / 4 ) ) ) . toString ( 16 ) : ( [ 1e16 ] + ( 1e16 ) . toString ( ) ) . replace ( / [ 0 1 ] / g, this . createCSRFToken ) ;
337324 }
338325
339- private static getResponseType ( options : RequestOptions ) : 'blob' | 'json' | 'text' {
326+ private static getSecureRandomValue ( ) : number {
327+ const max = Math . pow ( 2 , 32 ) ;
328+ return window . crypto . getRandomValues ( new Uint32Array ( 1 ) ) [ 0 ] / max ;
329+ }
340330
331+ private static getResponseType ( options : RequestOptions ) : 'blob' | 'json' | 'text' {
341332 const isBlobType = options . returnType ?. toString ( ) . toLowerCase ( ) === 'blob' || options . responseType ?. toString ( ) . toLowerCase ( ) === 'blob' ;
342333
343334 if ( isBlobType ) {
@@ -359,7 +350,6 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
359350 * @returns deserialized object
360351 */
361352 private static deserialize < T > ( response : HttpResponse < T > , returnType ?: Constructor < unknown > | 'blob' ) : any {
362-
363353 if ( response === null ) {
364354 return null ;
365355 }
@@ -390,9 +380,7 @@ export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
390380 return new returnType ( body ) ;
391381 }
392382
393-
394383 private static deserializeBlobResponse ( response : HttpResponse < Blob > ) {
395- return new Blob ( [ response . body ] , { type : response . headers . get ( 'Content-Type' ) } ) ;
384+ return new Blob ( [ response . body ] , { type : response . headers . get ( 'Content-Type' ) } ) ;
396385 }
397386}
398-
0 commit comments