mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[AAE-9019] - People/Group cloud with HxP (#7658)
* Cover the use cases by mocking them * Replace the mock with real stream and remove useless code * Provide new service to fetch groups * Fix group tests * Use the interface and token injection * [AAE-8870] add unit test and mock for new service * Improve roles condifion * initialize the stream as part of NgOnInit to be sure it relies on the correct FormControl instance(input) * Rollback tmp change for roles * [AAE-8641] people abstraction mock * [AAE-8641] revert angular.json changes * [AAE-8641] few conditions and code improvements * [AAE-8641] revert change input controls name * [AAE-8641] initialize the stream as part of ngOnInit * [AAE-8641] people abstraction improvements * [AAE-8870] cherry pick people abstraction * [AAE-8641] fix people-group e2es * fix lint * remove hardcoded identityHost * Use the identityhost api in case of cloud * Solve issue with returnType array string * Rebase and use GroupModel from cloud * Rebase and use GroupModel from cloud * Use the bpmHost instead of identityFor * Add identity ingress for user access service * Rename test * Fix linting issues * Fix playwright storybook e2e for people and group * Trigger travis * Fix people group e2e * improved formatting * Remove not needed travis var override * Remove unused import after rebase * Make roles in filter optional + remove comments Co-authored-by: Tomasz <tomasz.gnyp@hyland.com> Co-authored-by: arditdomi <ardit.domi@hyland.com>
This commit is contained in:
@@ -27,14 +27,17 @@ import {
|
||||
SimpleChanges,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
SimpleChange
|
||||
Inject
|
||||
} from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { trigger, state, style, transition, animate } from '@angular/animations';
|
||||
import { Observable, of, BehaviorSubject, Subject } from 'rxjs';
|
||||
import { distinctUntilChanged, switchMap, mergeMap, filter, tap, map, takeUntil, debounceTime } from 'rxjs/operators';
|
||||
import { IdentityGroupModel, IdentityGroupService, LogService } from '@alfresco/adf-core';
|
||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
import { distinctUntilChanged, switchMap, mergeMap, filter, tap, takeUntil, debounceTime } from 'rxjs/operators';
|
||||
import { LogService } from '@alfresco/adf-core';
|
||||
import { ComponentSelectionMode } from '../../types';
|
||||
import { IdentityGroupModel } from '../models/identity-group.model';
|
||||
import { IdentityGroupServiceInterface } from '../services/identity-group.service.interface';
|
||||
import { IDENTITY_GROUP_SERVICE_TOKEN } from '../services/identity-group-service.token';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-cloud-group',
|
||||
@@ -125,7 +128,6 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
searchGroups$ = new BehaviorSubject<IdentityGroupModel[]>(this.searchGroups);
|
||||
subscriptAnimationState: string = 'enter';
|
||||
clientId: string;
|
||||
isFocused: boolean;
|
||||
touched: boolean = false;
|
||||
|
||||
@@ -135,12 +137,14 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
validationLoading = false;
|
||||
searchLoading = false;
|
||||
|
||||
typingUniqueValueNotEmpty$: Observable<any>;
|
||||
|
||||
constructor(
|
||||
private identityGroupService: IdentityGroupService,
|
||||
@Inject(IDENTITY_GROUP_SERVICE_TOKEN)
|
||||
private identityGroupService: IdentityGroupServiceInterface,
|
||||
private logService: LogService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadClientId();
|
||||
this.initSearch();
|
||||
}
|
||||
|
||||
@@ -157,41 +161,52 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.invalidGroups = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (changes.appName && this.isAppNameChanged(changes.appName)) {
|
||||
this.loadClientId();
|
||||
}
|
||||
}
|
||||
|
||||
private isAppNameChanged(change: SimpleChange): boolean {
|
||||
return change
|
||||
&& change.previousValue !== change.currentValue
|
||||
&& this.appName
|
||||
&& this.appName.length > 0;
|
||||
private initSearch(): void {
|
||||
this.initializeStream();
|
||||
this.typingUniqueValueNotEmpty$.pipe(
|
||||
switchMap((name: string) =>
|
||||
this.identityGroupService.search(name, { roles: this.roles, withinApplication: this.appName })
|
||||
),
|
||||
mergeMap((groups: IdentityGroupModel[]) => {
|
||||
this.resetSearchGroups();
|
||||
this.searchLoading = false;
|
||||
return groups;
|
||||
}),
|
||||
filter(group => !this.isGroupAlreadySelected(group)),
|
||||
takeUntil(this.onDestroy$)
|
||||
).subscribe((searchedGroup: IdentityGroupModel) => {
|
||||
this.searchGroups.push(searchedGroup);
|
||||
this.searchGroups$.next(this.searchGroups);
|
||||
});
|
||||
}
|
||||
|
||||
private async loadClientId(): Promise<void> {
|
||||
this.clientId = await this.identityGroupService.getClientIdByApplicationName(this.appName).toPromise();
|
||||
private initializeStream() {
|
||||
const typingValueFromControl$ = this.searchGroupsControl.valueChanges;
|
||||
|
||||
if (this.clientId) {
|
||||
this.searchGroupsControl.enable();
|
||||
}
|
||||
}
|
||||
|
||||
initSearch(): void {
|
||||
this.searchGroupsControl.valueChanges.pipe(
|
||||
filter((value) => {
|
||||
const typingValueTypeSting$ = typingValueFromControl$.pipe(
|
||||
filter(value => {
|
||||
this.searchLoading = true;
|
||||
return typeof value === 'string';
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
const typingValueHandleErrorMessage$ = typingValueTypeSting$.pipe(
|
||||
tap((value: string) => {
|
||||
if (value) {
|
||||
this.setTypingError();
|
||||
}
|
||||
}),
|
||||
})
|
||||
);
|
||||
|
||||
const typingValueDebouncedUnique$ = typingValueHandleErrorMessage$.pipe(
|
||||
debounceTime(500),
|
||||
distinctUntilChanged(),
|
||||
tap((value) => {
|
||||
distinctUntilChanged()
|
||||
);
|
||||
|
||||
this.typingUniqueValueNotEmpty$ = typingValueDebouncedUnique$.pipe(
|
||||
tap((value: string) => {
|
||||
if (value.trim()) {
|
||||
this.searchedValue = value;
|
||||
} else {
|
||||
@@ -199,42 +214,8 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.searchGroupsControl.markAsUntouched();
|
||||
}
|
||||
}),
|
||||
tap(() => this.resetSearchGroups()),
|
||||
switchMap((name: string) =>
|
||||
this.identityGroupService.findGroupsByName({ name: name.trim() })
|
||||
),
|
||||
mergeMap((groups) => {
|
||||
this.resetSearchGroups();
|
||||
this.searchLoading = false;
|
||||
return groups;
|
||||
}),
|
||||
filter(group => !this.isGroupAlreadySelected(group)),
|
||||
mergeMap(group => {
|
||||
if (this.appName) {
|
||||
return this.checkGroupHasAccess(group.id).pipe(
|
||||
mergeMap(
|
||||
hasRole => hasRole ? of(group) : of()
|
||||
)
|
||||
);
|
||||
} else if (this.hasRoles()) {
|
||||
return this.filterGroupsByRoles(group);
|
||||
} else {
|
||||
return of(group);
|
||||
}
|
||||
}),
|
||||
takeUntil(this.onDestroy$)
|
||||
).subscribe(searchedGroup => {
|
||||
this.searchGroups.push(searchedGroup);
|
||||
this.searchGroups$.next(this.searchGroups);
|
||||
});
|
||||
}
|
||||
|
||||
checkGroupHasAccess(groupId: string): Observable<boolean> {
|
||||
if (this.hasRoles()) {
|
||||
return this.identityGroupService.checkGroupHasAnyClientAppRole(groupId, this.clientId, this.roles);
|
||||
} else {
|
||||
return this.identityGroupService.checkGroupHasClientApp(groupId, this.clientId);
|
||||
}
|
||||
tap(() => this.resetSearchGroups())
|
||||
);
|
||||
}
|
||||
|
||||
private isGroupAlreadySelected(group: IdentityGroupModel): boolean {
|
||||
@@ -246,8 +227,8 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
return false;
|
||||
}
|
||||
|
||||
async searchGroup(name: string): Promise<IdentityGroupModel> {
|
||||
return (await this.identityGroupService.findGroupsByName({ name }).toPromise())[0];
|
||||
private async searchGroup(name: string): Promise<IdentityGroupModel> {
|
||||
return (await this.identityGroupService.search(name).toPromise())[0];
|
||||
}
|
||||
|
||||
private getPreselectedGroups(): IdentityGroupModel[] {
|
||||
@@ -258,7 +239,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
async validatePreselectGroups(): Promise<any> {
|
||||
private async validatePreselectGroups(): Promise<any> {
|
||||
this.invalidGroups = [];
|
||||
|
||||
for (const group of this.getPreselectedGroups()) {
|
||||
@@ -276,7 +257,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.checkPreselectValidationErrors();
|
||||
}
|
||||
|
||||
checkPreselectValidationErrors(): void {
|
||||
private checkPreselectValidationErrors(): void {
|
||||
this.invalidGroups = this.removeDuplicatedGroups(this.invalidGroups);
|
||||
|
||||
if (this.invalidGroups.length > 0) {
|
||||
@@ -289,7 +270,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
generateInvalidGroupsMessage(): void {
|
||||
private generateInvalidGroupsMessage(): void {
|
||||
this.validateGroupsMessage = '';
|
||||
|
||||
this.invalidGroups.forEach((invalidGroup: IdentityGroupModel, index) => {
|
||||
@@ -317,13 +298,6 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
filterGroupsByRoles(group: IdentityGroupModel): Observable<IdentityGroupModel> {
|
||||
return this.identityGroupService.checkGroupHasRole(group.id, this.roles).pipe(
|
||||
map((hasRole: boolean) => ({ hasRole, group })),
|
||||
filter((filteredGroup: { hasRole: boolean; group: IdentityGroupModel }) => filteredGroup.hasRole),
|
||||
map((filteredGroup: { hasRole: boolean; group: IdentityGroupModel }) => filteredGroup.group));
|
||||
}
|
||||
|
||||
onSelect(group: IdentityGroupModel): void {
|
||||
if (group) {
|
||||
this.selectGroup.emit(group);
|
||||
@@ -365,6 +339,19 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private isPreselectedGroupInvalid(preselectedGroup: IdentityGroupModel, validatedGroup: IdentityGroupModel): boolean {
|
||||
if (validatedGroup && validatedGroup.name !== undefined) {
|
||||
return preselectedGroup.name !== validatedGroup.name;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
removeDuplicatedGroups(groups: IdentityGroupModel[]): IdentityGroupModel[] {
|
||||
return groups.filter((group, index, self) =>
|
||||
index === self.findIndex((auxGroup) => group.id === auxGroup.id && group.name === auxGroup.name));
|
||||
}
|
||||
|
||||
private groupChipsCtrlValue(value: string) {
|
||||
this.groupChipsCtrl.setValue(value);
|
||||
this.groupChipsCtrl.markAsDirty();
|
||||
@@ -392,43 +379,18 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.searchGroups$.next(this.searchGroups);
|
||||
}
|
||||
|
||||
isPreselectedGroupInvalid(preselectedGroup: IdentityGroupModel, validatedGroup: IdentityGroupModel): boolean {
|
||||
if (validatedGroup && validatedGroup.name !== undefined) {
|
||||
return preselectedGroup.name !== validatedGroup.name;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
isSingleMode(): boolean {
|
||||
return this.mode === 'single';
|
||||
}
|
||||
|
||||
private isSingleSelectionReadonly(): boolean {
|
||||
return this.isSingleMode() && this.selectedGroups.length === 1 && this.selectedGroups[0].readonly === true;
|
||||
}
|
||||
|
||||
hasPreselectError(): boolean {
|
||||
return this.invalidGroups && this.invalidGroups.length > 0;
|
||||
private isSingleMode(): boolean {
|
||||
return this.mode === 'single';
|
||||
}
|
||||
|
||||
isReadonly(): boolean {
|
||||
return this.readOnly || this.isSingleSelectionReadonly();
|
||||
}
|
||||
|
||||
isMultipleMode(): boolean {
|
||||
private isMultipleMode(): boolean {
|
||||
return this.mode === 'multiple';
|
||||
}
|
||||
|
||||
getDisplayName(group: IdentityGroupModel): string {
|
||||
return group ? group.name : '';
|
||||
}
|
||||
|
||||
removeDuplicatedGroups(groups: IdentityGroupModel[]): IdentityGroupModel[] {
|
||||
return groups.filter((group, index, self) =>
|
||||
index === self.findIndex((auxGroup) => group.id === auxGroup.id && group.name === auxGroup.name));
|
||||
}
|
||||
|
||||
private hasPreSelectGroups(): boolean {
|
||||
return this.preSelectGroups && this.preSelectGroups.length > 0;
|
||||
}
|
||||
@@ -457,10 +419,6 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
&& changes.preSelectGroups.currentValue.length === 0;
|
||||
}
|
||||
|
||||
private hasRoles(): boolean {
|
||||
return this.roles && this.roles.length > 0;
|
||||
}
|
||||
|
||||
private setTypingError(): void {
|
||||
this.searchGroupsControl.setErrors({
|
||||
searchTypingError: true,
|
||||
@@ -468,6 +426,18 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
hasPreselectError(): boolean {
|
||||
return this.invalidGroups && this.invalidGroups.length > 0;
|
||||
}
|
||||
|
||||
isReadonly(): boolean {
|
||||
return this.readOnly || this.isSingleSelectionReadonly();
|
||||
}
|
||||
|
||||
getDisplayName(group: IdentityGroupModel): string {
|
||||
return group ? group.name : '';
|
||||
}
|
||||
|
||||
hasError(): boolean {
|
||||
return !!this.searchGroupsControl.errors;
|
||||
}
|
||||
|
Reference in New Issue
Block a user