[AAE-12501] Align JS API (#8344)

* [AAE-12501] Provide an AlfrescoApiService implementation that disable the AlfrescoApi oauth initialization when we use new oidc implementation

* [AAE-12501] Replace oauth2Auth with authentication service, define get username as abstract

* [AAE-12501] Replace sitesService with authentication service since sitesService get the username from oauth2Auth

* [AAE-12501] Call implicitLogin by authentication service

* [AAE-12501] Replace Oauth2Auth with AlfrescoApi and call the custom api without using authentication

* [AAE-12501] Replace oauth2Auth with authentication service to get the token

* [AAE-12501] Replace oauth2Auth with alfrescoApi

* remove unneeded JS-API dep
move auth in the right place

* [AAE-10501] Rename alfresco-api.http-client to adf-http-client

* [AAE-10501] Remove config from a CoreModule, a different service is provided in AuthModule to use angular http client instead of super agent

* [AAE-10501] Disable AlfrescoApi oauth initialization while using new adf oidc authentication

* [AAE-12501] Replace alfresco api client with AdfHttpClient

* [AAE-12501] Restore get username methods

* [AAE-12501] Get username with authentication service

* [AAE-12501] removee unused method

* [AAE-12501] Trigger on login when token is received

* [AAE-12501] Fix content-services unit test

* [AAE-12501] Fix import

* [AAE-12501] Fix core unit tests

* [AAE-12501] Fix process-services-cloud unit tests

* [AAE-12501] Create a request options interface with the needed props, remove the import from js-api, return the body from request

* [AAE-12501] Fix process-services-cloud unit tests without Expectation

* [AAE-12501] Fix Core secondary entrypoints unit tests are not executed: move test.ts one level up in order to find all the spec files into the secondary entrypoints folders and update path

* [AAE-12501] Fix Core unit tests that weren't executed because of the previous test.ts wrong location

* [AAE-12501] Fix authentication token_issued subscription

* add emitters

* [AAE-12501] Replace Math.random() to fix hospot security issue, fix lint issues

* [AAE-12501] Install event-emitter dependency

* [AAE-12501] Comment temporary setCsrfToken because is not possible to import app config service from core due to circular dependencies

* [AAE-12501] Get disableCsrf from app config serviice when app configuration is loaded

* [AAE-12501] Fix license-header lint issue

* [AAE-14221] Regenerate lock file

* [AAE-14221] Fix sonarcloud issues

* [AAE-12501] Remove wrong character

* [AAE-12501] Regenerate lock file

* [AAE-12501] Fix BC: update alfresco api response error

---------

Co-authored-by: eromano <eugenioromano16@gmail.com>
This commit is contained in:
Amedeo Lepore
2023-06-27 17:22:30 +02:00
committed by GitHub
parent 037dce0ae7
commit 7abebf0652
53 changed files with 5474 additions and 10457 deletions

View File

@@ -19,4 +19,5 @@ export * from './lib/api-client.factory';
export * from './lib/api-clients.service';
export * from './lib/clients';
export * from './lib/types';
export * from './lib/alfresco-api/alfresco-api.http-client';
export * from './lib/adf-http-client.service';
export * from './lib/interfaces';

View File

@@ -19,8 +19,8 @@ import { Emitters, RequestOptions, ResultListDataRepresentationTaskRepresentatio
import { HttpParams } from '@angular/common/http';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { TestBed } from '@angular/core/testing';
import { AlfrescoApiHttpClient } from './alfresco-api.http-client';
import { AlfrescoApiResponseError } from './alfresco-api.response-error';
import { AdfHttpClient } from './adf-http-client.service';
import { AlfrescoApiResponseError } from './alfresco-api/alfresco-api.response-error';
const securityOptions: SecurityOptions = {
authentications: {},
@@ -52,8 +52,8 @@ const mockResponse = {
]
};
describe('AlfrescoApiHttpClient', () => {
let angularHttpClient: AlfrescoApiHttpClient;
describe('AdfHttpClient', () => {
let angularHttpClient: AdfHttpClient;
let controller: HttpTestingController;
beforeEach(() => {
@@ -62,7 +62,7 @@ describe('AlfrescoApiHttpClient', () => {
HttpClientTestingModule
]
});
angularHttpClient = TestBed.inject(AlfrescoApiHttpClient);
angularHttpClient = TestBed.inject(AdfHttpClient);
controller = TestBed.inject(HttpTestingController);
});
@@ -90,7 +90,7 @@ describe('AlfrescoApiHttpClient', () => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
expect(res.data![0].created instanceof Date).toBeTruthy();
done();
});
}).catch(error=> fail(error));
const req = controller.expectOne('http://example.com');
expect(req.request.method).toEqual('POST');
@@ -110,7 +110,7 @@ describe('AlfrescoApiHttpClient', () => {
angularHttpClient.request('http://example.com', options, securityOptions, emitters).then((res) => {
expect(res).toEqual(mockResponse);
done();
});
}).catch(error=> fail(error));
const req = controller.expectOne('http://example.com');
expect(req.request.method).toEqual('POST');
@@ -177,7 +177,9 @@ describe('AlfrescoApiHttpClient', () => {
returnType: null
};
angularHttpClient.request('http://example.com', requestOptions, securityOptions, emitters);
angularHttpClient.request('http://example.com', requestOptions, securityOptions, emitters).catch(error =>
fail(error)
);
const req = controller.expectOne('http://example.com?autoRename=true&include=allowableOperations');
expect(req.request.method).toEqual('POST');
@@ -232,9 +234,9 @@ describe('AlfrescoApiHttpClient', () => {
const errorResponse = new Blob();
angularHttpClient.request('http://example.com', options, securityOptions, emitters).catch((res: AlfrescoApiResponseError) => {
expect(res.status).toBe(400);
expect(res.error.response.body instanceof Blob).toBeTruthy();
angularHttpClient.request('http://example.com', options, securityOptions, emitters).catch((err: AlfrescoApiResponseError) => {
expect(err.status).toBe(400);
expect(err.response.body instanceof Blob).toBeTruthy();
done();
});
@@ -257,7 +259,9 @@ describe('AlfrescoApiHttpClient', () => {
}
};
angularHttpClient.request('http://example.com/candidatebaseapp/query/v1/process-instances', options, securityOptions, emitters);
angularHttpClient.request('http://example.com/candidatebaseapp/query/v1/process-instances', options, securityOptions, emitters).catch(error =>
fail(error)
);
const req = controller.expectOne('http://example.com/candidatebaseapp/query/v1/process-instances?skipCount=0&status=RUNNING&status=SUSPENDED&sort=startDate%2CDESC');
expect(req.request.method).toEqual('POST');
@@ -274,7 +278,7 @@ describe('AlfrescoApiHttpClient', () => {
angularHttpClient.request('http://example.com', options, securityOptions, emitters).then((res) => {
expect(res).toEqual('');
done();
});
}).catch(error=> fail(error));
const req = controller.expectOne('http://example.com');
@@ -290,7 +294,9 @@ describe('AlfrescoApiHttpClient', () => {
}
};
angularHttpClient.request('http://example.com', options, securityOptions, emitters);
angularHttpClient.request('http://example.com', options, securityOptions, emitters).catch(error =>
fail(error)
);
const req = controller.expectOne('http://example.com?lastModifiedFrom=2022-08-17T00%3A00%3A00.000%2B02%3A00');
@@ -306,7 +312,9 @@ describe('AlfrescoApiHttpClient', () => {
}
};
angularHttpClient.request('http://example.com', options, securityOptions, emitters);
angularHttpClient.request('http://example.com', options, securityOptions, emitters).catch(error =>
fail(error)
);
const req = controller.expectOne('http://example.com?lastModifiedFrom=2022-08-17T00%3A00%3A00.000Z');

View File

@@ -0,0 +1,353 @@
/*!
* @license
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { SHOULD_ADD_AUTH_TOKEN } from '@alfresco/adf-core/auth';
import { Emitters as JsApiEmitters, HttpClient as JsApiHttpClient } from '@alfresco/js-api';
import {
HttpClient,
HttpContext,
HttpErrorResponse,
HttpEvent,
HttpHeaders,
HttpParams,
HttpResponse
} from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, of, Subject, throwError } from 'rxjs';
import { catchError, map, takeUntil } from 'rxjs/operators';
import {
convertObjectToFormData,
getQueryParamsWithCustomEncoder,
isBlobResponse,
isConstructor,
isHttpResponseEvent,
isHttpUploadProgressEvent,
removeNilValues
} from './alfresco-api/alfresco-api.utils';
import { AlfrescoApiParamEncoder } from './alfresco-api/alfresco-api.param-encoder';
import { AlfrescoApiResponseError } from './alfresco-api/alfresco-api.response-error';
import { Constructor } from './types';
import { RequestOptions, SecurityOptions } from './interfaces';
import ee, { Emitter } from 'event-emitter';
export interface Emitters {
readonly eventEmitter: Emitter;
readonly apiClientEmitter: Emitter;
}
@Injectable({
providedIn: 'root'
})
export class AdfHttpClient implements ee.Emitter,JsApiHttpClient {
on: ee.EmitterMethod;
off: ee.EmitterMethod;
once: ee.EmitterMethod;
emit: (type: string, ...args: any[]) => void;
private _disableCsrf = false;
private defaultSecurityOptions = {
withCredentials: true,
isBpmRequest: false,
authentications: {},
defaultHeaders: {}
};
get disableCsrf(): boolean {
return this._disableCsrf;
}
set disableCsrf(disableCsrf: boolean) {
this._disableCsrf = disableCsrf;
}
constructor(private httpClient: HttpClient
) {
ee(this);
}
setDefaultSecurityOption(options: any) {
this.defaultSecurityOptions = this.merge(this.defaultSecurityOptions, options);
}
merge(...objects): any {
const result = {};
objects.forEach((source) => {
Object.keys(source).forEach((prop) => {
if (prop in result && Array.isArray(result[prop])) {
result[prop] = result[prop].concat(source[prop]);
} else if (prop in result && typeof result[prop] === 'object') {
result[prop] = this.merge(result[prop], source[prop]);
} else {
result[prop] = source[prop];
}
});
});
return result;
}
request<T = any>(url: string, options?: RequestOptions, sc: SecurityOptions = this.defaultSecurityOptions, emitters?: JsApiEmitters): Promise<T> {
const body = AdfHttpClient.getBody(options);
const params = getQueryParamsWithCustomEncoder(options.queryParams, new AlfrescoApiParamEncoder());
const responseType = AdfHttpClient.getResponseType(options);
const context = new HttpContext().set(SHOULD_ADD_AUTH_TOKEN, true);
const security: SecurityOptions = {...this.defaultSecurityOptions, ...sc};
const headers = this.getHeaders(options);
if (!emitters) {
emitters = this.getEventEmitters();
}
const request = this.httpClient.request(
options.httpMethod,
url,
{
context,
...(body && {body}),
...(responseType && {responseType}),
...security,
...(params && {params}),
headers,
observe: 'events',
reportProgress: true
}
);
return this.requestWithLegacyEventEmitters<T>(request, emitters, options.returnType);
}
post<T = any>(url: string, options?: RequestOptions, sc?: SecurityOptions, emitters?: JsApiEmitters): Promise<T> {
return this.request<T>(url, {...options, httpMethod: 'POST'}, sc, emitters);
}
put<T = any>(url: string, options?: RequestOptions, sc?: SecurityOptions, emitters?: JsApiEmitters): Promise<T> {
return this.request<T>(url, {...options, httpMethod: 'PUT'}, sc, emitters);
}
get<T = any>(url: string, options?: RequestOptions, sc?: SecurityOptions, emitters?: JsApiEmitters): Promise<T> {
return this.request<T>(url, {...options, httpMethod: 'GET'}, sc, emitters);
}
delete<T = void>(url: string, options?: RequestOptions, sc?: SecurityOptions, emitters?: JsApiEmitters): Promise<T> {
return this.request<T>(url, {...options, httpMethod: 'DELETE'}, sc, emitters);
}
private addPromiseListeners<T = any>(promise: Promise<T>, eventEmitter: any) {
const eventPromise = Object.assign(promise, {
on() {
eventEmitter.on.apply(eventEmitter, arguments);
return this;
},
once() {
eventEmitter.once.apply(eventEmitter, arguments);
return this;
},
emit() {
eventEmitter.emit.apply(eventEmitter, arguments);
return this;
},
off() {
eventEmitter.off.apply(eventEmitter, arguments);
return this;
}
});
return eventPromise;
}
private getEventEmitters(): Emitters {
const apiClientEmitter = {
on: this.on.bind(this),
off: this.off.bind(this),
once: this.once.bind(this),
emit: this.emit.bind(this)
};
return {
apiClientEmitter,
eventEmitter: ee({})
};
}
private requestWithLegacyEventEmitters<T = any>(request$: Observable<HttpEvent<T>>, emitters: JsApiEmitters, returnType: any): Promise<T> {
const abort$ = new Subject<void>();
const {eventEmitter, apiClientEmitter} = emitters;
const promise = request$.pipe(
map((res) => {
if (isHttpUploadProgressEvent(res)) {
const percent = Math.round((res.loaded / res.total) * 100);
eventEmitter.emit('progress', {loaded: res.loaded, total: res.total, percent});
}
if (isHttpResponseEvent(res)) {
eventEmitter.emit('success', res.body);
return AdfHttpClient.deserialize(res, returnType);
}
}),
catchError((err: HttpErrorResponse): Observable<AlfrescoApiResponseError> => {
// since we can't always determinate ahead of time if the response is going to be xml or plain text response
// we need to handle false positive cases here.
if (err.status === 200) {
eventEmitter.emit('success', err.error.text);
return of(err.error.text);
}
eventEmitter.emit('error', err);
apiClientEmitter.emit('error', err);
if (err.status === 401) {
eventEmitter.emit('unauthorized');
apiClientEmitter.emit('unauthorized');
}
// for backwards compatibility we need to convert it to error class as the HttpErrorResponse only implements Error interface, not extending it,
// and we need to be able to correctly pass instanceof Error conditions used inside repository
// we also need to pass error as Stringify string as we are detecting statusCodes using JSON.parse(error.message) in some places
const msg = typeof err.error === 'string' ? err.error : JSON.stringify(err.error);
// for backwards compatibility to handle cases in code where we try read response.error.response.body;
const error = {
response: {...err, body: err.error}
};
const alfrescoApiError = new AlfrescoApiResponseError(msg, err.status, error.response);
return throwError(alfrescoApiError);
}),
takeUntil(abort$)
).toPromise();
(promise as any).abort = function() {
eventEmitter.emit('abort');
abort$.next();
abort$.complete();
return this;
};
return this.addPromiseListeners(promise, eventEmitter);
}
private static getBody(options: RequestOptions): any {
const contentType = options.contentType;
const isFormData = contentType === 'multipart/form-data';
const isFormUrlEncoded = contentType === 'application/x-www-form-urlencoded';
const body = options.bodyParam;
if (isFormData) {
return convertObjectToFormData(options.formParams);
}
if (isFormUrlEncoded) {
return new HttpParams({fromObject: removeNilValues(options.formParams)});
}
return body;
}
private getHeaders(options: RequestOptions): HttpHeaders {
const optionsHeaders = {
...options.headerParams,
...(options.accept && {Accept: options.accept}),
...((options.contentType) && {'Content-Type': options.contentType})
};
if (!this.disableCsrf) {
this.setCsrfToken(optionsHeaders);
}
return new HttpHeaders(optionsHeaders);
}
private setCsrfToken(optionsHeaders: any) {
const token = this.createCSRFToken();
optionsHeaders['X-CSRF-TOKEN'] = token;
try {
document.cookie = 'CSRF-TOKEN=' + token + ';path=/';
} catch (err) {
/* continue regardless of error */
}
}
private createCSRFToken(a?: any): string {
const randomValue = window.crypto.getRandomValues(new Uint32Array(1))[0];
return a ? (a ^ ((randomValue * 16) >> (a / 4))).toString(16) : ([1e16] + (1e16).toString()).replace(/[01]/g, this.createCSRFToken);
}
private static getResponseType(options: RequestOptions): 'blob' | 'json' | 'text' {
const isBlobType = options.returnType?.toString().toLowerCase() === 'blob' || options.responseType?.toString().toLowerCase() === 'blob';
if (isBlobType) {
return 'blob';
}
if (options.returnType === 'String') {
return 'text';
}
return 'json';
}
/**
* Deserialize an HTTP response body into a value of the specified type.
*/
private static deserialize<T>(response: HttpResponse<T>, returnType?: Constructor<unknown> | 'blob'): any {
if (response === null) {
return null;
}
const body = response.body;
if (!returnType) {
// for backwards compatibility we need to return empty string instead of null,
// to avoid issues when accessing null response would break application [C309878]
// cannot read property 'entry' of null in cases like
// return this.post(apiUrl, saveFormRepresentation).pipe(map((res: any) => res.entry))
return body !== null ? body : '';
}
if (isBlobResponse(response, returnType)) {
return AdfHttpClient.deserializeBlobResponse(response);
}
if (!isConstructor(returnType)) {
return body;
}
if (Array.isArray(body)) {
return body.map((element) => new returnType(element));
}
return new returnType(body);
}
private static deserializeBlobResponse(response: HttpResponse<Blob>) {
return new Blob([response.body], {type: response.headers.get('Content-Type')});
}
}

View File

@@ -1,227 +0,0 @@
/*!
* @license
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { SHOULD_ADD_AUTH_TOKEN } from '@alfresco/adf-core/auth';
import { Emitters as JsApiEmitters, HttpClient as JsApiHttpClient, RequestOptions, SecurityOptions, isBrowser } from '@alfresco/js-api';
import { HttpClient, HttpContext, HttpErrorResponse, HttpEvent, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable, of, Subject, throwError } from 'rxjs';
import { catchError, map, takeUntil } from 'rxjs/operators';
import { convertObjectToFormData, getQueryParamsWithCustomEncoder, isBlobResponse, isConstructor, isHttpResponseEvent, isHttpUploadProgressEvent, removeNilValues } from './alfresco-api.utils';
import { AlfrescoApiParamEncoder } from './alfresco-api.param-encoder';
import { AlfrescoApiResponseError } from './alfresco-api.response-error';
import { Constructor } from '../types';
@Injectable({
providedIn: 'root'
})
export class AlfrescoApiHttpClient implements JsApiHttpClient {
constructor(private httpClient: HttpClient) {}
request<T = any>(url: string, options: RequestOptions, sc: SecurityOptions, emitters: JsApiEmitters): Promise<T> {
const body = AlfrescoApiHttpClient.getBody(options);
const params = getQueryParamsWithCustomEncoder(options.queryParams, new AlfrescoApiParamEncoder());
const headers = AlfrescoApiHttpClient.getHeaders(options);
const responseType = AlfrescoApiHttpClient.getResponseType(options);
const context = new HttpContext().set(SHOULD_ADD_AUTH_TOKEN, true);
const request = this.httpClient.request(
options.httpMethod,
url,
{
context,
...(body && { body }),
...(responseType && { responseType }),
...(sc.withCredentials && { withCredentials: true }),
...(params && { params }),
headers,
observe: 'events',
reportProgress: true
}
);
return this.requestWithLegacyEventEmitters<T>(request, emitters, options.returnType);
}
post<T = any>(url: string, options: RequestOptions, sc: SecurityOptions, emitters: JsApiEmitters): Promise<T> {
return this.request<T>(url, { ...options, httpMethod: 'POST' }, sc, emitters);
}
put<T = any>(url: string, options: RequestOptions, sc: SecurityOptions, emitters: JsApiEmitters): Promise<T> {
return this.request<T>(url, { ...options, httpMethod: 'PUT' }, sc, emitters);
}
get<T = any>(url: string, options: RequestOptions, sc: SecurityOptions, emitters: JsApiEmitters): Promise<T> {
return this.request<T>(url, { ...options, httpMethod: 'GET' }, sc, emitters);
}
delete<T = void>(url: string, options: RequestOptions, sc: SecurityOptions, emitters: JsApiEmitters): Promise<T> {
return this.request<T>(url, { ...options, httpMethod: 'DELETE' }, sc, emitters);
}
private requestWithLegacyEventEmitters<T = any>(request$: Observable<HttpEvent<T>>, emitters: JsApiEmitters, returnType: any): Promise<T> {
const abort$ = new Subject<void>();
const { eventEmitter, apiClientEmitter } = emitters;
const promise = request$.pipe(
map((res) => {
if (isHttpUploadProgressEvent(res)) {
const percent = Math.round((res.loaded / res.total) * 100);
eventEmitter.emit('progress', { loaded: res.loaded, total: res.total, percent });
}
if (isHttpResponseEvent(res)) {
eventEmitter.emit('success', res.body);
return AlfrescoApiHttpClient.deserialize(res, returnType);
}
}),
catchError((err: HttpErrorResponse): Observable<AlfrescoApiResponseError> => {
// since we can't always determinate ahead of time if the response is going to be xml or plain text response
// we need to handle false positive cases here.
if (err.status === 200) {
eventEmitter.emit('success', err.error.text);
return of(err.error.text);
}
eventEmitter.emit('error', err);
apiClientEmitter.emit('error', err);
if (err.status === 401) {
eventEmitter.emit('unauthorized');
apiClientEmitter.emit('unauthorized');
}
// for backwards compatibility we need to convert it to error class as the HttpErrorResponse only implements Error interface, not extending it,
// and we need to be able to correctly pass instanceof Error conditions used inside repository
// we also need to pass error as Stringify string as we are detecting statusCodes using JSON.parse(error.message) in some places
const msg = typeof err.error === 'string' ? err.error : JSON.stringify(err.error);
// for backwards compatibility to handle cases in code where we try read response.error.response.body;
const error = {
response: { ...err, body: err.error }
};
const alfrescoApiError = new AlfrescoApiResponseError(msg, err.status, error);
return throwError(alfrescoApiError);
}),
takeUntil(abort$)
).toPromise();
(promise as any).abort = function() {
eventEmitter.emit('abort');
abort$.next();
abort$.complete();
return this;
};
return promise;
}
private static getBody(options: RequestOptions): any {
const contentType = options.contentType;
const isFormData = contentType === 'multipart/form-data';
const isFormUrlEncoded = contentType === 'application/x-www-form-urlencoded';
const body = options.bodyParam;
if (isFormData) {
return convertObjectToFormData(options.formParams);
}
if (isFormUrlEncoded) {
return new HttpParams({ fromObject: removeNilValues(options.formParams) });
}
return body;
}
private static getHeaders(options: RequestOptions): HttpHeaders {
const optionsHeaders = {
...options.headerParams,
...(options.accept && { Accept: options.accept }),
...((options.contentType) && { 'Content-Type': options.contentType })
};
return new HttpHeaders(optionsHeaders);
}
private static getResponseType(options: RequestOptions): 'blob' | 'json' | 'text' {
const isBlobType = options.returnType?.toString().toLowerCase() === 'blob' || options.responseType?.toString().toLowerCase() === 'blob';
if (isBlobType) {
return 'blob';
}
if (options.returnType === 'String') {
return 'text';
}
return 'json';
}
/**
* Deserialize an HTTP response body into a value of the specified type.
*/
private static deserialize<T>(response: HttpResponse<T>, returnType?: Constructor<unknown> | 'blob'): any {
if (response === null) {
return null;
}
const body = response.body;
if (!returnType) {
// for backwards compatibility we need to return empty string instead of null,
// to avoid issues when accessing null response would break application [C309878]
// cannot read property 'entry' of null in cases like
// return this.post(apiUrl, saveFormRepresentation).pipe(map((res: any) => res.entry))
return body !== null ? body : '';
}
if (isBlobResponse(response, returnType)) {
return AlfrescoApiHttpClient.deserializeBlobResponse(response);
}
if (!isConstructor(returnType)) {
return body;
}
if (Array.isArray(body)) {
return body.map((element) => new returnType(element));
}
return new returnType(body);
}
private static deserializeBlobResponse(response: HttpResponse<Blob>) {
if (isBrowser()) {
return new Blob([response.body], { type: response.headers.get('Content-Type') });
}
return Buffer.from(response.body as unknown as WithImplicitCoercion<string>, 'binary');
}
}

View File

@@ -19,7 +19,7 @@ export class AlfrescoApiResponseError extends Error {
public name = 'AlfrescoApiResponseError';
constructor(msg: string, public status: number, public error: { response: Record<string, any> }) {
constructor(msg: string, public status: number, public response: Record<string, any> ) {
super(msg);
}
}

View File

@@ -0,0 +1,36 @@
/*!
* @license
* Copyright © 2005-2023 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export interface RequestOptions {
httpMethod?: string;
queryParams?: any;
headerParams?: any;
formParams?: any;
bodyParam?: any;
returnType?: any;
responseType?: string;
readonly accept?: string;
readonly contentType?: string;
}
export interface SecurityOptions {
readonly isBpmRequest: boolean;
readonly enableCsrf?: boolean;
readonly withCredentials?: boolean;
readonly authentications: any;
readonly defaultHeaders: Record<string, string>;
}

View File

@@ -48,6 +48,7 @@ describe('AuthenticationInterceptor', () => {
});
it('should call add auth token method when SHOULD_ADD_AUTH_TOKEN context is set to true', () => {
addTokenToHeaderSpy.and.callThrough();
request.context.set(SHOULD_ADD_AUTH_TOKEN, true);
interceptor.intercept(request, mockNext);
expect(addTokenToHeaderSpy).toHaveBeenCalled();

View File

@@ -26,6 +26,7 @@ import { CommonModule } from '@angular/common';
import { ShellAppService, SHELL_APP_SERVICE } from '../../services/shell-app.service';
import { HttpClientModule } from '@angular/common/http';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core';
class MockRouter {
private url = 'some-url';
@@ -57,7 +58,15 @@ describe('AppLayoutComponent', () => {
};
TestBed.configureTestingModule({
imports: [CommonModule, NoopAnimationsModule, HttpClientModule, SidenavLayoutModule, ExtensionsModule, RouterModule.forChild([])],
imports: [
CommonModule,
NoopAnimationsModule,
HttpClientModule,
SidenavLayoutModule,
ExtensionsModule,
RouterModule.forChild([]),
TranslateModule.forRoot()
],
providers: [
{
provide: Router,

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { AlfrescoApiHttpClient } from '@alfresco/adf-core/api';
import { AdfHttpClient } from '@alfresco/adf-core/api';
import { StorageService } from '../common/services/storage.service';
import { AlfrescoApi, AlfrescoApiConfig } from '@alfresco/js-api';
import { Injectable } from '@angular/core';
@@ -23,16 +23,19 @@ import { AppConfigService } from '../app-config';
import { AlfrescoApiService } from '../services/alfresco-api.service';
@Injectable()
export class AlfrescoApiServiceWithAngularBasedHttpClient extends AlfrescoApiService {
export class AlfrescoApiNoAuthService extends AlfrescoApiService {
constructor(
storage: StorageService,
appConfig: AppConfigService,
private readonly alfrescoApiHttpClient: AlfrescoApiHttpClient
private readonly adfHttpClient: AdfHttpClient
) {
super(appConfig, storage);
}
override createInstance(config: AlfrescoApiConfig) {
return new AlfrescoApi(config, this.alfrescoApiHttpClient);
return new AlfrescoApi({
...config,
oauthInit: false
}, this.adfHttpClient);
}
}

View File

@@ -17,9 +17,11 @@
import { AppConfigService, AppConfigValues } from './app-config.service';
import { StorageService } from '../common/services/storage.service';
import { AdfHttpClient } from '@alfresco/adf-core/api';
export function loadAppConfig(appConfigService: AppConfigService, storageService: StorageService) {
export function loadAppConfig(appConfigService: AppConfigService, storageService: StorageService, adfHttpClient: AdfHttpClient) {
return () => appConfigService.load().then(() => {
adfHttpClient.disableCsrf = appConfigService.get<boolean>(AppConfigValues.DISABLECSRF);
storageService.prefix = appConfigService.get<string>(AppConfigValues.STORAGE_PREFIX, '');
});
}

View File

@@ -17,7 +17,7 @@
import { APP_INITIALIZER, ModuleWithProviders, NgModule } from '@angular/core';
import { AuthConfig, AUTH_CONFIG, OAuthModule, OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
import { AlfrescoApiServiceWithAngularBasedHttpClient } from '../../api-factories/alfresco-api-service-with-angular-based-http-client';
import { AlfrescoApiNoAuthService } from '../../api-factories/alfresco-api-no-auth.service';
import { AlfrescoApiService } from '../../services/alfresco-api.service';
import { AuthGuardBpm } from '../guard/auth-guard-bpm.service';
import { AuthGuardEcm } from '../guard/auth-guard-ecm.service';
@@ -47,7 +47,7 @@ export function loginFactory(oAuthService: OAuthService, storage: OAuthStorage,
{ provide: AuthGuardEcm, useClass: OidcAuthGuard },
{ provide: AuthGuardBpm, useClass: OidcAuthGuard },
{ provide: AuthenticationService, useClass: OIDCAuthenticationService },
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceWithAngularBasedHttpClient },
{ provide: AlfrescoApiService, useClass: AlfrescoApiNoAuthService },
{
provide: AUTH_CONFIG,
useFactory: authConfigFactory,

View File

@@ -16,11 +16,11 @@
*/
import { Injectable, inject } from '@angular/core';
import { OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
import { OAuthEvent, OAuthService, OAuthStorage } from 'angular-oauth2-oidc';
import { EMPTY, Observable } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { catchError, filter, map } from 'rxjs/operators';
import { AppConfigValues } from '../../app-config/app-config.service';
import { BaseAuthenticationService } from '../../services/base-authentication.service';
import { BaseAuthenticationService } from '../services/base-authentication.service';
import { JwtHelperService } from '../services/jwt-helper.service';
import { AuthConfigService } from '../oidc/auth-config.service';
import { AuthService } from './auth.service';
@@ -39,7 +39,9 @@ export class OIDCAuthenticationService extends BaseAuthenticationService {
constructor() {
super();
this.alfrescoApi.alfrescoApiInitialized.subscribe(() => {
this.alfrescoApi.getInstance().reply('logged-in', () => {
this.oauthService.events.pipe(
filter((event)=> event.type === 'token_received')
).subscribe(()=>{
this.onLogin.next();
});
});
@@ -87,6 +89,14 @@ export class OIDCAuthenticationService extends BaseAuthenticationService {
);
}
getEcmUsername(): string {
return (this.oauthService.getIdentityClaims() as any).preferred_username;
}
getBpmUsername(): string {
return (this.oauthService.getIdentityClaims() as any).preferred_username;
}
ssoImplicitLogin() {
this.oauthService.initLoginFlow();
}
@@ -117,4 +127,8 @@ export class OIDCAuthenticationService extends BaseAuthenticationService {
this.auth.login();
}
}
once(event: string): Observable<OAuthEvent> {
return this.oauthService.events.pipe(filter(_event => _event.type === event));
}
}

View File

@@ -21,7 +21,7 @@ import { AppConfigValues } from '../../app-config/app-config.service';
import { map, catchError, tap } from 'rxjs/operators';
import { JwtHelperService } from './jwt-helper.service';
import { StorageService } from '../../common/services/storage.service';
import { BaseAuthenticationService } from '../../services/base-authentication.service';
import { BaseAuthenticationService } from './base-authentication.service';
@Injectable({
providedIn: 'root'
@@ -152,6 +152,24 @@ export class AuthenticationService extends BaseAuthenticationService {
return false;
}
/**
* Gets the ECM username.
*
* @returns The ECM username
*/
getEcmUsername(): string {
return this.alfrescoApi.getInstance().getEcmUsername();
}
/**
* Gets the BPM username
*
* @returns The BPM username
*/
getBpmUsername(): string {
return this.alfrescoApi.getInstance().getBpmUsername();
}
isImplicitFlow(): boolean {
return !!this.appConfig.oauth2?.implicitFlow;
}
@@ -169,5 +187,12 @@ export class AuthenticationService extends BaseAuthenticationService {
return this.storageService.getItem(JwtHelperService.USER_ACCESS_TOKEN);
}
reset() {}
reset() { }
once(event: string): Observable<any> {
const alfrescoApiEvent = event === 'token_received' ? 'token_issued' : event;
return new Observable((subscriber) => {
this.alfrescoApi.getInstance().oauth2Auth.once(alfrescoApiEvent, () => subscriber.next());
});
}
}

View File

@@ -15,14 +15,14 @@
* limitations under the License.
*/
import { PeopleApi, UserProfileApi, UserRepresentation } from '@alfresco/js-api';
import { PeopleApi, UserProfileApi } from '@alfresco/js-api';
import { HttpHeaders } from '@angular/common/http';
import { RedirectionModel } from '../auth/models/redirection.model';
import { from, Observable, Observer, ReplaySubject, throwError } from 'rxjs';
import { AppConfigService, AppConfigValues } from '../app-config/app-config.service';
import { AlfrescoApiService } from './alfresco-api.service';
import { CookieService } from '../common/services/cookie.service';
import { LogService } from '../common/services/log.service';
import { RedirectionModel } from '../models/redirection.model';
import { Observable, Observer, ReplaySubject, throwError } from 'rxjs';
import { AppConfigService, AppConfigValues } from '../../app-config/app-config.service';
import { AlfrescoApiService } from '../../services/alfresco-api.service';
import { CookieService } from '../../common/services/cookie.service';
import { LogService } from '../../common/services/log.service';
import { inject } from '@angular/core';
const REMEMBER_ME_COOKIE_KEY = 'ALFRESCO_REMEMBER_ME';
@@ -57,14 +57,15 @@ export abstract class BaseAuthenticationService {
abstract isLoggedIn(): boolean;
abstract isLoggedInWith(provider: string): boolean;
abstract isOauth(): boolean;
abstract isImplicitFlow(): boolean;
abstract isAuthCodeFlow(): boolean;
abstract login(username: string, password: string, rememberMe?: boolean): Observable<{ type: string; ticket: any }>;
abstract ssoImplicitLogin(): void;
abstract logout(): Observable<any>;
abstract isEcmLoggedIn(): boolean;
abstract isBpmLoggedIn(): boolean;
abstract getEcmUsername(): string;
abstract getBpmUsername(): string;
abstract reset(): void;
abstract once(event: string): Observable<any>;
getBearerExcludedUrls(): readonly string[] {
return this.bearerExcludedUrls;
@@ -126,24 +127,6 @@ export abstract class BaseAuthenticationService {
return header.set('Authorization', ticket);
}
/**
* Gets the ECM username.
*
* @returns The ECM username
*/
getEcmUsername(): string {
return this.alfrescoApi.getInstance().getEcmUsername();
}
/**
* Gets the BPM username
*
* @returns The BPM username
*/
getBpmUsername(): string {
return this.alfrescoApi.getInstance().getBpmUsername();
}
isPublicUrl(): boolean {
return this.alfrescoApi.getInstance().isPublicUrl();
}
@@ -206,15 +189,6 @@ export abstract class BaseAuthenticationService {
return null;
}
/**
* Gets information about the user currently logged into APS.
*
* @returns User information
*/
getBpmLoggedUser(): Observable<UserRepresentation> {
return from(this.profileApi.getProfile());
}
/**
* Prints an error message in the console browser
*

View File

@@ -17,7 +17,6 @@
import { fakeAsync, TestBed } from '@angular/core/testing';
import { setupTestBed } from '../../testing/setup-test-bed';
import { AlfrescoApiService } from './../../services/alfresco-api.service';
import { IdentityGroupService } from './identity-group.service';
import { HttpErrorResponse } from '@angular/common/http';
import { throwError, of } from 'rxjs';
@@ -25,23 +24,18 @@ import {
mockIdentityRoles,
clientRoles,
mockIdentityGroup1,
mockIdentityGroupsCount
mockIdentityGroupsCount,
mockIdentityGroups,
roleMappingMock
} from '../mock/identity-group.mock';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import {
applicationDetailsMockApi,
createGroupMappingApi,
deleteGroupMappingApi,
groupsMockApi,
noRoleMappingApi,
roleMappingApi,
updateGroupMappingApi
} from '../mock/oauth2.service.mock';
import { AdfHttpClient } from '../../../../api/src';
describe('IdentityGroupService', () => {
let service: IdentityGroupService;
let apiService: AlfrescoApiService;
let adfHttpClient: AdfHttpClient;
let requestSpy: jasmine.Spy;
setupTestBed({
imports: [
@@ -52,11 +46,12 @@ describe('IdentityGroupService', () => {
beforeEach(fakeAsync(() => {
service = TestBed.inject(IdentityGroupService);
apiService = TestBed.inject(AlfrescoApiService);
adfHttpClient = TestBed.inject(AdfHttpClient);
requestSpy = spyOn(adfHttpClient, 'request');
}));
it('should be able to fetch groups based on group name', (done) => {
spyOn(apiService, 'getInstance').and.returnValue(groupsMockApi);
requestSpy.and.returnValue(Promise.resolve(mockIdentityGroups));
service.findGroupsByName({name: 'mock'}).subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
@@ -72,7 +67,7 @@ describe('IdentityGroupService', () => {
});
it('should return true if group has client role mapping', (done) => {
spyOn(apiService, 'getInstance').and.returnValue(roleMappingApi);
requestSpy.and.returnValue(Promise.resolve(roleMappingMock));
service.checkGroupHasClientApp('mock-group-id', 'mock-app-id').subscribe((hasRole) => {
expect(hasRole).toBeDefined();
expect(hasRole).toBe(true);
@@ -81,7 +76,7 @@ describe('IdentityGroupService', () => {
});
it('should return false if group does not have client role mapping', (done) => {
spyOn(apiService, 'getInstance').and.returnValue(noRoleMappingApi);
requestSpy.and.returnValue(Promise.resolve([]));
service.checkGroupHasClientApp('mock-group-id', 'mock-app-id').subscribe((hasRole) => {
expect(hasRole).toBeDefined();
expect(hasRole).toBe(false);
@@ -226,7 +221,7 @@ describe('IdentityGroupService', () => {
});
it('should be able to fetch the client id', (done) => {
spyOn(apiService, 'getInstance').and.returnValue(applicationDetailsMockApi);
requestSpy.and.returnValue(Promise.resolve([{id: 'mock-app-id', name: 'mock-app-name'}]));
service.getClientIdByApplicationName('mock-app-name').subscribe((clientId) => {
expect(clientId).toBeDefined();
expect(clientId).not.toBeNull();
@@ -236,7 +231,7 @@ describe('IdentityGroupService', () => {
});
it('should be able to all fetch groups', (done) => {
spyOn(apiService, 'getInstance').and.returnValue(groupsMockApi);
requestSpy.and.returnValue(Promise.resolve(mockIdentityGroups));
service.getGroups().subscribe((res) => {
expect(res.length).toBe(5);
expect(res[0].id).toBe('mock-group-id-1');
@@ -273,7 +268,7 @@ describe('IdentityGroupService', () => {
it('should be able to query groups based on first & max params', (done) => {
spyOn(service, 'getTotalGroupsCount').and.returnValue(of(mockIdentityGroupsCount));
spyOn(apiService, 'getInstance').and.returnValue(groupsMockApi);
requestSpy.and.returnValue(Promise.resolve(mockIdentityGroups));
service.queryGroups({first: 0, max: 5}).subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
@@ -314,7 +309,7 @@ describe('IdentityGroupService', () => {
});
it('should be able to create group', (done) => {
const createCustomApiSpy = spyOn(apiService, 'getInstance').and.returnValue(createGroupMappingApi);
const createCustomApiSpy = requestSpy.and.returnValue(Promise.resolve());
service.createGroup(mockIdentityGroup1).subscribe(() => {
expect(createCustomApiSpy).toHaveBeenCalled();
done();
@@ -344,7 +339,7 @@ describe('IdentityGroupService', () => {
});
it('should be able to update group', (done) => {
const updateCustomApiSpy = spyOn(apiService, 'getInstance').and.returnValue(updateGroupMappingApi);
const updateCustomApiSpy = requestSpy.and.returnValue(Promise.resolve());
service.updateGroup('mock-group-id', mockIdentityGroup1).subscribe(() => {
expect(updateCustomApiSpy).toHaveBeenCalled();
done();
@@ -374,7 +369,7 @@ describe('IdentityGroupService', () => {
});
it('should be able to delete group', (done) => {
const deleteCustomApiSpy = spyOn(apiService, 'getInstance').and.returnValue(deleteGroupMappingApi);
const deleteCustomApiSpy = requestSpy.and.returnValue(Promise.resolve());
service.deleteGroup('mock-group-id').subscribe(() => {
expect(deleteCustomApiSpy).toHaveBeenCalled();
done();

View File

@@ -22,31 +22,20 @@ import {
mockIdentityUser1,
mockIdentityUser2,
mockIdentityRole,
mockIdentityUsers
mockIdentityUsers,
mockAssignedRoles,
mockAvailableRoles,
mockEffectiveRoles
} from '../mock/identity-user.mock';
import { mockJoinGroupRequest } from '../mock/identity-group.mock';
import { mockGroups, mockJoinGroupRequest } from '../mock/identity-group.mock';
import { IdentityUserService } from './identity-user.service';
import { JwtHelperService } from './jwt-helper.service';
import { setupTestBed } from '../../testing/setup-test-bed';
import { AlfrescoApiService } from '../../services/alfresco-api.service';
import { mockToken } from '../mock/jwt-helper.service.spec';
import { IdentityRoleModel } from '../models/identity-role.model';
import { CoreTestingModule } from '../../testing/core.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import {
assignRolesMockApi,
createUserMockApi,
deleteUserMockApi,
getAssignedRolesMockApi,
getAvailableRolesMockApi,
getEffectiveRolesMockApi,
getInvolvedGroupsMockApi,
joinGroupMockApi,
leaveGroupMockApi,
queryUsersMockApi,
removeRolesMockApi,
updateUserMockApi
} from '../mock/oauth2.service.mock';
import { AdfHttpClient } from '../../../../api/src';
describe('IdentityUserService', () => {
@@ -59,7 +48,8 @@ describe('IdentityUserService', () => {
];
let service: IdentityUserService;
let alfrescoApiService: AlfrescoApiService;
let adfHttpClient: AdfHttpClient;
let requestSpy: jasmine.Spy;
setupTestBed({
imports: [
@@ -70,7 +60,8 @@ describe('IdentityUserService', () => {
beforeEach(() => {
service = TestBed.inject(IdentityUserService);
alfrescoApiService = TestBed.inject(AlfrescoApiService);
adfHttpClient = TestBed.inject(AdfHttpClient);
requestSpy = spyOn(adfHttpClient, 'request');
});
beforeEach(() => {
@@ -297,7 +288,7 @@ describe('IdentityUserService', () => {
});
it('should be able to query users based on query params (first & max params)', (done) => {
spyOn(alfrescoApiService, 'getInstance').and.returnValue(queryUsersMockApi);
requestSpy.and.returnValue(Promise.resolve(mockIdentityUsers));
service.queryUsers({first: 0, max: 5}).subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
@@ -335,7 +326,7 @@ describe('IdentityUserService', () => {
});
it('should be able to create user', (done) => {
const createCustomApiSpy = spyOn(alfrescoApiService, 'getInstance').and.returnValue(createUserMockApi);
const createCustomApiSpy = requestSpy.and.returnValue(Promise.resolve());
service.createUser(mockIdentityUser1).subscribe(() => {
expect(createCustomApiSpy).toHaveBeenCalled();
done();
@@ -365,7 +356,7 @@ describe('IdentityUserService', () => {
});
it('should be able to update user', (done) => {
const updateCustomApiSpy = spyOn(alfrescoApiService, 'getInstance').and.returnValue(updateUserMockApi);
const updateCustomApiSpy = requestSpy.and.returnValue(Promise.resolve());
service.updateUser('mock-id-2', mockIdentityUser2).subscribe(() => {
expect(updateCustomApiSpy).toHaveBeenCalled();
done();
@@ -395,7 +386,7 @@ describe('IdentityUserService', () => {
});
it('should be able to delete group', (done) => {
const deleteCustomApiSpy = spyOn(alfrescoApiService, 'getInstance').and.returnValue(deleteUserMockApi);
const deleteCustomApiSpy = requestSpy.and.returnValue(Promise.resolve());
service.deleteUser('mock-user-id').subscribe(() => {
expect(deleteCustomApiSpy).toHaveBeenCalled();
done();
@@ -425,7 +416,7 @@ describe('IdentityUserService', () => {
});
it('should be able to fetch involved groups based on user id', (done) => {
spyOn(alfrescoApiService, 'getInstance').and.returnValue(getInvolvedGroupsMockApi);
requestSpy.and.returnValue(Promise.resolve(mockGroups));
service.getInvolvedGroups('mock-user-id').subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
@@ -461,7 +452,7 @@ describe('IdentityUserService', () => {
});
it('should be able to join the group', (done) => {
const joinGroupCustomApiSpy = spyOn(alfrescoApiService, 'getInstance').and.returnValue(joinGroupMockApi);
const joinGroupCustomApiSpy = requestSpy.and.returnValue(Promise.resolve());
service.joinGroup(mockJoinGroupRequest).subscribe(() => {
expect(joinGroupCustomApiSpy).toHaveBeenCalled();
done();
@@ -491,7 +482,7 @@ describe('IdentityUserService', () => {
});
it('should be able to leave the group', (done) => {
const leaveGroupCustomApiSpy = spyOn(alfrescoApiService, 'getInstance').and.returnValue(leaveGroupMockApi);
const leaveGroupCustomApiSpy = requestSpy.and.returnValue(Promise.resolve());
service.leaveGroup('mock-user-id', 'mock-group-id').subscribe(() => {
expect(leaveGroupCustomApiSpy).toHaveBeenCalled();
done();
@@ -521,7 +512,7 @@ describe('IdentityUserService', () => {
});
it('should be able to fetch available roles based on user id', (done) => {
spyOn(alfrescoApiService, 'getInstance').and.returnValue(getAvailableRolesMockApi);
requestSpy.and.returnValue(Promise.resolve(mockAvailableRoles));
service.getAvailableRoles('mock-user-id').subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
@@ -559,7 +550,7 @@ describe('IdentityUserService', () => {
});
it('should be able to fetch assigned roles based on user id', (done) => {
spyOn(alfrescoApiService, 'getInstance').and.returnValue(getAssignedRolesMockApi);
requestSpy.and.returnValue(Promise.resolve(mockAssignedRoles));
service.getAssignedRoles('mock-user-id').subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
@@ -597,7 +588,7 @@ describe('IdentityUserService', () => {
});
it('should be able to fetch effective roles based on user id', (done) => {
spyOn(alfrescoApiService, 'getInstance').and.returnValue(getEffectiveRolesMockApi);
requestSpy.and.returnValue(Promise.resolve(mockEffectiveRoles));
service.getEffectiveRoles('mock-user-id').subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
@@ -635,7 +626,7 @@ describe('IdentityUserService', () => {
});
it('should be able to assign roles to the user', (done) => {
const assignRolesCustomApiSpy = spyOn(alfrescoApiService, 'getInstance').and.returnValue(assignRolesMockApi);
const assignRolesCustomApiSpy = requestSpy.and.returnValue(Promise.resolve());
service.assignRoles('mock-user-id', [mockIdentityRole]).subscribe(() => {
expect(assignRolesCustomApiSpy).toHaveBeenCalled();
done();
@@ -665,7 +656,7 @@ describe('IdentityUserService', () => {
});
it('should be able to remove roles', (done) => {
const removeRolesCustomApiSpy = spyOn(alfrescoApiService, 'getInstance').and.returnValue(removeRolesMockApi);
const removeRolesCustomApiSpy = requestSpy.and.returnValue(Promise.resolve());
service.removeRoles('mock-user-id', [mockIdentityRole]).subscribe(() => {
expect(removeRolesCustomApiSpy).toHaveBeenCalled();
done();

View File

@@ -16,9 +16,8 @@
*/
import { Injectable } from '@angular/core';
import { AlfrescoApiService } from '../../services/alfresco-api.service';
import { Observable, from } from 'rxjs';
import { Oauth2Auth } from '@alfresco/js-api';
import { AdfHttpClient } from '@alfresco/adf-core/api';
export const JSON_TYPE = ['application/json'];
@@ -32,25 +31,21 @@ export interface OAuth2RequestParams {
@Injectable({ providedIn: 'root' })
export class OAuth2Service {
constructor(private alfrescoApiService: AlfrescoApiService) {}
get apiClient(): Oauth2Auth {
return this.alfrescoApiService.getInstance().oauth2Auth;
}
constructor(private adfHttpClient: AdfHttpClient) {}
request<T>(opts: OAuth2RequestParams): Observable<T> {
const { httpMethod, url, bodyParam, queryParams } = opts;
return from(
this.apiClient.callCustomApi(
opts.url,
opts.httpMethod,
opts.pathParams,
opts.queryParams,
{},
{},
opts.bodyParam,
JSON_TYPE,
JSON_TYPE,
Object
this.adfHttpClient.request(
url,
{
httpMethod,
queryParams,
headerParams: {},
formParams: {},
bodyParam,
returnType: Object
}
)
);
}

View File

@@ -45,7 +45,6 @@ import { BlankPageModule } from './blank-page/blank-page.module';
import { DirectiveModule } from './directives/directive.module';
import { PipeModule } from './pipes/pipe.module';
import { AlfrescoApiService } from './services/alfresco-api.service';
import { TranslationService } from './translation/translation.service';
import { SortingPickerModule } from './sorting-picker/sorting-picker.module';
import { IconModule } from './icon/icon.module';
@@ -54,7 +53,7 @@ import { ExtensionsModule } from '@alfresco/adf-extensions';
import { directionalityConfigFactory } from './common/services/directionality-config-factory';
import { DirectionalityConfigService } from './common/services/directionality-config.service';
import { SearchTextModule } from './search-text/search-text-input.module';
import { AlfrescoJsClientsModule } from '@alfresco/adf-core/api';
import { AdfHttpClient, AlfrescoJsClientsModule } from '@alfresco/adf-core/api';
import { AuthenticationInterceptor, Authentication } from '@alfresco/adf-core/auth';
import { LegacyApiClientModule } from './api-factories/legacy-api-client.module';
import { HttpClientModule, HttpClientXsrfModule, HTTP_INTERCEPTORS } from '@angular/common/http';
@@ -65,13 +64,6 @@ import { loadAppConfig } from './app-config/app-config.loader';
import { AppConfigService } from './app-config/app-config.service';
import { StorageService } from './common/services/storage.service';
import { AlfrescoApiLoaderService, createAlfrescoApiInstance } from './api-factories/alfresco-api-v2-loader.service';
import { AlfrescoApiServiceWithAngularBasedHttpClient } from './api-factories/alfresco-api-service-with-angular-based-http-client';
interface Config {
readonly useAngularBasedHttpClientInAlfrescoJs: boolean;
}
const defaultConfig: Config = { useAngularBasedHttpClientInAlfrescoJs: false };
@NgModule({
imports: [
@@ -149,7 +141,7 @@ const defaultConfig: Config = { useAngularBasedHttpClientInAlfrescoJs: false };
]
})
export class CoreModule {
static forRoot(config: Config = defaultConfig): ModuleWithProviders<CoreModule> {
static forRoot(): ModuleWithProviders<CoreModule> {
return {
ngModule: CoreModule,
providers: [
@@ -159,7 +151,7 @@ export class CoreModule {
{
provide: APP_INITIALIZER,
useFactory: loadAppConfig,
deps: [ AppConfigService, StorageService ], multi: true
deps: [ AppConfigService, StorageService, AdfHttpClient ], multi: true
},
{
provide: APP_INITIALIZER,
@@ -180,11 +172,7 @@ export class CoreModule {
useFactory: createAlfrescoApiInstance,
deps: [ AlfrescoApiLoaderService ],
multi: true
},
...(config.useAngularBasedHttpClientInAlfrescoJs
? [{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceWithAngularBasedHttpClient }]
: []
)
}
]
};
}

View File

@@ -24,7 +24,6 @@ import { Router, ActivatedRoute, Params } from '@angular/router';
import { AuthenticationService } from '../../auth/services/authentication.service';
import { TranslationService } from '../../translation/translation.service';
import { UserPreferencesService } from '../../common/services/user-preferences.service';
import { AlfrescoApiService } from '../../services/alfresco-api.service';
import { LoginErrorEvent } from '../models/login-error.event';
import { LoginSubmitEvent } from '../models/login-submit.event';
@@ -136,8 +135,7 @@ export class LoginComponent implements OnInit, OnDestroy {
private appConfig: AppConfigService,
private userPreferences: UserPreferencesService,
private route: ActivatedRoute,
private sanitizer: DomSanitizer,
private alfrescoApiService: AlfrescoApiService
private sanitizer: DomSanitizer
) {
}
@@ -188,7 +186,7 @@ export class LoginComponent implements OnInit, OnDestroy {
}
redirectToImplicitLogin() {
this.alfrescoApiService.getInstance().oauth2Auth.implicitLogin();
this.authService.ssoImplicitLogin();
}
/**

View File

@@ -12,6 +12,6 @@
}
},
"exclude": ["src/test.ts", "**/*.spec.ts", "**/*.test.ts"],
"exclude": ["./test.ts", "**/*.spec.ts", "**/*.test.ts"],
"include": ["**/*.ts"]
}

View File

@@ -3,6 +3,6 @@
"compilerOptions": {
"outDir": "../../dist/out-tsc"
},
"files": ["src/test.ts"],
"files": ["./test.ts"],
"include": ["**/*.spec.ts", "**/*.test.ts", "**/*.d.ts"]
}