[ADF-4738] [Process -Cloud] Move GroupCloudService to adf-core (#4928)

* [ADF-4738] [Process -Cloud] Move GroupCloudService to adf-core lib.

* * Changed GroupModel to IdentityGroupModel
* Updated unit tests the recent changes.

* * Added documentation to the identityGroupService
* Updated GroupInitial and GroupCoudcomponent doc.

* * Created groupCount model. * Updated unit test to the recent changes

* * After rebase updated doc

* * Fixed comments.
This commit is contained in:
siva kumar
2019-08-12 20:57:30 +05:30
committed by Eugenio Romano
parent 4ead51d2a6
commit eb2811fdd0
19 changed files with 915 additions and 499 deletions

View File

@@ -22,17 +22,20 @@ import { ProcessServiceCloudTestingModule } from './../../testing/process-servic
import { GroupCloudModule } from '../group-cloud.module';
import { GroupCloudComponent } from './group-cloud.component';
import { GroupCloudService } from '../services/group-cloud.service';
import { setupTestBed, AlfrescoApiServiceMock } from '@alfresco/adf-core';
import { mockGroups } from '../mock/group-cloud.mock';
import { GroupModel } from '../models/group.model';
import {
setupTestBed,
AlfrescoApiServiceMock,
IdentityGroupService,
IdentityGroupModel,
mockIdentityGroups
} from '@alfresco/adf-core';
import { SimpleChange } from '@angular/core';
describe('GroupCloudComponent', () => {
let component: GroupCloudComponent;
let fixture: ComponentFixture<GroupCloudComponent>;
let element: HTMLElement;
let service: GroupCloudService;
let service: IdentityGroupService;
let findGroupsByNameSpy: jasmine.Spy;
let getClientIdByApplicationNameSpy: jasmine.Spy;
let checkGroupHasAccessSpy: jasmine.Spy;
@@ -40,15 +43,15 @@ describe('GroupCloudComponent', () => {
setupTestBed({
imports: [ProcessServiceCloudTestingModule, GroupCloudModule],
providers: [AlfrescoApiServiceMock, GroupCloudService]
providers: [AlfrescoApiServiceMock, IdentityGroupService]
});
beforeEach(() => {
fixture = TestBed.createComponent(GroupCloudComponent);
component = fixture.componentInstance;
element = fixture.nativeElement;
service = TestBed.get(GroupCloudService);
findGroupsByNameSpy = spyOn(service, 'findGroupsByName').and.returnValue(of(mockGroups));
service = TestBed.get(IdentityGroupService);
findGroupsByNameSpy = spyOn(service, 'findGroupsByName').and.returnValue(of(mockIdentityGroups));
getClientIdByApplicationNameSpy = spyOn(service, 'getClientIdByApplicationName').and.returnValue(of('mock-client-id'));
checkGroupHasAccessSpy = spyOn(service, 'checkGroupHasClientApp').and.returnValue(of(true));
checkGroupHasGivenRoleSpy = spyOn(service, 'checkGroupHasRole').and.returnValue(of(true));
@@ -72,7 +75,7 @@ describe('GroupCloudComponent', () => {
it('should show the groups if the typed result match', async(() => {
fixture.detectChanges();
component.searchGroups$ = of(<GroupModel[]> mockGroups);
component.searchGroups$.next(<IdentityGroupModel[]> mockIdentityGroups);
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
inputHTMLElement.focus();
inputHTMLElement.dispatchEvent(new Event('input'));
@@ -103,7 +106,7 @@ describe('GroupCloudComponent', () => {
it('should emit selectedGroup if option is valid', async(() => {
fixture.detectChanges();
const selectEmitSpy = spyOn(component.selectGroup, 'emit');
component.onSelect(new GroupModel({ name: 'group name'}));
component.onSelect(new IdentityGroupModel({ name: 'group name'}));
fixture.whenStable().then(() => {
expect(selectEmitSpy).toHaveBeenCalled();
});
@@ -147,7 +150,7 @@ describe('GroupCloudComponent', () => {
it('should pre-select all preSelectGroups when mode=multiple', async(() => {
component.mode = 'multiple';
component.preSelectGroups = <any> [{id: mockGroups[1].id}, {id: mockGroups[2].id}];
component.preSelectGroups = <any> [{id: mockIdentityGroups[1].id}, {id: mockIdentityGroups[2].id}];
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
@@ -168,11 +171,11 @@ describe('GroupCloudComponent', () => {
it('should pre-select preSelectGroups[0] when mode=single', async(() => {
component.mode = 'single';
component.preSelectGroups = <any> [{id: mockGroups[1].id}, {id: mockGroups[2].id}];
component.preSelectGroups = <any> [{id: mockIdentityGroups[1].id}, {id: mockIdentityGroups[2].id}];
fixture.detectChanges();
fixture.whenStable().then(() => {
const selectedGroup = component.searchGroupsControl.value;
expect(selectedGroup.id).toBe(mockGroups[1].id);
expect(selectedGroup.id).toBe(mockIdentityGroups[1].id);
});
}));
@@ -189,7 +192,7 @@ describe('GroupCloudComponent', () => {
const removeGroupSpy = spyOn(component.removeGroup, 'emit');
component.mode = 'multiple';
component.preSelectGroups = <any> [{id: mockGroups[1].id}, {id: mockGroups[2].id}];
component.preSelectGroups = <any> [{id: mockIdentityGroups[1].id}, {id: mockIdentityGroups[2].id}];
fixture.detectChanges();
fixture.whenStable().then(() => {
@@ -197,7 +200,7 @@ describe('GroupCloudComponent', () => {
const removeIcon = fixture.debugElement.query(By.css('mat-chip mat-icon'));
removeIcon.nativeElement.click();
expect(removeGroupSpy).toHaveBeenCalledWith({ id: mockGroups[1].id });
expect(removeGroupSpy).toHaveBeenCalledWith({ id: mockIdentityGroups[1].id });
});
}));
@@ -213,7 +216,7 @@ describe('GroupCloudComponent', () => {
fixture.whenStable().then(() => {
fixture.detectChanges();
const groupsList = fixture.debugElement.queryAll(By.css('mat-option'));
expect(groupsList.length).toBe(mockGroups.length);
expect(groupsList.length).toBe(mockIdentityGroups.length);
});
}));
@@ -246,7 +249,7 @@ describe('GroupCloudComponent', () => {
fixture.whenStable().then(() => {
fixture.detectChanges();
const groupsList = fixture.debugElement.queryAll(By.css('mat-option'));
expect(groupsList.length).toBe(mockGroups.length);
expect(groupsList.length).toBe(mockIdentityGroups.length);
expect(checkGroupHasGivenRoleSpy).toHaveBeenCalled();
});
}));
@@ -263,13 +266,13 @@ describe('GroupCloudComponent', () => {
fixture.whenStable().then(() => {
fixture.detectChanges();
const groupsList = fixture.debugElement.queryAll(By.css('mat-option'));
expect(groupsList.length).toBe(mockGroups.length);
expect(groupsList.length).toBe(mockIdentityGroups.length);
expect(checkGroupHasGivenRoleSpy).not.toHaveBeenCalled();
});
}));
it('should validate access to the app when appName is specified', async(() => {
findGroupsByNameSpy.and.returnValue(of(mockGroups));
findGroupsByNameSpy.and.returnValue(of(mockIdentityGroups));
checkGroupHasAccessSpy.and.returnValue(of(true));
fixture.detectChanges();
const inputHTMLElement: HTMLInputElement = <HTMLInputElement> element.querySelector('input');
@@ -279,7 +282,7 @@ describe('GroupCloudComponent', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(checkGroupHasAccessSpy).toHaveBeenCalledTimes(mockGroups.length);
expect(checkGroupHasAccessSpy).toHaveBeenCalledTimes(mockIdentityGroups.length);
});
}));

View File

@@ -25,15 +25,15 @@ import {
ViewEncapsulation,
Input,
SimpleChanges,
OnChanges
OnChanges,
OnDestroy
} from '@angular/core';
import { FormControl } from '@angular/forms';
import { trigger, state, style, transition, animate } from '@angular/animations';
import { Observable, of, BehaviorSubject } from 'rxjs';
import { GroupModel, GroupSearchParam } from '../models/group.model';
import { GroupCloudService } from '../services/group-cloud.service';
import { Observable, of, BehaviorSubject, Subject } from 'rxjs';
import { debounceTime } from 'rxjs/internal/operators/debounceTime';
import { distinctUntilChanged, switchMap, mergeMap, filter, tap, map } from 'rxjs/operators';
import { distinctUntilChanged, switchMap, mergeMap, filter, tap, map, takeUntil } from 'rxjs/operators';
import { IdentityGroupModel, IdentityGroupSearchParam, IdentityGroupService } from '@alfresco/adf-core';
@Component({
selector: 'adf-cloud-group',
@@ -50,7 +50,7 @@ import { distinctUntilChanged, switchMap, mergeMap, filter, tap, map } from 'rxj
],
encapsulation: ViewEncapsulation.None
})
export class GroupCloudComponent implements OnInit, OnChanges {
export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
static MODE_SINGLE = 'single';
static MODE_MULTIPLE = 'multiple';
@@ -69,7 +69,7 @@ export class GroupCloudComponent implements OnInit, OnChanges {
/** Array of users to be pre-selected. This pre-selects all users in multi selection mode and only the first user of the array in single selection mode. */
@Input()
preSelectGroups: GroupModel[] = [];
preSelectGroups: IdentityGroupModel[] = [];
/** FormControl to search the group */
@Input()
@@ -81,26 +81,22 @@ export class GroupCloudComponent implements OnInit, OnChanges {
/** Emitted when a group is selected. */
@Output()
selectGroup: EventEmitter<GroupModel> = new EventEmitter<GroupModel>();
selectGroup = new EventEmitter<IdentityGroupModel>();
/** Emitted when a group is removed. */
@Output()
removeGroup: EventEmitter<GroupModel> = new EventEmitter<GroupModel>();
removeGroup = new EventEmitter<IdentityGroupModel>();
@ViewChild('groupInput')
private groupInput: ElementRef<HTMLInputElement>;
private selectedGroups: GroupModel[] = [];
private selectedGroups: IdentityGroupModel[] = [];
private searchGroups: GroupModel[] = [];
private searchGroups: IdentityGroupModel[] = [];
private searchGroupsSubject: BehaviorSubject<GroupModel[]>;
searchGroups$ = new BehaviorSubject<IdentityGroupModel[]>([]);
private selectedGroupsSubject: BehaviorSubject<GroupModel[]>;
searchGroups$: Observable<GroupModel[]>;
selectedGroups$: Observable<GroupModel[]>;
selectedGroups$ = new BehaviorSubject<IdentityGroupModel[]>([]);
_subscriptAnimationState = 'enter';
@@ -112,12 +108,9 @@ export class GroupCloudComponent implements OnInit, OnChanges {
isDisabled: boolean;
constructor(private groupService: GroupCloudService) {
this.selectedGroupsSubject = new BehaviorSubject<GroupModel[]>(this.selectedGroups);
this.searchGroupsSubject = new BehaviorSubject<GroupModel[]>(this.searchGroups);
this.selectedGroups$ = this.selectedGroupsSubject.asObservable();
this.searchGroups$ = this.searchGroupsSubject.asObservable();
}
private onDestroy$ = new Subject<boolean>();
constructor(private identityGroupService: IdentityGroupService) { }
ngOnInit() {
this.initSearch();
@@ -136,12 +129,12 @@ export class GroupCloudComponent implements OnInit, OnChanges {
}
}
private isAppNameChanged(change) {
private isAppNameChanged(change): boolean {
return change.previousValue !== change.currentValue && this.appName && this.appName.length > 0;
}
private async loadClientId() {
this.clientId = await this.groupService.getClientIdByApplicationName(this.appName).toPromise();
this.clientId = await this.identityGroupService.getClientIdByApplicationName(this.appName).toPromise();
if (this.clientId) {
this.enableSearch();
}
@@ -165,7 +158,7 @@ export class GroupCloudComponent implements OnInit, OnChanges {
}),
switchMap((inputValue) => {
const queryParams = this.createSearchParam(inputValue);
return this.groupService.findGroupsByName(queryParams);
return this.identityGroupService.findGroupsByName(queryParams);
}),
mergeMap((groups) => {
return groups;
@@ -185,23 +178,24 @@ export class GroupCloudComponent implements OnInit, OnChanges {
} else {
return of(group);
}
})
).subscribe((searchedGroup) => {
}),
takeUntil(this.onDestroy$)
).subscribe((searchedGroup: any) => {
this.searchGroups.push(searchedGroup);
this.searchGroupsSubject.next(this.searchGroups);
this.searchGroups$.next(this.searchGroups);
});
}
checkGroupHasAccess(groupId: string): Observable<boolean> {
if (this.hasRoles()) {
return this.groupService.checkGroupHasAnyClientAppRole(groupId, this.clientId, this.roles);
return this.identityGroupService.checkGroupHasAnyClientAppRole(groupId, this.clientId, this.roles);
} else {
return this.groupService.checkGroupHasClientApp(groupId, this.clientId);
return this.identityGroupService.checkGroupHasClientApp(groupId, this.clientId);
}
}
isGroupAlreadySelected(group: GroupModel): boolean {
const result = this.selectedGroups.find((selectedGroup: GroupModel) => {
isGroupAlreadySelected(group: IdentityGroupModel): boolean {
const result = this.selectedGroups.find((selectedGroup: IdentityGroupModel) => {
return selectedGroup.id === group.id;
});
@@ -211,30 +205,30 @@ export class GroupCloudComponent implements OnInit, OnChanges {
private loadPreSelectGroups() {
if (this.isMultipleMode()) {
this.selectedGroups = [];
this.preSelectGroups.forEach((group: GroupModel) => {
this.preSelectGroups.forEach((group: IdentityGroupModel) => {
this.selectedGroups.push(group);
});
this.selectedGroupsSubject.next(this.selectedGroups);
this.selectedGroups$.next(this.selectedGroups);
} else {
this.searchGroupsControl.setValue(this.preSelectGroups[0]);
this.onSelect(this.preSelectGroups[0]);
}
}
filterGroupsByRoles(group: GroupModel): Observable<GroupModel> {
return this.groupService.checkGroupHasRole(group.id, this.roles).pipe(
filterGroupsByRoles(group: IdentityGroupModel): Observable<IdentityGroupModel> {
return this.identityGroupService.checkGroupHasRole(group.id, this.roles).pipe(
map((hasRole: boolean) => ({ hasRole: hasRole, group: group })),
filter((filteredGroup: { hasRole: boolean, group: GroupModel }) => filteredGroup.hasRole),
map((filteredGroup: { hasRole: boolean, group: GroupModel }) => filteredGroup.group));
filter((filteredGroup: { hasRole: boolean, group: IdentityGroupModel }) => filteredGroup.hasRole),
map((filteredGroup: { hasRole: boolean, group: IdentityGroupModel }) => filteredGroup.group));
}
onSelect(selectedGroup: GroupModel) {
onSelect(selectedGroup: IdentityGroupModel) {
if (this.isMultipleMode()) {
if (!this.isGroupAlreadySelected(selectedGroup)) {
this.selectedGroups.push(selectedGroup);
this.selectedGroupsSubject.next(this.selectedGroups);
this.selectedGroups$.next(this.selectedGroups);
this.selectGroup.emit(selectedGroup);
this.searchGroupsSubject.next([]);
this.searchGroups$.next([]);
}
this.groupInput.nativeElement.value = '';
this.searchGroupsControl.setValue('');
@@ -246,25 +240,25 @@ export class GroupCloudComponent implements OnInit, OnChanges {
this.resetSearchGroups();
}
onRemove(selectedGroup: GroupModel) {
onRemove(selectedGroup: IdentityGroupModel) {
this.removeGroup.emit(selectedGroup);
const indexToRemove = this.selectedGroups.findIndex((group: GroupModel) => {
const indexToRemove = this.selectedGroups.findIndex((group: IdentityGroupModel) => {
return group.id === selectedGroup.id;
});
this.selectedGroups.splice(indexToRemove, 1);
this.selectedGroupsSubject.next(this.selectedGroups);
this.selectedGroups$.next(this.selectedGroups);
}
private resetSearchGroups() {
this.searchGroups = [];
this.searchGroupsSubject.next([]);
this.searchGroups$.next([]);
}
isMultipleMode(): boolean {
return this.mode === GroupCloudComponent.MODE_MULTIPLE;
}
getDisplayName(group: GroupModel): string {
getDisplayName(group: IdentityGroupModel): string {
return group ? group.name : '';
}
@@ -272,8 +266,8 @@ export class GroupCloudComponent implements OnInit, OnChanges {
return this.preSelectGroups && this.preSelectGroups.length > 0;
}
private createSearchParam(value: string): GroupSearchParam {
const queryParams: GroupSearchParam = { name: value };
private createSearchParam(value: string): IdentityGroupSearchParam {
const queryParams: IdentityGroupSearchParam = { name: value };
return queryParams;
}
@@ -310,4 +304,9 @@ export class GroupCloudComponent implements OnInit, OnChanges {
hasErrorMessage(): boolean {
return !this.isFocused && this.hasError();
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
}

View File

@@ -1,112 +0,0 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* 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 { GroupModel, GroupRoleModel } from '../models/group.model';
export let mockGroup1 = new GroupModel({
id: 'mock-id-1', name: 'Mock Group 1', path: '/mock', subGroups: []
});
export let mockGroup2 = new GroupModel({
id: 'mock-id-2', name: 'Mock Group 2', path: '', subGroups: []
});
export let mockGroup3 = new GroupModel({
id: 'mock-id-3', name: 'Fake Group 3', path: '', subGroups: []
});
export let mockGroups = [
mockGroup1, mockGroup2, mockGroup3
];
export let mockApplicationDetails = {id: 'mock-app-id', name: 'mock-app-name'};
export let mockError = {
error: {
errorKey: 'failed',
statusCode: 400,
stackTrace: 'For security reasons the stack trace is no longer displayed, but the property is kept for previous versions.'
}
};
export let mockApiError = {
oauth2Auth: {
callCustomApi: () => {
return Promise.reject(mockError);
}
}
};
export let roleMappingMock = [
{ id: 'role-id-1', name: 'role-name-1' }, { id: 'role-id-2', name: 'role-name-2' }
];
export let roleMappingApi = {
oauth2Auth: {
callCustomApi: () => {
return Promise.resolve(roleMappingMock);
}
}
};
export let noRoleMappingApi = {
oauth2Auth: {
callCustomApi: () => {
return Promise.resolve([]);
}
}
};
export let groupsMockApi = {
oauth2Auth: {
callCustomApi: () => {
return Promise.resolve(mockGroups);
}
}
};
export let returnCallQueryParameters = {
oauth2Auth: {
callCustomApi: (queryUrl, operation, context, queryParams) => {
return Promise.resolve(queryParams);
}
}
};
export let returnCallUrl = {
oauth2Auth: {
callCustomApi: (queryUrl, operation, context, queryParams) => {
return Promise.resolve(queryUrl);
}
}
};
export let applicationDetailsMockApi = {
oauth2Auth: {
callCustomApi: () => {
return Promise.resolve([mockApplicationDetails]);
}
}
};
export let groupRoles = [
new GroupRoleModel({id: 'mock-id', name: 'MOCK-ADMIN-ROLE'}),
new GroupRoleModel({id: 'mock-id', name: 'MOCK-USER-ROLE'}),
new GroupRoleModel({id: 'mock-id', name: 'MOCK-ROLE-1'})
];
export let clientRoles = [ 'MOCK-ADMIN-ROLE', 'MOCK-USER-ROLE'];

View File

@@ -1,54 +0,0 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* 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 class GroupModel {
id: string;
name: string;
path: string;
realmRoles: string[];
access: any;
attributes: any;
clientRoles: any;
constructor(obj?: any) {
this.id = obj.id || null;
this.name = obj.name || null;
this.path = obj.path || null;
this.realmRoles = obj.realmRoles || null;
this.access = obj.access || null;
this.attributes = obj.attributes || null;
this.clientRoles = obj.clientRoles || null;
}
}
export interface GroupSearchParam {
name?: string;
}
export class GroupRoleModel {
id?: string;
name: string;
constructor(obj?: any) {
if (obj) {
this.id = obj.id || null;
this.name = obj.name || null;
}
}
}

View File

@@ -16,16 +16,16 @@
*/
import { InitialGroupNamePipe } from './group-initial.pipe';
import { GroupModel } from '../models/group.model';
import { IdentityGroupModel } from '@alfresco/adf-core';
describe('InitialGroupNamePipe', () => {
let pipe: InitialGroupNamePipe;
let fakeGroup: GroupModel;
let fakeGroup: IdentityGroupModel;
beforeEach(() => {
pipe = new InitialGroupNamePipe();
fakeGroup = new GroupModel({name: 'mock'});
fakeGroup = new IdentityGroupModel({name: 'mock'});
});
it('should return with the group initial', () => {

View File

@@ -16,7 +16,7 @@
*/
import { Pipe, PipeTransform } from '@angular/core';
import { GroupModel } from '../models/group.model';
import { IdentityGroupModel } from '@alfresco/adf-core';
@Pipe({
name: 'groupNameInitial'
@@ -25,7 +25,7 @@ export class InitialGroupNamePipe implements PipeTransform {
constructor() {}
transform(group: GroupModel): string {
transform(group: IdentityGroupModel): string {
let result = '';
if (group) {
result = this.getInitialGroupName(group.name).toUpperCase();

View File

@@ -15,7 +15,5 @@
* limitations under the License.
*/
export * from './models/group.model';
export * from './services/group-cloud.service';
export * from './components/group-cloud.component';
export * from './group-cloud.module';

View File

@@ -1,275 +0,0 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* 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 { async } from '@angular/core/testing';
import { TestBed } from '@angular/core/testing';
import { GroupCloudService } from './group-cloud.service';
import {
AlfrescoApiServiceMock,
CoreModule,
setupTestBed,
AlfrescoApiService,
LogService
} from '@alfresco/adf-core';
import {
applicationDetailsMockApi,
groupsMockApi,
returnCallQueryParameters,
returnCallUrl,
mockApiError,
mockError,
roleMappingApi,
noRoleMappingApi,
groupRoles,
clientRoles
} from '../mock/group-cloud.mock';
import { GroupSearchParam } from '../models/group.model';
import { HttpErrorResponse } from '@angular/common/http';
import { throwError, of } from 'rxjs';
describe('GroupCloudService', () => {
let service: GroupCloudService;
let apiService: AlfrescoApiService;
let logService: LogService;
setupTestBed({
imports: [CoreModule.forRoot()],
providers: [
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock }
]
});
beforeEach(async(() => {
service = TestBed.get(GroupCloudService);
apiService = TestBed.get(AlfrescoApiService);
logService = TestBed.get(LogService);
}));
it('should be able to fetch groups', (done) => {
spyOn(apiService, 'getInstance').and.returnValue(groupsMockApi);
service.findGroupsByName(<GroupSearchParam> {name: 'mock'}).subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.length).toBe(3);
expect(res[0].id).toBe('mock-id-1');
expect(res[0].name).toBe('Mock Group 1');
expect(res[1].id).toBe('mock-id-2');
expect(res[1].name).toBe('Mock Group 2');
expect(res[2].id).toBe('mock-id-3');
expect(res[2].name).toBe('Fake Group 3');
done();
});
});
it('should return true if group has client role mapping', (done) => {
spyOn(apiService, 'getInstance').and.returnValue(roleMappingApi);
service.checkGroupHasClientApp('mock-group-id', 'mock-app-id').subscribe((hasRole) => {
expect(hasRole).toBeDefined();
expect(hasRole).toBe(true);
done();
});
});
it('should return false if group does not have client role mapping', (done) => {
spyOn(apiService, 'getInstance').and.returnValue(noRoleMappingApi);
service.checkGroupHasClientApp('mock-group-id', 'mock-app-id').subscribe((hasRole) => {
expect(hasRole).toBeDefined();
expect(hasRole).toBe(false);
done();
});
});
it('should able to fetch group roles by groupId', (done) => {
spyOn(service, 'getGroupRoles').and.returnValue(of(groupRoles));
service.getGroupRoles('mock-group-id').subscribe(
(res: any) => {
expect(res).toBeDefined();
expect(res.length).toEqual(3);
expect(res[0].name).toEqual('MOCK-ADMIN-ROLE');
expect(res[1].name).toEqual('MOCK-USER-ROLE');
expect(res[2].name).toEqual('MOCK-ROLE-1');
done();
}
);
});
it('Should not able to fetch group roles if error occurred', (done) => {
const errorResponse = new HttpErrorResponse({
error: 'Mock Error',
status: 404, statusText: 'Not Found'
});
spyOn(service, 'getGroupRoles').and.returnValue(throwError(errorResponse));
service.getGroupRoles('mock-group-id')
.subscribe(
() => {
fail('expected an error, not group roles');
},
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
});
it('should return true if group has given role', (done) => {
spyOn(service, 'getGroupRoles').and.returnValue(of(groupRoles));
service.checkGroupHasRole('mock-group-id', ['MOCK-ADMIN-ROLE']).subscribe(
(res: boolean) => {
expect(res).toBeDefined();
expect(res).toBeTruthy();
done();
}
);
});
it('should return false if group does not have given role', (done) => {
spyOn(service, 'getGroupRoles').and.returnValue(of(groupRoles));
service.checkGroupHasRole('mock-group-id', ['MOCK-ADMIN-MODELER']).subscribe(
(res: boolean) => {
expect(res).toBeDefined();
expect(res).toBeFalsy();
done();
}
);
});
it('should fetch client roles by groupId and clientId', (done) => {
spyOn(service, 'getClientRoles').and.returnValue(of(clientRoles));
service.getClientRoles('mock-group-id', 'mock-client-id').subscribe(
(res: any) => {
expect(res).toBeDefined();
expect(res.length).toEqual(2);
expect(res).toEqual(clientRoles);
done();
}
);
});
it('Should not fetch client roles if error occurred', (done) => {
const errorResponse = new HttpErrorResponse({
error: 'Mock Error',
status: 404, statusText: 'Not Found'
});
spyOn(service, 'getClientRoles').and.returnValue(throwError(errorResponse));
service.getClientRoles('mock-group-id', 'mock-client-id')
.subscribe(
() => {
fail('expected an error, not client roles');
},
(error) => {
expect(error.status).toEqual(404);
expect(error.statusText).toEqual('Not Found');
expect(error.error).toEqual('Mock Error');
done();
}
);
});
it('should return true if group has client access', (done) => {
spyOn(service, 'getClientRoles').and.returnValue(of(clientRoles));
service.checkGroupHasClientApp('mock-group-id', 'mock-client-id').subscribe(
(res: boolean) => {
expect(res).toBeDefined();
expect(res).toBeTruthy();
done();
}
);
});
it('should return false if group does not have client access', (done) => {
spyOn(service, 'getClientRoles').and.returnValue(of([]));
service.checkGroupHasClientApp('mock-group-id', 'mock-client-id').subscribe(
(res: boolean) => {
expect(res).toBeDefined();
expect(res).toBeFalsy();
done();
}
);
});
it('should return true if group has any client role', (done) => {
spyOn(service, 'checkGroupHasAnyClientAppRole').and.returnValue(of(true));
service.checkGroupHasAnyClientAppRole('mock-group-id', 'mock-client-id', ['MOCK-USER-ROLE']).subscribe(
(res: boolean) => {
expect(res).toBeDefined();
expect(res).toBeTruthy();
done();
}
);
});
it('should return false if group does not have any client role', (done) => {
spyOn(service, 'getClientRoles').and.returnValue(of([]));
service.checkGroupHasAnyClientAppRole('mock-group-id', 'mock-client-id', ['MOCK-ADMIN-MODELER']).subscribe(
(res: boolean) => {
expect(res).toBeDefined();
expect(res).toBeFalsy();
done();
}
);
});
it('should append to the call all the parameters', (done) => {
spyOn(apiService, 'getInstance').and.returnValue(returnCallQueryParameters);
service.findGroupsByName(<GroupSearchParam> {name: 'mock'}).subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.search).toBe('mock');
done();
});
});
it('should request groups api url', (done) => {
spyOn(apiService, 'getInstance').and.returnValue(returnCallUrl);
service.findGroupsByName(<GroupSearchParam> {name: 'mock'}).subscribe((requestUrl) => {
expect(requestUrl).toBeDefined();
expect(requestUrl).not.toBeNull();
expect(requestUrl).toContain('/groups');
done();
});
});
it('should be able to fetch the client id', (done) => {
spyOn(apiService, 'getInstance').and.returnValue(applicationDetailsMockApi);
service.getClientIdByApplicationName('mock-app-name').subscribe((clientId) => {
expect(clientId).toBeDefined();
expect(clientId).not.toBeNull();
expect(clientId).toBe('mock-app-id');
done();
});
});
it('should notify errors returned from the API', (done) => {
const logServiceSpy = spyOn(logService, 'error').and.callThrough();
spyOn(apiService, 'getInstance').and.returnValue(mockApiError);
service.findGroupsByName(<GroupSearchParam> {name: 'mock'}).subscribe(
() => {},
(res: any) => {
expect(res).toBeDefined();
expect(res).toEqual(mockError);
expect(logServiceSpy).toHaveBeenCalled();
done();
}
);
});
});

View File

@@ -1,212 +0,0 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* 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 { Injectable } from '@angular/core';
import { from, of, Observable, throwError } from 'rxjs';
import { map, catchError } from 'rxjs/operators';
import { AlfrescoApiService, AppConfigService, LogService } from '@alfresco/adf-core';
import { GroupSearchParam, GroupRoleModel } from '../models/group.model';
@Injectable({
providedIn: 'root'
})
export class GroupCloudService {
constructor(
private apiService: AlfrescoApiService,
private appConfigService: AppConfigService,
private logService: LogService
) {}
/**
* Finds groups filtered by name.
* @param searchParams Object containing the name filter string
* @returns List of group information
*/
findGroupsByName(searchParams: GroupSearchParam): Observable<any> {
if (searchParams.name === '') {
return of([]);
}
const url = this.getGroupsApi();
const httpMethod = 'GET', pathParams = {}, queryParams = {search: searchParams.name}, bodyParam = {}, headerParams = {},
formParams = {}, contentTypes = ['application/json'], accepts = ['application/json'];
return (from(this.apiService.getInstance().oauth2Auth.callCustomApi(
url, httpMethod, pathParams, queryParams,
headerParams, formParams, bodyParam,
contentTypes, accepts, Object, null, null)
)).pipe(
catchError((err) => this.handleError(err))
);
}
/**
* Gets details for a specified group.
* @param groupId ID of the target group
* @returns Group details
*/
getGroupRoles(groupId: string): Observable<GroupRoleModel[]> {
const url = this.buildRolesUrl(groupId);
const httpMethod = 'GET', pathParams = {}, queryParams = {}, bodyParam = {}, headerParams = {},
formParams = {}, contentTypes = ['application/json'], accepts = ['application/json'];
return (from(this.apiService.getInstance().oauth2Auth.callCustomApi(
url, httpMethod, pathParams, queryParams,
headerParams, formParams, bodyParam,
contentTypes, accepts, Object, null, null)
)).pipe(
catchError((err) => this.handleError(err))
);
}
/**
* Check that a group has one or more roles from the supplied list.
* @param groupId ID of the target group
* @param roleNames Array of role names
* @returns True if the group has one or more of the roles, false otherwise
*/
checkGroupHasRole(groupId: string, roleNames: string[]): Observable<boolean> {
return this.getGroupRoles(groupId).pipe(map((groupRoles: GroupRoleModel[]) => {
let hasRole = false;
if (groupRoles && groupRoles.length > 0) {
roleNames.forEach((roleName: string) => {
const role = groupRoles.find((groupRole) => {
return roleName === groupRole.name;
});
if (role) {
hasRole = true;
return;
}
});
}
return hasRole;
}));
}
/**
* Gets the client ID using the app name.
* @param applicationName Name of the app
* @returns client ID string
*/
getClientIdByApplicationName(applicationName: string): Observable<string> {
const url = this.getApplicationIdApi();
const httpMethod = 'GET', pathParams = {}, queryParams = {clientId: applicationName}, bodyParam = {}, headerParams = {}, formParams = {},
contentTypes = ['application/json'], accepts = ['application/json'];
return from(this.apiService.getInstance()
.oauth2Auth.callCustomApi(url, httpMethod, pathParams, queryParams, headerParams,
formParams, bodyParam, contentTypes,
accepts, Object, null, null)
).pipe(
map((response: any[]) => {
const clientId = response && response.length > 0 ? response[0].id : '';
return clientId;
}),
catchError((err) => this.handleError(err))
);
}
/**
* Gets client roles.
* @param groupId ID of the target group
* @param clientId ID of the client
* @returns List of roles
*/
getClientRoles(groupId: string, clientId: string): Observable<any[]> {
const url = this.groupClientRoleMappingApi(groupId, clientId);
const httpMethod = 'GET', pathParams = {}, queryParams = {}, bodyParam = {}, headerParams = {},
formParams = {}, contentTypes = ['application/json'], accepts = ['application/json'];
return from(this.apiService.getInstance().oauth2Auth.callCustomApi(
url, httpMethod, pathParams, queryParams,
headerParams, formParams, bodyParam,
contentTypes, accepts, Object, null, null)
);
}
/**
* Checks if a group has a client app.
* @param groupId ID of the target group
* @param clientId ID of the client
* @returns True if the group has the client app, false otherwise
*/
checkGroupHasClientApp(groupId: string, clientId: string): Observable<boolean> {
return this.getClientRoles(groupId, clientId).pipe(
map((response: any[]) => {
if (response && response.length > 0) {
return true;
}
return false;
}),
catchError((err) => this.handleError(err))
);
}
/**
* Check if a group has any of the client app roles in the supplied list.
* @param groupId ID of the target group
* @param clientId ID of the client
* @param roleNames Array of role names to check
* @returns True if the group has one or more of the roles, false otherwise
*/
checkGroupHasAnyClientAppRole(groupId: string, clientId: string, roleNames: string[]): Observable<boolean> {
return this.getClientRoles(groupId, clientId).pipe(
map((clientRoles: any[]) => {
let hasRole = false;
if (clientRoles.length > 0) {
roleNames.forEach((roleName) => {
const role = clientRoles.find((availableRole) => {
return availableRole.name === roleName;
});
if (role) {
hasRole = true;
return;
}
});
}
return hasRole;
}),
catchError((err) => this.handleError(err))
);
}
private groupClientRoleMappingApi(groupId: string, clientId: string): any {
return `${this.appConfigService.get('identityHost')}/groups/${groupId}/role-mappings/clients/${clientId}`;
}
private getApplicationIdApi() {
return `${this.appConfigService.get('identityHost')}/clients`;
}
private getGroupsApi() {
return `${this.appConfigService.get('identityHost')}/groups`;
}
private buildRolesUrl(groupId: string): any {
return `${this.appConfigService.get('identityHost')}/groups/${groupId}/role-mappings/realm/composite`;
}
/**
* Throw the error
* @param error
*/
private handleError(error: Response) {
this.logService.error(error);
return throwError(error || 'Server error');
}
}