mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +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:
parent
93c5619e23
commit
e27833d770
@ -181,7 +181,7 @@ jobs:
|
||||
use:
|
||||
- built_libs_cache
|
||||
- built_demo_shell_cache
|
||||
|
||||
|
||||
- stage: "e2e Test"
|
||||
name: "Core"
|
||||
before_script:
|
||||
|
@ -843,8 +843,5 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "demoshell",
|
||||
"cli": {
|
||||
"analytics": false
|
||||
}
|
||||
}
|
||||
"defaultProject": "demoshell"
|
||||
}
|
||||
|
@ -332,7 +332,8 @@
|
||||
"ALL_PRESELECTED_GROUPS": "All Preselected Groups",
|
||||
"INVALID_USERS": "Invalid Users",
|
||||
"INVALID_GROUPS": "Invalid Groups",
|
||||
"READONLY_MODE": "Read-only Mode"
|
||||
"READONLY_MODE": "Read-only Mode",
|
||||
"GROUPS_RESTRICTION": "Groups Restriction"
|
||||
},
|
||||
"SETTINGS_CLOUD": {
|
||||
"MULTISELECTION": "Multiselection",
|
||||
|
@ -12,20 +12,22 @@
|
||||
'PEOPLE_GROUPS_CLOUD.MULTI' | translate }}</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<div class="app-people-control-options">
|
||||
<mat-radio-group (change)="onChangePeopleFilterMode($event)">
|
||||
<mat-radio-button [checked]="true" class="app-people-single-mode" value="appName">{{
|
||||
'PEOPLE_GROUPS_CLOUD.APP_FILTER_MODE' | translate }}</mat-radio-button>
|
||||
<mat-radio-button class="app-people-multiple-mode" data-automation-id="app-people-filter-role" value="role">{{
|
||||
'PEOPLE_GROUPS_CLOUD.ROLE_FILTER_MODE' | translate }}</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<mat-form-field *ngIf="!isPeopleAppNameSelected()" class="app-preselect-value">
|
||||
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.ROLE' | translate }} ["ACTIVITI_ADMIN", "ACTIVITI_USER"]</mat-label>
|
||||
<input matInput (input)="setPeopleRoles($any($event).target?.value)" data-automation-id="app-people-roles-input" />
|
||||
</mat-form-field>
|
||||
<mat-form-field *ngIf="isPeopleAppNameSelected()" class="app-preselect-value">
|
||||
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.APP_NAME' | translate }}</mat-label>
|
||||
<input matInput (input)="setPeopleAppName($any($event).target?.value)" data-automation-id="app-people-app-input" />
|
||||
</mat-form-field>
|
||||
<mat-form-field class="app-preselect-value">
|
||||
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.ROLE' | translate }} ["ACTIVITI_ADMIN", "ACTIVITI_USER"]</mat-label>
|
||||
<input matInput
|
||||
(input)="setPeopleRoles($any($event).target?.value)"
|
||||
data-automation-id="app-people-roles-input"/>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="app-preselect-value">
|
||||
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.GROUPS_RESTRICTION' | translate }} ["hr", "sales"]</mat-label>
|
||||
<input matInput
|
||||
(input)="setPeopleGroupsRestriction($any($event).target?.value)"
|
||||
data-automation-id="app-people-groups-restriction-input"/>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="app-preselect-value-full">
|
||||
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.PRESELECTED_VALUE' | translate }} {{ defaultPeoplePlaceholder }}</mat-label>
|
||||
<input matInput (input)="setPeoplePreselectValue($any($event).target?.value)" data-automation-id="app-people-preselect-input" />
|
||||
@ -42,6 +44,7 @@
|
||||
[validate]="peoplePreselectValidation"
|
||||
[appName]="peopleAppName"
|
||||
[roles]="peopleRoles"
|
||||
[groupsRestriction]="groupsRestriction"
|
||||
[title]="'ADF_TASK_LIST.START_TASK.FORM.LABEL.ASSIGNEE'"
|
||||
[mode]="peopleMode"
|
||||
(warning)="onUsersWarning($event)"></adf-cloud-people>
|
||||
@ -83,24 +86,18 @@
|
||||
'PEOPLE_GROUPS_CLOUD.MULTI' | translate }}</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<div class="app-groups-control-options">
|
||||
<mat-radio-group (change)="onChangeGroupsFilterMode($event)">
|
||||
<mat-radio-button [checked]="true" class="app-people-single-mode" value="appName">{{
|
||||
'PEOPLE_GROUPS_CLOUD.APP_FILTER_MODE' | translate }}</mat-radio-button>
|
||||
<mat-radio-button class="app-people-multiple-mode" data-automation-id="app-group-filter-role" value="role">{{
|
||||
'PEOPLE_GROUPS_CLOUD.ROLE_FILTER_MODE' | translate }}</mat-radio-button>
|
||||
</mat-radio-group>
|
||||
<mat-form-field *ngIf="!isGroupAppNameSelected()" class="app-preselect-value">
|
||||
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.ROLE' | translate }} ["ACTIVITI_ADMIN", "ACTIVITI_USER"]</mat-label>
|
||||
<input matInput
|
||||
(input)="setGroupRoles($any($event).target?.value)"
|
||||
data-automation-id="app-group-roles-input"/>
|
||||
</mat-form-field>
|
||||
<mat-form-field *ngIf="isGroupAppNameSelected()" class="app-preselect-value">
|
||||
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.APP_NAME' | translate }}</mat-label>
|
||||
<input matInput
|
||||
(input)="setGroupAppName($any($event).target?.value)"
|
||||
data-automation-id="app-group-app-input"/>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="app-preselect-value">
|
||||
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.ROLE' | translate }} ["ACTIVITI_ADMIN", "ACTIVITI_USER"]</mat-label>
|
||||
<input matInput
|
||||
(input)="setGroupRoles($any($event).target?.value)"
|
||||
data-automation-id="app-group-roles-input"/>
|
||||
</mat-form-field>
|
||||
<mat-form-field class="app-preselect-value-full">
|
||||
<mat-label>Preselect: {{ defaultGroupPlaceholder }}</mat-label>
|
||||
<input matInput
|
||||
|
@ -16,10 +16,9 @@
|
||||
*/
|
||||
|
||||
import { Component, ViewEncapsulation } from '@angular/core';
|
||||
import { ComponentSelectionMode } from '@alfresco/adf-process-services-cloud';
|
||||
import { ComponentSelectionMode, IdentityUserModel, IdentityGroupModel } from '@alfresco/adf-process-services-cloud';
|
||||
import { MatCheckboxChange } from '@angular/material/checkbox';
|
||||
import { MatRadioChange } from '@angular/material/radio';
|
||||
import { IdentityGroupModel, IdentityUserModel } from '@alfresco/adf-core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-people-groups-cloud',
|
||||
@ -37,6 +36,7 @@ export class PeopleGroupCloudDemoComponent {
|
||||
preSelectUsers: IdentityUserModel[] = [];
|
||||
invalidUsers: IdentityUserModel[] = [];
|
||||
peopleRoles: string[] = [];
|
||||
groupsRestriction: string[] = [];
|
||||
peopleAppName: string;
|
||||
peopleFilterMode: string = this.defaultFilterMode;
|
||||
peoplePreselectValidation = false;
|
||||
@ -75,6 +75,10 @@ export class PeopleGroupCloudDemoComponent {
|
||||
this.groupAppName = value;
|
||||
}
|
||||
|
||||
setPeopleGroupsRestriction(value: string): void {
|
||||
this.groupsRestriction = this.getArrayFromString(value);
|
||||
}
|
||||
|
||||
onChangePeopleMode(event: MatRadioChange): void {
|
||||
this.peopleMode = event.value;
|
||||
}
|
||||
|
@ -23,6 +23,6 @@ export class PeopleComponent extends BaseComponent {
|
||||
super(page, rootElement);
|
||||
}
|
||||
|
||||
public getUserLocator = (userName: 'userName1' | 'userName2') => this.getChild(`[data-automation-id="adf-people-cloud-chip-${userName}"]`);
|
||||
public getUserLocator = (userName: string) => this.getChild(`[data-automation-id="adf-people-cloud-chip-${userName}"]`);
|
||||
|
||||
}
|
||||
|
@ -13,11 +13,8 @@ test.describe.configure({ mode: 'parallel' });
|
||||
test.describe('Groups component stories tests', () => {
|
||||
test('Valid Preselected Groups', async ({ processServicesCloud, groupComponent }) => {
|
||||
const expectedUsersName = `
|
||||
Mock Group 1 cancel
|
||||
Mock Group 2 cancel
|
||||
Mock Group 3 cancel
|
||||
Mock Group 4 cancel
|
||||
Mock Group 5
|
||||
Vegetable Aubergine cancel
|
||||
Meat Chicken cancel
|
||||
`;
|
||||
|
||||
await processServicesCloud.navigateTo({ componentName: 'group', story: 'valid-preselected-groups' });
|
||||
@ -27,20 +24,19 @@ test.describe('Groups component stories tests', () => {
|
||||
|
||||
test('Mandatory Preselected Groups', async ({ processServicesCloud, groupComponent }) => {
|
||||
const expectedUsersName = `
|
||||
Mock Group 1
|
||||
Mock Group 2 cancel
|
||||
Mock Group 3
|
||||
Vegetable Aubergine cancel
|
||||
Meat Chicken
|
||||
`;
|
||||
|
||||
await processServicesCloud.navigateTo({ componentName: 'group', story: 'mandatory-preselected-groups' });
|
||||
await expect.soft(groupComponent.groupNaming).toContainText(expectedUsersName);
|
||||
|
||||
await groupComponent.getUserLocator('Mock Group 1').hover();
|
||||
await groupComponent.getUserLocator('Meat Chicken').hover();
|
||||
await expect(groupComponent.tooltip.content).toContainText('Mandatory');
|
||||
});
|
||||
|
||||
test('Invalid Preselected Groups', async ({ processServicesCloud, groupComponent }) => {
|
||||
const expectedWarningMessage = 'No group found with the name invalid groups';
|
||||
const expectedWarningMessage = 'No group found with the name Invalid Group';
|
||||
const expectedWarningIcon = 'error_outline';
|
||||
|
||||
await processServicesCloud.navigateTo({ componentName: 'group', story: 'invalid-preselected-groups' });
|
||||
|
@ -13,11 +13,9 @@ test.describe.configure({ mode: 'parallel' });
|
||||
test.describe('People component stories tests', () => {
|
||||
test('Valid Preselected Users', async ({ processServicesCloud, peopleComponent }) => {
|
||||
const expectedUsersName = `
|
||||
first-name-1 last-name-1 cancel
|
||||
first-name-2 last-name-2 cancel
|
||||
first-name-3 last-name-3 cancel
|
||||
first-name-4 last-name-4 cancel
|
||||
first-name-5 last-name-5
|
||||
Yorkshire Pudding cancel
|
||||
Shepherds Pie cancel
|
||||
Kielbasa Sausage cancel
|
||||
`;
|
||||
|
||||
await processServicesCloud.navigateTo({ componentName: 'people', story: 'valid-preselected-users' });
|
||||
@ -27,19 +25,19 @@ test.describe('People component stories tests', () => {
|
||||
|
||||
test('Mandatory Preselected Users', async ({ processServicesCloud, peopleComponent }) => {
|
||||
const expectedUsersName = `
|
||||
first-name-1 last-name-1
|
||||
first-name-2 last-name-2 cancel
|
||||
Kielbasa Sausage
|
||||
Shepherds Pie cancel
|
||||
`;
|
||||
|
||||
await processServicesCloud.navigateTo({ componentName: 'people', story: 'mandatory-preselected-users' });
|
||||
await peopleComponent.getUserLocator('userName1').hover();
|
||||
await peopleComponent.getUserLocator('Kielbasa Sausage').hover();
|
||||
|
||||
await expect.soft(peopleComponent.usersNaming).toContainText(expectedUsersName);
|
||||
await expect(peopleComponent.tooltip.content).toContainText('Mandatory');
|
||||
});
|
||||
|
||||
test('Invalid Preselected Users', async ({ processServicesCloud, peopleComponent }) => {
|
||||
const expectedWarningMessage = 'No user found with the username invalid user';
|
||||
const expectedWarningMessage = 'No user found with the username Invalid User';
|
||||
const expectedWarningIcon = 'error_outline';
|
||||
|
||||
await processServicesCloud.navigateTo({ componentName: 'people', story: 'invalid-preselected-users' });
|
||||
@ -49,8 +47,8 @@ test.describe('People component stories tests', () => {
|
||||
|
||||
test('Excluded Users', async ({ processServicesCloud, peopleComponent }) => {
|
||||
const expectedExcludedUsers = `
|
||||
mocked-user-id-2
|
||||
mocked-user-id-3
|
||||
kielbasa
|
||||
yorkshire
|
||||
`;
|
||||
|
||||
await processServicesCloud.navigateTo({ componentName: 'people', story: 'excluded-users' });
|
||||
|
@ -24,10 +24,8 @@ export class PeopleGroupCloudComponentPage {
|
||||
peopleCloudMultipleSelectionChecked = $('mat-radio-button[data-automation-id="app-people-multiple-mode"][class*="mat-radio-checked"]');
|
||||
peopleCloudSingleSelection = $('mat-radio-button[data-automation-id="app-people-single-mode"]');
|
||||
peopleCloudMultipleSelection = $('mat-radio-button[data-automation-id="app-people-multiple-mode"]');
|
||||
peopleCloudFilterRole = $('mat-radio-button[data-automation-id="app-people-filter-role"]');
|
||||
groupCloudSingleSelection = $('mat-radio-button[data-automation-id="app-group-single-mode"]');
|
||||
groupCloudMultipleSelection = $('mat-radio-button[data-automation-id="app-group-multiple-mode"]');
|
||||
groupCloudFilterRole = $('mat-radio-button[data-automation-id="app-group-filter-role"]');
|
||||
peopleRoleInput = $('input[data-automation-id="app-people-roles-input"]');
|
||||
peopleAppInput = $('input[data-automation-id="app-people-app-input"]');
|
||||
peoplePreselect = $('input[data-automation-id="app-people-preselect-input"]');
|
||||
@ -37,8 +35,6 @@ export class PeopleGroupCloudComponentPage {
|
||||
groupCloudComponentTitle = element(by.cssContainingText('mat-card-title', 'Groups Cloud Component'));
|
||||
preselectValidation = $$('mat-checkbox.app-preselect-value').first();
|
||||
preselectValidationStatus = $$('mat-checkbox.app-preselect-value label input').first();
|
||||
peopleFilterByAppName = $('.app-people-control-options mat-radio-button[value="appName"]');
|
||||
groupFilterByAppName = $('.app-groups-control-options mat-radio-button[value="appName"]');
|
||||
|
||||
async navigateTo() {
|
||||
await browser.get('#/cloud/people-group-cloud');
|
||||
@ -69,18 +65,6 @@ export class PeopleGroupCloudComponentPage {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.peopleCloudMultipleSelectionChecked);
|
||||
}
|
||||
|
||||
async checkPeopleCloudFilterRole(): Promise<void> {
|
||||
await BrowserVisibility.waitUntilElementIsVisible(this.peopleCloudFilterRole);
|
||||
}
|
||||
|
||||
async clickPeopleCloudFilterRole(): Promise<void> {
|
||||
await BrowserActions.click(this.peopleCloudFilterRole);
|
||||
}
|
||||
|
||||
async clickGroupCloudFilterRole(): Promise<void> {
|
||||
await BrowserActions.click(this.groupCloudFilterRole);
|
||||
}
|
||||
|
||||
async enterPeopleRoles(roles: string): Promise<void> {
|
||||
await BrowserActions.clearSendKeys(this.peopleRoleInput, roles);
|
||||
}
|
||||
@ -113,14 +97,6 @@ export class PeopleGroupCloudComponentPage {
|
||||
return BrowserActions.getAttribute(this.preselectValidationStatus, 'aria-checked');
|
||||
}
|
||||
|
||||
async clickPeopleFilerByApp(): Promise<void> {
|
||||
await BrowserActions.click(this.peopleFilterByAppName);
|
||||
}
|
||||
|
||||
async clickGroupFilerByApp(): Promise<void> {
|
||||
await BrowserActions.click(this.groupFilterByAppName);
|
||||
}
|
||||
|
||||
async enterPeopleAppName(appName: string): Promise<void> {
|
||||
await BrowserActions.clearSendKeys(this.peopleAppInput, appName);
|
||||
}
|
||||
|
@ -29,224 +29,220 @@ import { NavigationBarPage } from '../../core/pages/navigation-bar.page';
|
||||
|
||||
describe('People Groups Cloud Component', () => {
|
||||
|
||||
describe('People Groups Cloud Component', () => {
|
||||
const loginSSOPage = new LoginPage();
|
||||
const navigationBarPage = new NavigationBarPage();
|
||||
const peopleGroupCloudComponentPage = new PeopleGroupCloudComponentPage();
|
||||
const peopleCloudComponent = new PeopleCloudComponentPage();
|
||||
const groupCloudComponentPage = new GroupCloudComponentPage();
|
||||
|
||||
const loginSSOPage = new LoginPage();
|
||||
const navigationBarPage = new NavigationBarPage();
|
||||
const peopleGroupCloudComponentPage = new PeopleGroupCloudComponentPage();
|
||||
const peopleCloudComponent = new PeopleCloudComponentPage();
|
||||
const groupCloudComponentPage = new GroupCloudComponentPage();
|
||||
const apiService = createApiService();
|
||||
const identityService = new IdentityService(apiService);
|
||||
const rolesService = new RolesService(apiService);
|
||||
const groupIdentityService = new GroupIdentityService(apiService);
|
||||
|
||||
const apiService = createApiService();
|
||||
const identityService = new IdentityService(apiService);
|
||||
const rolesService = new RolesService(apiService);
|
||||
const groupIdentityService = new GroupIdentityService(apiService);
|
||||
let apsUser;
|
||||
let testUser;
|
||||
let devopsUser;
|
||||
let activitiUser;
|
||||
let multipleRolesUser;
|
||||
let noRoleUser;
|
||||
let groupUser;
|
||||
let groupAdmin;
|
||||
let groupNoRole;
|
||||
let groupMultipleRoles;
|
||||
let apsUserRoleId: string;
|
||||
let apsAdminRoleId: string;
|
||||
let users = [];
|
||||
let groups = [];
|
||||
|
||||
let apsUser;
|
||||
let testUser;
|
||||
let devopsUser;
|
||||
let activitiUser;
|
||||
let noRoleUser;
|
||||
let groupUser;
|
||||
let groupAdmin;
|
||||
let groupNoRole;
|
||||
let apsUserRoleId: string;
|
||||
let apsAdminRoleId: string;
|
||||
let users = [];
|
||||
let groups = [];
|
||||
beforeAll(async () => {
|
||||
await apiService.loginWithProfile('identityAdmin');
|
||||
|
||||
beforeAll(async () => {
|
||||
await apiService.loginWithProfile('identityAdmin');
|
||||
testUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]);
|
||||
apsUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]);
|
||||
activitiUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]);
|
||||
devopsUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_DEVOPS]);
|
||||
multipleRolesUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER, identityService.ROLES.ACTIVITI_ADMIN]);
|
||||
noRoleUser = await identityService.createIdentityUser();
|
||||
|
||||
testUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]);
|
||||
apsUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]);
|
||||
activitiUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]);
|
||||
devopsUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_DEVOPS]);
|
||||
noRoleUser = await identityService.createIdentityUser();
|
||||
apsAdminRoleId = await rolesService.getRoleIdByRoleName(identityService.ROLES.ACTIVITI_ADMIN);
|
||||
apsUserRoleId = await rolesService.getRoleIdByRoleName(identityService.ROLES.ACTIVITI_USER);
|
||||
|
||||
apsAdminRoleId = await rolesService.getRoleIdByRoleName(identityService.ROLES.ACTIVITI_ADMIN);
|
||||
apsUserRoleId = await rolesService.getRoleIdByRoleName(identityService.ROLES.ACTIVITI_USER);
|
||||
groupUser = await groupIdentityService.createIdentityGroup();
|
||||
await groupIdentityService.assignRole(groupUser.id, apsUserRoleId, identityService.ROLES.ACTIVITI_USER);
|
||||
|
||||
groupUser = await groupIdentityService.createIdentityGroup();
|
||||
await groupIdentityService.assignRole(groupUser.id, apsUserRoleId, identityService.ROLES.ACTIVITI_USER);
|
||||
groupAdmin = await groupIdentityService.createIdentityGroup();
|
||||
await groupIdentityService.assignRole(groupAdmin.id, apsAdminRoleId, identityService.ROLES.ACTIVITI_ADMIN);
|
||||
|
||||
groupAdmin = await groupIdentityService.createIdentityGroup();
|
||||
await groupIdentityService.assignRole(groupAdmin.id, apsAdminRoleId, identityService.ROLES.ACTIVITI_ADMIN);
|
||||
groupMultipleRoles = await groupIdentityService.createIdentityGroup();
|
||||
await groupIdentityService.assignRole(groupMultipleRoles.id, apsAdminRoleId, identityService.ROLES.ACTIVITI_ADMIN);
|
||||
await groupIdentityService.assignRole(groupMultipleRoles.id, apsUserRoleId, identityService.ROLES.ACTIVITI_USER);
|
||||
|
||||
groupNoRole = await groupIdentityService.createIdentityGroup();
|
||||
groupNoRole = await groupIdentityService.createIdentityGroup();
|
||||
|
||||
users = [`${apsUser.idIdentityService}`, `${activitiUser.idIdentityService}`, `${noRoleUser.idIdentityService}`,
|
||||
`${testUser.idIdentityService}`, `${devopsUser.idIdentityService}`];
|
||||
groups = [`${groupUser.id}`, `${groupAdmin.id}`, `${groupNoRole.id}`];
|
||||
users = [`${apsUser.idIdentityService}`, `${activitiUser.idIdentityService}`, `${noRoleUser.idIdentityService}`,
|
||||
`${testUser.idIdentityService}`, `${devopsUser.idIdentityService}`, `${multipleRolesUser.idIdentityService}`];
|
||||
groups = [`${groupUser.id}`, `${groupAdmin.id}`, `${groupNoRole.id}`, `${groupMultipleRoles.id}`];
|
||||
|
||||
await loginSSOPage.login(testUser.username, testUser.password);
|
||||
});
|
||||
await loginSSOPage.login(testUser.username, testUser.password);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await apiService.loginWithProfile('identityAdmin');
|
||||
for (const user of users) {
|
||||
await identityService.deleteIdentityUser(user);
|
||||
}
|
||||
for (const group of groups) {
|
||||
await groupIdentityService.deleteIdentityGroup(group);
|
||||
}
|
||||
});
|
||||
afterAll(async () => {
|
||||
await apiService.loginWithProfile('identityAdmin');
|
||||
for (const user of users) {
|
||||
await identityService.deleteIdentityUser(user);
|
||||
}
|
||||
for (const group of groups) {
|
||||
await groupIdentityService.deleteIdentityGroup(group);
|
||||
}
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
await navigationBarPage.navigateToPeopleGroupCloudPage();
|
||||
await peopleGroupCloudComponentPage.checkGroupsCloudComponentTitleIsDisplayed();
|
||||
await peopleGroupCloudComponentPage.checkPeopleCloudComponentTitleIsDisplayed();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await browser.refresh();
|
||||
});
|
||||
|
||||
describe('[C297674] Should be able to add filtering to People Cloud Component', () => {
|
||||
beforeEach(async () => {
|
||||
await navigationBarPage.navigateToPeopleGroupCloudPage();
|
||||
await peopleGroupCloudComponentPage.checkGroupsCloudComponentTitleIsDisplayed();
|
||||
await peopleGroupCloudComponentPage.checkPeopleCloudComponentTitleIsDisplayed();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await browser.refresh();
|
||||
});
|
||||
|
||||
describe('[C297674] Should be able to add filtering to People Cloud Component', () => {
|
||||
beforeEach(async () => {
|
||||
await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection();
|
||||
await peopleGroupCloudComponentPage.checkPeopleCloudMultipleSelectionIsSelected();
|
||||
await peopleGroupCloudComponentPage.clickPeopleCloudFilterRole();
|
||||
await peopleGroupCloudComponentPage.checkPeopleCloudFilterRole();
|
||||
});
|
||||
|
||||
it('No role filtering', async () => {
|
||||
await peopleCloudComponent.searchAssignee(noRoleUser.lastName);
|
||||
await peopleCloudComponent.checkUserIsDisplayed(`${noRoleUser.firstName} ${noRoleUser.lastName}`);
|
||||
await peopleCloudComponent.searchAssignee(apsUser.lastName);
|
||||
await peopleCloudComponent.checkUserIsDisplayed(`${apsUser.firstName} ${apsUser.lastName}`);
|
||||
await peopleCloudComponent.searchAssignee(testUser.lastName);
|
||||
await peopleCloudComponent.checkUserIsDisplayed(`${testUser.firstName} ${testUser.lastName}`);
|
||||
});
|
||||
|
||||
it('One role filtering', async () => {
|
||||
await peopleGroupCloudComponentPage.enterPeopleRoles(`["${identityService.ROLES.ACTIVITI_USER}"]`);
|
||||
await peopleCloudComponent.searchAssignee(apsUser.lastName);
|
||||
await peopleCloudComponent.checkUserIsDisplayed(`${apsUser.firstName} ${apsUser.lastName}`);
|
||||
await peopleCloudComponent.searchAssignee(devopsUser.lastName);
|
||||
await peopleCloudComponent.checkNoResultsFoundError();
|
||||
await peopleCloudComponent.searchAssignee(noRoleUser.lastName);
|
||||
await peopleCloudComponent.checkNoResultsFoundError();
|
||||
});
|
||||
|
||||
it('Multiple roles filtering', async () => {
|
||||
await peopleGroupCloudComponentPage.enterPeopleRoles(`["${identityService.ROLES.ACTIVITI_USER}", "${identityService.ROLES.ACTIVITI_USER}"]`);
|
||||
await peopleCloudComponent.searchAssignee(apsUser.lastName);
|
||||
await peopleCloudComponent.checkUserIsDisplayed(`${apsUser.firstName} ${apsUser.lastName}`);
|
||||
await peopleCloudComponent.searchAssignee(testUser.lastName);
|
||||
await peopleCloudComponent.checkUserIsDisplayed(`${testUser.firstName} ${testUser.lastName}`);
|
||||
await peopleCloudComponent.searchAssignee(noRoleUser.lastName);
|
||||
await peopleCloudComponent.checkNoResultsFoundError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('[C309674] Should be able to add filtering to Group Cloud Component', () => {
|
||||
beforeEach(async () => {
|
||||
await peopleGroupCloudComponentPage.clickGroupCloudMultipleSelection();
|
||||
await peopleGroupCloudComponentPage.clickGroupCloudFilterRole();
|
||||
});
|
||||
|
||||
it('No role filtering', async () => {
|
||||
await peopleGroupCloudComponentPage.clearField(peopleGroupCloudComponentPage.groupRoleInput);
|
||||
await groupCloudComponentPage.searchGroups(groupNoRole.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(groupNoRole.name);
|
||||
await groupCloudComponentPage.searchGroups(groupAdmin.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(groupAdmin.name);
|
||||
await groupCloudComponentPage.searchGroups(groupUser.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(groupUser.name);
|
||||
});
|
||||
|
||||
it('One role filtering', async () => {
|
||||
await peopleGroupCloudComponentPage.enterGroupRoles(`["${identityService.ROLES.ACTIVITI_ADMIN}"]`);
|
||||
await groupCloudComponentPage.searchGroups(groupAdmin.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(groupAdmin.name);
|
||||
await groupCloudComponentPage.searchGroups(groupUser.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupAdmin.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupUser.name);
|
||||
await groupCloudComponentPage.searchGroups(groupNoRole.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupNoRole.name);
|
||||
});
|
||||
|
||||
it('[C309996] Should be able to filter groups based on composite roles ACTIVITI_USER', async () => {
|
||||
await peopleGroupCloudComponentPage.enterGroupRoles(`["${identityService.ROLES.ACTIVITI_USER}"]`);
|
||||
await groupCloudComponentPage.searchGroups(groupAdmin.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupAdmin.name);
|
||||
await groupCloudComponentPage.searchGroups(groupNoRole.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupNoRole.name);
|
||||
await groupCloudComponentPage.searchGroups(groupUser.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(groupUser.name);
|
||||
});
|
||||
|
||||
it('Multiple roles filtering', async () => {
|
||||
await peopleGroupCloudComponentPage.enterGroupRoles(`["${identityService.ROLES.ACTIVITI_ADMIN}", "${identityService.ROLES.ACTIVITI_USER}"]`);
|
||||
await groupCloudComponentPage.searchGroups(groupAdmin.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(groupAdmin.name);
|
||||
await groupCloudComponentPage.searchGroups(groupUser.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(groupUser.name);
|
||||
await groupCloudComponentPage.searchGroups(groupNoRole.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupNoRole.name);
|
||||
});
|
||||
});
|
||||
|
||||
it('[C305033] Should fetch the preselect users based on the Validate flag set to True in Single mode selection', async () => {
|
||||
await peopleGroupCloudComponentPage.clickPeopleCloudSingleSelection();
|
||||
await peopleGroupCloudComponentPage.checkPeopleCloudSingleSelectionIsSelected();
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect('[{"id":"12345","username":"someUsername","email":"someEmail"}]');
|
||||
await expect(await peopleCloudComponent.checkSelectedPeople('someUsername'));
|
||||
|
||||
await peopleGroupCloudComponentPage.clickPreselectValidation();
|
||||
await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('true');
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"id":"${noRoleUser.idIdentityService}"}]`);
|
||||
await expect(await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`));
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"email":"${apsUser.email}"}]`);
|
||||
await expect(await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`));
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"username":"${testUser.username}"}]`);
|
||||
await expect(await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`));
|
||||
});
|
||||
|
||||
it('[C309676] Should fetch the preselect users based on the Validate flag set to True in Multiple mode selection', async () => {
|
||||
await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection();
|
||||
await peopleGroupCloudComponentPage.checkPeopleCloudMultipleSelectionIsSelected();
|
||||
await peopleGroupCloudComponentPage.clickPreselectValidation();
|
||||
await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('true');
|
||||
});
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"id":"${apsUser.idIdentityService}"},{"id":"${testUser.idIdentityService}"},` +
|
||||
`{"id":"${noRoleUser.idIdentityService}"}]`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`);
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"email":"${apsUser.email}"},{"email":"${testUser.email}"},{"email":"${noRoleUser.email}"}]`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`);
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"username":"${apsUser.username}"},{"username":"${testUser.username}"},` +
|
||||
`{"username":"${noRoleUser.username}"}]`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`);
|
||||
it('No role filtering', async () => {
|
||||
await peopleCloudComponent.searchAssignee(noRoleUser.lastName);
|
||||
await peopleCloudComponent.checkUserIsDisplayed(`${noRoleUser.firstName} ${noRoleUser.lastName}`);
|
||||
await peopleCloudComponent.searchAssignee(apsUser.lastName);
|
||||
await peopleCloudComponent.checkUserIsDisplayed(`${apsUser.firstName} ${apsUser.lastName}`);
|
||||
await peopleCloudComponent.searchAssignee(testUser.lastName);
|
||||
await peopleCloudComponent.checkUserIsDisplayed(`${testUser.firstName} ${testUser.lastName}`);
|
||||
});
|
||||
|
||||
it('One role filtering', async () => {
|
||||
await peopleGroupCloudComponentPage.enterPeopleRoles(`["${identityService.ROLES.ACTIVITI_USER}"]`);
|
||||
await peopleCloudComponent.searchAssignee(apsUser.lastName);
|
||||
await peopleCloudComponent.checkUserIsDisplayed(`${apsUser.firstName} ${apsUser.lastName}`);
|
||||
await peopleCloudComponent.searchAssignee(devopsUser.lastName);
|
||||
await peopleCloudComponent.checkNoResultsFoundError();
|
||||
await peopleCloudComponent.searchAssignee(noRoleUser.lastName);
|
||||
await peopleCloudComponent.checkNoResultsFoundError();
|
||||
});
|
||||
|
||||
it('[C309677] Should populate the Users without any validation when the Preselect flag is set to false', async () => {
|
||||
await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection();
|
||||
await peopleGroupCloudComponentPage.checkPeopleCloudMultipleSelectionIsSelected();
|
||||
await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('false');
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(
|
||||
`[{"id":"TestId1","firstName":"TestFirstName1","lastName":"TestLastName1"},` +
|
||||
`{"id":"TestId2","firstName":"TestFirstName2","lastName":"TestLastName2"},` +
|
||||
`{"id":"TestId3","firstName":"TestFirstName3","lastName":"TestLastName3"}]`);
|
||||
await peopleCloudComponent.checkSelectedPeople('TestFirstName1 TestLastName1');
|
||||
await peopleCloudComponent.checkSelectedPeople('TestFirstName2 TestLastName2');
|
||||
await peopleCloudComponent.checkSelectedPeople('TestFirstName3 TestLastName3');
|
||||
it('Multiple roles filtering', async () => {
|
||||
await peopleGroupCloudComponentPage.enterPeopleRoles(`["${identityService.ROLES.ACTIVITI_USER}", "${identityService.ROLES.ACTIVITI_ADMIN}"]`);
|
||||
await peopleCloudComponent.searchAssignee(multipleRolesUser.lastName);
|
||||
await peopleCloudComponent.checkUserIsDisplayed(`${multipleRolesUser.firstName} ${multipleRolesUser.lastName}`);
|
||||
await peopleCloudComponent.searchAssignee(apsUser.lastName);
|
||||
await peopleCloudComponent.checkUserIsNotDisplayed(`${apsUser.firstName} ${apsUser.lastName}`);
|
||||
await peopleCloudComponent.searchAssignee(testUser.lastName);
|
||||
await peopleCloudComponent.checkUserIsNotDisplayed(`${testUser.firstName} ${testUser.lastName}`);
|
||||
await peopleCloudComponent.searchAssignee(noRoleUser.lastName);
|
||||
await peopleCloudComponent.checkNoResultsFoundError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('[C309674] Should be able to add filtering to Group Cloud Component', () => {
|
||||
beforeEach(async () => {
|
||||
await peopleGroupCloudComponentPage.clickGroupCloudMultipleSelection();
|
||||
});
|
||||
|
||||
it('No role filtering', async () => {
|
||||
await peopleGroupCloudComponentPage.clearField(peopleGroupCloudComponentPage.groupRoleInput);
|
||||
await groupCloudComponentPage.searchGroups(groupNoRole.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(groupNoRole.name);
|
||||
await groupCloudComponentPage.searchGroups(groupAdmin.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(groupAdmin.name);
|
||||
await groupCloudComponentPage.searchGroups(groupUser.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(groupUser.name);
|
||||
});
|
||||
|
||||
it('One role filtering', async () => {
|
||||
await peopleGroupCloudComponentPage.enterGroupRoles(`["${identityService.ROLES.ACTIVITI_ADMIN}"]`);
|
||||
await groupCloudComponentPage.searchGroups(groupAdmin.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(groupAdmin.name);
|
||||
await groupCloudComponentPage.searchGroups(groupUser.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupAdmin.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupUser.name);
|
||||
await groupCloudComponentPage.searchGroups(groupNoRole.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupNoRole.name);
|
||||
});
|
||||
|
||||
it('[C309996] Should be able to filter groups based on composite roles ACTIVITI_USER', async () => {
|
||||
await peopleGroupCloudComponentPage.enterGroupRoles(`["${identityService.ROLES.ACTIVITI_USER}"]`);
|
||||
await groupCloudComponentPage.searchGroups(groupAdmin.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupAdmin.name);
|
||||
await groupCloudComponentPage.searchGroups(groupNoRole.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupNoRole.name);
|
||||
await groupCloudComponentPage.searchGroups(groupUser.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(groupUser.name);
|
||||
});
|
||||
|
||||
it('Multiple roles filtering', async () => {
|
||||
await peopleGroupCloudComponentPage.enterGroupRoles(`["${identityService.ROLES.ACTIVITI_ADMIN}", "${identityService.ROLES.ACTIVITI_USER}"]`);
|
||||
await groupCloudComponentPage.searchGroups(groupMultipleRoles.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(groupMultipleRoles.name);
|
||||
await groupCloudComponentPage.searchGroups(groupAdmin.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupAdmin.name);
|
||||
await groupCloudComponentPage.searchGroups(groupUser.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupUser.name);
|
||||
await groupCloudComponentPage.searchGroups(groupNoRole.name);
|
||||
await groupCloudComponentPage.checkGroupIsNotDisplayed(groupNoRole.name);
|
||||
});
|
||||
});
|
||||
|
||||
it('[C305033] Should fetch the preselect users based on the Validate flag set to True in Single mode selection', async () => {
|
||||
await peopleGroupCloudComponentPage.clickPeopleCloudSingleSelection();
|
||||
await peopleGroupCloudComponentPage.checkPeopleCloudSingleSelectionIsSelected();
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect('[{"id":"12345","username":"someUsername","email":"someEmail"}]');
|
||||
await expect(await peopleCloudComponent.checkSelectedPeople('someUsername'));
|
||||
|
||||
await peopleGroupCloudComponentPage.clickPreselectValidation();
|
||||
await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('true');
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"email":"${apsUser.email}"}]`);
|
||||
await expect(await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`));
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"username":"${testUser.username}"}]`);
|
||||
await expect(await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`));
|
||||
});
|
||||
|
||||
it('[C309676] Should fetch the preselect users based on the Validate flag set to True in Multiple mode selection', async () => {
|
||||
await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection();
|
||||
await peopleGroupCloudComponentPage.checkPeopleCloudMultipleSelectionIsSelected();
|
||||
await peopleGroupCloudComponentPage.clickPreselectValidation();
|
||||
await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('true');
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"email":"${apsUser.email}"},{"email":"${testUser.email}"},{"email":"${noRoleUser.email}"}]`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`);
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"username":"${apsUser.username}"},{"username":"${testUser.username}"},` +
|
||||
`{"username":"${noRoleUser.username}"}]`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`);
|
||||
await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`);
|
||||
|
||||
await peopleCloudComponent.searchAssignee(noRoleUser.lastName);
|
||||
await peopleCloudComponent.checkNoResultsFoundError();
|
||||
});
|
||||
|
||||
it('[C309677] Should populate the Users without any validation when the Preselect flag is set to false', async () => {
|
||||
await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection();
|
||||
await peopleGroupCloudComponentPage.checkPeopleCloudMultipleSelectionIsSelected();
|
||||
await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('false');
|
||||
|
||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(
|
||||
`[{"id":"TestId1","firstName":"TestFirstName1","lastName":"TestLastName1"},` +
|
||||
`{"id":"TestId2","firstName":"TestFirstName2","lastName":"TestLastName2"},` +
|
||||
`{"id":"TestId3","firstName":"TestFirstName3","lastName":"TestLastName3"}]`);
|
||||
await peopleCloudComponent.checkSelectedPeople('TestFirstName1 TestLastName1');
|
||||
await peopleCloudComponent.checkSelectedPeople('TestFirstName2 TestLastName2');
|
||||
await peopleCloudComponent.checkSelectedPeople('TestFirstName3 TestLastName3');
|
||||
});
|
||||
});
|
||||
|
@ -32,7 +32,8 @@ describe('People Groups Cloud Component', () => {
|
||||
const identityService = new IdentityService(apiService);
|
||||
const groupIdentityService = new GroupIdentityService(apiService);
|
||||
|
||||
let apsUser, testUser;
|
||||
let apsUser;
|
||||
let testUser;
|
||||
let noRoleUser;
|
||||
let groupNoRole;
|
||||
let users = [];
|
||||
@ -73,7 +74,6 @@ describe('People Groups Cloud Component', () => {
|
||||
|
||||
it('[C305041] Should filter the People Single Selection with the Application name filter', async () => {
|
||||
await peopleGroupCloudComponentPage.checkPeopleCloudSingleSelectionIsSelected();
|
||||
await peopleGroupCloudComponentPage.clickPeopleFilerByApp();
|
||||
await peopleGroupCloudComponentPage.enterPeopleAppName(browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name);
|
||||
|
||||
await peopleCloudComponent.searchAssignee(testUser.firstName);
|
||||
@ -85,7 +85,6 @@ describe('People Groups Cloud Component', () => {
|
||||
|
||||
it('[C305041] Should filter the People Multiple Selection with the Application name filter', async () => {
|
||||
await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection();
|
||||
await peopleGroupCloudComponentPage.clickPeopleFilerByApp();
|
||||
await peopleGroupCloudComponentPage.enterPeopleAppName(browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name);
|
||||
await peopleCloudComponent.searchAssignee(testUser.firstName);
|
||||
await peopleCloudComponent.checkUserIsDisplayed(`${testUser.firstName} ${testUser.lastName}`);
|
||||
@ -102,7 +101,6 @@ describe('People Groups Cloud Component', () => {
|
||||
|
||||
it('[C305041] Should filter the Groups Single Selection with the Application name filter', async () => {
|
||||
await peopleGroupCloudComponentPage.clickGroupCloudSingleSelection();
|
||||
await peopleGroupCloudComponentPage.clickGroupFilerByApp();
|
||||
await peopleGroupCloudComponentPage.enterGroupAppName(browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name);
|
||||
await groupCloudComponentPage.searchGroups(hrGroup.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(hrGroup.name);
|
||||
@ -112,7 +110,6 @@ describe('People Groups Cloud Component', () => {
|
||||
|
||||
it('[C305041] Should filter the Groups Multiple Selection with the Application name filter', async () => {
|
||||
await peopleGroupCloudComponentPage.clickGroupCloudMultipleSelection();
|
||||
await peopleGroupCloudComponentPage.clickGroupFilerByApp();
|
||||
await peopleGroupCloudComponentPage.enterGroupAppName(browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name);
|
||||
await groupCloudComponentPage.searchGroups(testGroup.name);
|
||||
await groupCloudComponentPage.checkGroupIsDisplayed(testGroup.name);
|
||||
|
@ -16,6 +16,7 @@ const PROVIDER = process.env.PROVIDER ? process.env.PROVIDER : 'ALL';
|
||||
const AUTH_TYPE = process.env.AUTH_TYPE ? process.env.AUTH_TYPE : 'BASIC';
|
||||
|
||||
const HOST_SSO = process.env.HOST_SSO || process.env.PROXY_HOST_ADF || HOST || 'oauth';
|
||||
const IDENTITY_HOST = process.env.IDENTITY_HOST || process.env.HOST_SSO + '/auth/admin/realms/alfresco';
|
||||
const OAUTH_CLIENT_ID = process.env.OAUTH_CLIENDID || 'alfresco';
|
||||
|
||||
const IDENTITY_ADMIN_EMAIL = process.env.IDENTITY_ADMIN_EMAIL || "defaultadmin";
|
||||
@ -40,7 +41,7 @@ const appConfig = {
|
||||
"log": E2E_LOG_LEVEL,
|
||||
"ecmHost": HOST_ECM,
|
||||
"bpmHost": HOST_BPM,
|
||||
"identityHost": `${HOST_SSO}/auth/admin/realms/alfresco`,
|
||||
"identityHost": `${IDENTITY_HOST}`,
|
||||
"provider": PROVIDER,
|
||||
"authType": AUTH_TYPE,
|
||||
"oauth2": {
|
||||
|
@ -83,7 +83,7 @@ describe('WebscriptComponent', () => {
|
||||
|
||||
component.ngOnChanges().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(jasmine.Ajax.requests.mostRecent().url).toBe('http://localhost:9876/ecm/alfresco/service/sample/folder/Company%20Home');
|
||||
expect(jasmine.Ajax.requests.mostRecent().url).toContain('/ecm/alfresco/service/sample/folder/Company%20Home');
|
||||
done();
|
||||
});
|
||||
|
||||
|
@ -22,15 +22,27 @@ export class FullNamePipe implements PipeTransform {
|
||||
transform(user: any): string {
|
||||
let fullName = '';
|
||||
if (user) {
|
||||
if (user.firstName && user.firstName !== 'null') {
|
||||
fullName += user.firstName;
|
||||
}
|
||||
if (user.lastName && user.lastName !== 'null') {
|
||||
fullName += fullName.length > 0 ? ' ' : '';
|
||||
fullName += user.lastName;
|
||||
}
|
||||
if (!fullName) {
|
||||
fullName += user.username ? user.username : user.email ? user.email : '';
|
||||
if (user.displayName) {
|
||||
const firstAndLastName = user.displayName.split('\ (.*)');
|
||||
if (firstAndLastName[0]) {
|
||||
fullName += firstAndLastName[0];
|
||||
}
|
||||
if (firstAndLastName[1]) {
|
||||
fullName += fullName.length > 0 ? ' ' : '';
|
||||
fullName += firstAndLastName[1];
|
||||
}
|
||||
|
||||
} else {
|
||||
if (user.firstName && user.firstName !== 'null') {
|
||||
fullName += user.firstName;
|
||||
}
|
||||
if (user.lastName && user.lastName !== 'null') {
|
||||
fullName += fullName.length > 0 ? ' ' : '';
|
||||
fullName += user.lastName;
|
||||
}
|
||||
if (!fullName) {
|
||||
fullName += user.username ? user.username : user.email ? user.email : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
return fullName;
|
||||
|
@ -131,12 +131,12 @@ describe('UserAccessService', () => {
|
||||
expect(getAccessFromApiSpy.calls.count()).toEqual(1);
|
||||
});
|
||||
|
||||
it('should the url be composed from identity host of app.config', async () => {
|
||||
it('should the url be composed from bpm host of app.config', async () => {
|
||||
const fakeIdentityHost = 'https://fake-identity-host.fake.com';
|
||||
appConfigService.config.identityHost = fakeIdentityHost;
|
||||
appConfigService.config.bpmHost = fakeIdentityHost;
|
||||
await userAccessService.fetchUserAccess();
|
||||
|
||||
expect(getAccessFromApiSpy).toHaveBeenCalledWith({ url: `${ fakeIdentityHost }/v1/identity/roles` });
|
||||
expect(getAccessFromApiSpy).toHaveBeenCalledWith({ url: `${fakeIdentityHost}/modeling-service/v1/identity/roles` });
|
||||
});
|
||||
|
||||
it('should not fetch the access from the API if is not configured with OAUTH', async () => {
|
||||
|
@ -22,6 +22,8 @@ import { UserAccessModel } from '../models/user-access.model';
|
||||
import { AppConfigService } from '../app-config/app-config.service';
|
||||
import { OAuth2Service } from './oauth2.service';
|
||||
|
||||
const IDENTITY_MICRO_SERVICE_INGRESS = 'modeling-service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
@ -50,7 +52,7 @@ export class UserAccessService {
|
||||
}
|
||||
|
||||
private async fetchAccessFromApi() {
|
||||
const url = `${ this.identityHost }/v1/identity/roles`;
|
||||
const url = `${this.identityHost}/${IDENTITY_MICRO_SERVICE_INGRESS}/v1/identity/roles`;
|
||||
await this.oAuth2Service.get({ url })
|
||||
.toPromise()
|
||||
.then((response: UserAccessModel) => {
|
||||
@ -68,7 +70,7 @@ export class UserAccessService {
|
||||
}
|
||||
|
||||
private get identityHost(): string {
|
||||
return `${this.appConfigService.get('identityHost')}`;
|
||||
return `${this.appConfigService.get('bpmHost')}`;
|
||||
}
|
||||
|
||||
private isOauth(): boolean {
|
||||
|
@ -65,13 +65,13 @@ describe('GroupCloudWidgetComponent', () => {
|
||||
expect(asterisk.textContent).toEqual('*');
|
||||
});
|
||||
|
||||
it('should be invalid if no option is selected after interaction', async () => {
|
||||
it('should be invalid after user interaction without typing', async () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(element.querySelector('.adf-invalid')).toBeFalsy();
|
||||
|
||||
const cloudGroupInput = element.querySelector('adf-cloud-group');
|
||||
const cloudGroupInput = element.querySelector('[data-automation-id="adf-cloud-group-search-input"]');
|
||||
cloudGroupInput.dispatchEvent(new Event('blur'));
|
||||
|
||||
fixture.detectChanges();
|
||||
|
@ -16,11 +16,12 @@
|
||||
*/
|
||||
|
||||
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { WidgetComponent, IdentityGroupModel, FormService } from '@alfresco/adf-core';
|
||||
import { WidgetComponent, FormService } from '@alfresco/adf-core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { filter, takeUntil } from 'rxjs/operators';
|
||||
import { Subject } from 'rxjs';
|
||||
import { ComponentSelectionMode } from '../../../../types';
|
||||
import { IdentityGroupModel } from '../../../../group/models/identity-group.model';
|
||||
|
||||
/* eslint-disable @angular-eslint/component-selector */
|
||||
|
||||
|
@ -15,20 +15,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { FormFieldModel, FormFieldTypes, FormModel, IdentityUserModel, IdentityUserService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { FormFieldModel, FormFieldTypes, FormModel, IdentityUserModel, setupTestBed } from '@alfresco/adf-core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { PeopleCloudWidgetComponent } from './people-cloud.widget';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
|
||||
import { IdentityUserService } from '../../../../people/services/identity-user.service';
|
||||
import { mockShepherdsPie, mockYorkshirePudding } from '../../../../people/mock/people-cloud.mock';
|
||||
|
||||
describe('PeopleCloudWidgetComponent', () => {
|
||||
let fixture: ComponentFixture<PeopleCloudWidgetComponent>;
|
||||
let widget: PeopleCloudWidgetComponent;
|
||||
let element: HTMLElement;
|
||||
let identityUserService: IdentityUserService;
|
||||
const currentUser = { id: 'id', username: 'user' };
|
||||
const fakeUser = { id: 'fake-id', username: 'fake' };
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
@ -48,20 +48,20 @@ describe('PeopleCloudWidgetComponent', () => {
|
||||
fixture = TestBed.createComponent(PeopleCloudWidgetComponent);
|
||||
widget = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(fakeUser);
|
||||
spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(mockShepherdsPie);
|
||||
});
|
||||
|
||||
it('should preselect the current user', () => {
|
||||
widget.field = new FormFieldModel(new FormModel(), { value: null, selectLoggedUser: true });
|
||||
fixture.detectChanges();
|
||||
expect(widget.preSelectUsers).toEqual([fakeUser]);
|
||||
expect(widget.preSelectUsers).toEqual([mockShepherdsPie]);
|
||||
expect(identityUserService.getCurrentUserInfo).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not preselect the current user if value exist', () => {
|
||||
widget.field = new FormFieldModel(new FormModel(), { value: [currentUser], selectLoggedUser: true });
|
||||
widget.field = new FormFieldModel(new FormModel(), { value: [mockYorkshirePudding], selectLoggedUser: true });
|
||||
fixture.detectChanges();
|
||||
expect(widget.preSelectUsers).toEqual([currentUser]);
|
||||
expect(widget.preSelectUsers).toEqual([mockYorkshirePudding]);
|
||||
expect(identityUserService.getCurrentUserInfo).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -84,13 +84,13 @@ describe('PeopleCloudWidgetComponent', () => {
|
||||
expect(asterisk.textContent).toEqual('*');
|
||||
});
|
||||
|
||||
it('should be invalid if no option is selected after interaction', async () => {
|
||||
it('should be invalid after user interaction without typing', async () => {
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(element.querySelector('.adf-invalid')).toBeFalsy();
|
||||
|
||||
const cloudPeopleInput = element.querySelector('adf-cloud-people');
|
||||
const cloudPeopleInput = element.querySelector('[data-automation-id="adf-people-cloud-search-input"]');
|
||||
cloudPeopleInput.dispatchEvent(new Event('blur'));
|
||||
|
||||
fixture.detectChanges();
|
||||
|
@ -16,11 +16,13 @@
|
||||
*/
|
||||
|
||||
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
||||
import { WidgetComponent, IdentityUserModel, FormService, IdentityUserService } from '@alfresco/adf-core';
|
||||
import { WidgetComponent, FormService } from '@alfresco/adf-core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { filter, takeUntil } from 'rxjs/operators';
|
||||
import { Subject } from 'rxjs';
|
||||
import { ComponentSelectionMode } from '../../../../types';
|
||||
import { IdentityUserModel } from '../../../../people/models/identity-user.model';
|
||||
import { IdentityUserService } from '../../../../people/services/identity-user.service';
|
||||
|
||||
/* eslint-disable @angular-eslint/component-selector */
|
||||
|
||||
|
@ -24,35 +24,54 @@ import { GroupCloudModule } from '../group-cloud.module';
|
||||
import { GroupCloudComponent } from './group-cloud.component';
|
||||
import {
|
||||
setupTestBed,
|
||||
IdentityGroupService,
|
||||
mockIdentityGroups,
|
||||
AlfrescoApiService,
|
||||
CoreTestingModule
|
||||
} from '@alfresco/adf-core';
|
||||
import { SimpleChange } from '@angular/core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { IdentityGroupService } from '../services/identity-group.service';
|
||||
import { mockFoodGroups, mockMeatChicken, mockVegetableAubergine } from '../mock/group-cloud.mock';
|
||||
|
||||
describe('GroupCloudComponent', () => {
|
||||
let component: GroupCloudComponent;
|
||||
let fixture: ComponentFixture<GroupCloudComponent>;
|
||||
let element: HTMLElement;
|
||||
let identityGroupService: IdentityGroupService;
|
||||
let alfrescoApiService: AlfrescoApiService;
|
||||
let findGroupsByNameSpy: jasmine.Spy;
|
||||
|
||||
const mock: any = {
|
||||
oauth2Auth: {
|
||||
callCustomApi: () => Promise.resolve(mockIdentityGroups)
|
||||
},
|
||||
isEcmLoggedIn: () => false,
|
||||
reply: jasmine.createSpy('reply')
|
||||
};
|
||||
|
||||
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
||||
function getElement<T = HTMLElement>(selector: string): T {
|
||||
return fixture.nativeElement.querySelector(selector);
|
||||
}
|
||||
|
||||
async function searchGroup(value: string) {
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = value;
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
async function searchGroupsAndBlur(value: string) {
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = value;
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
await fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
input.blur();
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
function getGroupListUI() {
|
||||
return fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]'));
|
||||
}
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
@ -68,16 +87,12 @@ describe('GroupCloudComponent', () => {
|
||||
element = fixture.nativeElement;
|
||||
|
||||
identityGroupService = TestBed.inject(IdentityGroupService);
|
||||
alfrescoApiService = TestBed.inject(AlfrescoApiService);
|
||||
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(mock);
|
||||
});
|
||||
|
||||
it('should populate placeholder when title is present', async () => {
|
||||
it('should populate placeholder when title is present', () => {
|
||||
component.title = 'TITLE_KEY';
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const matLabel = element.querySelector<HTMLInputElement>('#adf-group-cloud-title-id');
|
||||
expect(matLabel.textContent).toEqual('TITLE_KEY');
|
||||
@ -87,330 +102,88 @@ describe('GroupCloudComponent', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
fixture.detectChanges();
|
||||
findGroupsByNameSpy = spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of(mockIdentityGroups));
|
||||
findGroupsByNameSpy = spyOn(identityGroupService, 'search').and.returnValue(of(mockFoodGroups));
|
||||
});
|
||||
|
||||
it('should list the groups as dropdown options if the search term has results', (done) => {
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'Mock';
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
it('should list the groups as dropdown options if the search term has results', async () => {
|
||||
await searchGroup('All');
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(5);
|
||||
expect(findGroupsByNameSpy).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
const groupList = getGroupListUI();
|
||||
expect(groupList.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should not be able to search for a group that its name matches one of the preselected groups name', (done) => {
|
||||
component.preSelectGroups = [{ name: mockIdentityGroups[0].name }];
|
||||
const changes = new SimpleChange(null, [{ name: mockIdentityGroups[0].name }], false);
|
||||
it('should not be able to search for a group that its name matches one of the preselected groups name', async () => {
|
||||
component.preSelectGroups = [{ name: mockVegetableAubergine.name }];
|
||||
const changes = new SimpleChange(null, [{ name: mockVegetableAubergine.name }], false);
|
||||
component.ngOnChanges({ preSelectGroups: changes });
|
||||
fixture.detectChanges();
|
||||
|
||||
const inputHTMLElement = element.querySelector<HTMLInputElement>('input');
|
||||
inputHTMLElement.focus();
|
||||
inputHTMLElement.value = 'mock-group';
|
||||
inputHTMLElement.dispatchEvent(new Event('keyup'));
|
||||
inputHTMLElement.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
await searchGroup('Aubergine');
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(4);
|
||||
done();
|
||||
});
|
||||
const groupList = getGroupListUI();
|
||||
expect(groupList.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should hide result list if input is empty', (done) => {
|
||||
fixture.detectChanges();
|
||||
it('should hide result list if input is empty', async () => {
|
||||
await searchGroup('');
|
||||
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = '';
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(element.querySelector('[data-automation-id="adf-cloud-group-row"]')).toBeNull();
|
||||
done();
|
||||
});
|
||||
expect(element.querySelector('[data-automation-id="adf-cloud-group-row"]')).toBeNull();
|
||||
});
|
||||
|
||||
it('should update selected groups when a group is selected', (done) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
it('should update selected groups when a group is selected', async () => {
|
||||
const selectEmitSpy = spyOn(component.selectGroup, 'emit');
|
||||
const changedGroupsSpy = spyOn(component.changedGroups, 'emit');
|
||||
|
||||
const group = { name: 'groupname' };
|
||||
component.onSelect(group);
|
||||
component.onSelect(mockMeatChicken);
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(selectEmitSpy).toHaveBeenCalledWith(group);
|
||||
expect(changedGroupsSpy).toHaveBeenCalledWith([group]);
|
||||
expect(component.selectedGroups).toEqual([group]);
|
||||
done();
|
||||
});
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(selectEmitSpy).toHaveBeenCalledWith(mockMeatChicken);
|
||||
expect(changedGroupsSpy).toHaveBeenCalledWith([mockMeatChicken]);
|
||||
expect(component.selectedGroups).toEqual([mockMeatChicken]);
|
||||
});
|
||||
|
||||
it('should replace the group in single-selection mode', () => {
|
||||
component.mode = 'single';
|
||||
|
||||
const group1 = { name: 'group1' };
|
||||
const group2 = { name: 'group2' };
|
||||
component.onSelect(mockVegetableAubergine);
|
||||
expect(component.selectedGroups).toEqual([mockVegetableAubergine]);
|
||||
|
||||
component.onSelect(group1);
|
||||
expect(component.selectedGroups).toEqual([group1]);
|
||||
|
||||
component.onSelect(group2);
|
||||
expect(component.selectedGroups).toEqual([group2]);
|
||||
component.onSelect(mockMeatChicken);
|
||||
expect(component.selectedGroups).toEqual([mockMeatChicken]);
|
||||
});
|
||||
|
||||
it('should allow multiple groups in multi-selection mode', () => {
|
||||
component.mode = 'multiple';
|
||||
|
||||
const group1 = { name: 'group1' };
|
||||
const group2 = { name: 'group2' };
|
||||
component.onSelect(mockVegetableAubergine);
|
||||
component.onSelect(mockMeatChicken);
|
||||
|
||||
component.onSelect(group1);
|
||||
component.onSelect(group2);
|
||||
|
||||
expect(component.selectedGroups).toEqual([group1, group2]);
|
||||
expect(component.selectedGroups).toEqual([mockVegetableAubergine, mockMeatChicken]);
|
||||
});
|
||||
|
||||
it('should allow only unique groups in multi-selection mode', () => {
|
||||
component.mode = 'multiple';
|
||||
|
||||
const group1 = { name: 'group1' };
|
||||
const group2 = { name: 'group2' };
|
||||
component.onSelect(mockVegetableAubergine);
|
||||
component.onSelect(mockMeatChicken);
|
||||
component.onSelect(mockMeatChicken);
|
||||
component.onSelect(mockVegetableAubergine);
|
||||
|
||||
component.onSelect(group1);
|
||||
component.onSelect(group2);
|
||||
component.onSelect(group1);
|
||||
component.onSelect(group2);
|
||||
|
||||
expect(component.selectedGroups).toEqual([group1, group2]);
|
||||
expect(component.selectedGroups).toEqual([mockVegetableAubergine, mockMeatChicken]);
|
||||
});
|
||||
|
||||
it('should show an error message if the search result empty', (done) => {
|
||||
it('should show an error message and icon if the search result empty', async () => {
|
||||
findGroupsByNameSpy.and.returnValue(of([]));
|
||||
fixture.detectChanges();
|
||||
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'ZZZ';
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
await searchGroupsAndBlur('INCORRECTVALUE');
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
input.blur();
|
||||
fixture.detectChanges();
|
||||
const errorMessage = element.querySelector('[data-automation-id="invalid-groups-typing-error"]');
|
||||
expect(errorMessage).not.toBeNull();
|
||||
expect(errorMessage.textContent).toContain('ADF_CLOUD_GROUPS.ERROR.NOT_FOUND');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should display proper error icon', (done) => {
|
||||
findGroupsByNameSpy.and.returnValue(of([]));
|
||||
fixture.detectChanges();
|
||||
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'ZZZ';
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
input.blur();
|
||||
fixture.detectChanges();
|
||||
const errorIcon = element.querySelector('.adf-error-icon').textContent;
|
||||
expect(errorIcon).toEqual('error_outline');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when application name defined', () => {
|
||||
let checkGroupHasAnyClientAppRoleSpy: jasmine.Spy;
|
||||
let checkGroupHasClientAppSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
findGroupsByNameSpy = spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of(mockIdentityGroups));
|
||||
checkGroupHasAnyClientAppRoleSpy = spyOn(identityGroupService, 'checkGroupHasAnyClientAppRole').and.returnValue(of(true));
|
||||
checkGroupHasClientAppSpy = spyOn(identityGroupService, 'checkGroupHasClientApp').and.returnValue(of(true));
|
||||
|
||||
component.preSelectGroups = [];
|
||||
component.appName = 'mock-app-name';
|
||||
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should fetch the client ID if appName specified', async () => {
|
||||
const getClientIdByApplicationNameSpy = spyOn(identityGroupService, 'getClientIdByApplicationName').and.callThrough();
|
||||
component.appName = 'mock-app-name';
|
||||
|
||||
const change = new SimpleChange(null, 'mock-app-name', false);
|
||||
component.ngOnChanges({ appName: change });
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(getClientIdByApplicationNameSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should list groups who have access to the app when appName is specified', (done) => {
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'M';
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(5);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not list groups who do not have access to the app when appName is specified', (done) => {
|
||||
checkGroupHasClientAppSpy.and.returnValue(of(false));
|
||||
fixture.detectChanges();
|
||||
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'M';
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(0);
|
||||
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-no-results"]')).length).toEqual(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should list groups if given roles mapped with client roles', (done) => {
|
||||
component.roles = ['MOCK_ROLE_1', 'MOCK_ROLE_1'];
|
||||
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'M';
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(5);
|
||||
expect(checkGroupHasAnyClientAppRoleSpy).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not list groups if roles are not mapping with client roles', (done) => {
|
||||
checkGroupHasAnyClientAppRoleSpy.and.returnValue(of(false));
|
||||
component.roles = ['MOCK_ROLE_1', 'MOCK_ROLE_1'];
|
||||
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'M';
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(0);
|
||||
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-no-results"]')).length).toEqual(1);
|
||||
expect(checkGroupHasAnyClientAppRoleSpy).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not call client role mapping sevice if roles not specified', (done) => {
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'M';
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(checkGroupHasAnyClientAppRoleSpy).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should validate access to the app when appName is specified', (done) => {
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'M';
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(checkGroupHasClientAppSpy).toHaveBeenCalledTimes(5);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not validate access to the app when appName is not specified', (done) => {
|
||||
component.appName = '';
|
||||
fixture.detectChanges();
|
||||
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'M';
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(checkGroupHasClientAppSpy).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should show an error message if the group does not have access', (done) => {
|
||||
checkGroupHasClientAppSpy.and.returnValue(of(false));
|
||||
findGroupsByNameSpy.and.returnValue(of([]));
|
||||
fixture.detectChanges();
|
||||
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'ZZZ';
|
||||
input.dispatchEvent(new Event('keyup'));
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
input.blur();
|
||||
fixture.detectChanges();
|
||||
|
||||
const errorMessage = element.querySelector('[data-automation-id="invalid-groups-typing-error"]');
|
||||
expect(errorMessage).not.toBeNull();
|
||||
expect(errorMessage.textContent).toContain('ADF_CLOUD_GROUPS.ERROR.NOT_FOUND');
|
||||
|
||||
done();
|
||||
});
|
||||
const errorMessage = element.querySelector('[data-automation-id="invalid-groups-typing-error"]');
|
||||
expect(errorMessage).not.toBeNull();
|
||||
expect(errorMessage.textContent).toContain('ADF_CLOUD_GROUPS.ERROR.NOT_FOUND');
|
||||
const errorIcon = element.querySelector('.adf-error-icon').textContent;
|
||||
expect(errorIcon).toEqual('error_outline');
|
||||
});
|
||||
});
|
||||
|
||||
@ -436,76 +209,12 @@ describe('GroupCloudComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('When roles defined', () => {
|
||||
let checkGroupHasRoleSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
component.roles = ['mock-role-1', 'mock-role-2'];
|
||||
spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of(mockIdentityGroups));
|
||||
checkGroupHasRoleSpy = spyOn(identityGroupService, 'checkGroupHasRole').and.returnValue(of(true));
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should filter if groups has any specified role', (done) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'M';
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(5);
|
||||
expect(checkGroupHasRoleSpy).toHaveBeenCalledTimes(5);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not filter groups if group does not have any specified role', (done) => {
|
||||
fixture.detectChanges();
|
||||
checkGroupHasRoleSpy.and.returnValue(of(false));
|
||||
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'M';
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(0);
|
||||
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-no-results"]')).length).toEqual(1);
|
||||
expect(checkGroupHasRoleSpy).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not call checkGroupHasRole service when roles are not specified', (done) => {
|
||||
component.roles = [];
|
||||
fixture.detectChanges();
|
||||
|
||||
const input = getElement<HTMLInputElement>('input');
|
||||
input.focus();
|
||||
input.value = 'M';
|
||||
input.dispatchEvent(new Event('input'));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(checkGroupHasRoleSpy).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Single Mode with pre-selected groups', () => {
|
||||
const changes = new SimpleChange(null, mockIdentityGroups, false);
|
||||
const changes = new SimpleChange(null, mockFoodGroups, false);
|
||||
|
||||
beforeEach(() => {
|
||||
component.mode = 'single';
|
||||
component.preSelectGroups = mockIdentityGroups;
|
||||
component.preSelectGroups = mockFoodGroups;
|
||||
component.ngOnChanges({ preSelectGroups: changes });
|
||||
fixture.detectChanges();
|
||||
});
|
||||
@ -513,16 +222,16 @@ describe('GroupCloudComponent', () => {
|
||||
it('should show only one mat chip with the first preSelectedGroup', () => {
|
||||
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
|
||||
expect(chips.length).toEqual(1);
|
||||
expect(chips[0].attributes['data-automation-id']).toEqual(`adf-cloud-group-chip-${mockIdentityGroups[0].name}`);
|
||||
expect(chips[0].attributes['data-automation-id']).toEqual(`adf-cloud-group-chip-${mockVegetableAubergine.name}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Multiple Mode with pre-selected groups', () => {
|
||||
const change = new SimpleChange(null, mockIdentityGroups, false);
|
||||
const change = new SimpleChange(null, mockFoodGroups, false);
|
||||
|
||||
beforeEach(() => {
|
||||
component.mode = 'multiple';
|
||||
component.preSelectGroups = mockIdentityGroups;
|
||||
component.preSelectGroups = mockFoodGroups;
|
||||
component.ngOnChanges({ preSelectGroups: change });
|
||||
fixture.detectChanges();
|
||||
});
|
||||
@ -533,196 +242,145 @@ describe('GroupCloudComponent', () => {
|
||||
component.ngOnChanges({ preSelectGroups: change });
|
||||
fixture.detectChanges();
|
||||
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
|
||||
expect(chips.length).toBe(5);
|
||||
expect(chips.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should removeGroup and changedGroups emit when a selected group is removed', (done) => {
|
||||
it('should removeGroup and changedGroups emit when a selected group is removed', async () => {
|
||||
const removeGroupEmitterSpy = spyOn(component.removeGroup, 'emit');
|
||||
const changedGroupsEmitterSpy = spyOn(component.changedGroups, 'emit');
|
||||
const groupToRemove = mockIdentityGroups[0];
|
||||
component.mode = 'multiple';
|
||||
fixture.detectChanges();
|
||||
|
||||
const removeIcon = fixture.debugElement.query(By.css('mat-chip mat-icon'));
|
||||
removeIcon.nativeElement.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
expect(removeGroupEmitterSpy).toHaveBeenCalledWith(groupToRemove);
|
||||
expect(changedGroupsEmitterSpy).toHaveBeenCalledWith([mockIdentityGroups[1], mockIdentityGroups[2], mockIdentityGroups[3], mockIdentityGroups[4]]);
|
||||
expect(component.selectedGroups.indexOf({
|
||||
id: groupToRemove.id,
|
||||
name: groupToRemove.name,
|
||||
path: groupToRemove.path
|
||||
})).toEqual(-1);
|
||||
done();
|
||||
});
|
||||
await fixture.whenStable();
|
||||
expect(removeGroupEmitterSpy).toHaveBeenCalledWith(mockVegetableAubergine);
|
||||
expect(changedGroupsEmitterSpy).toHaveBeenCalledWith([mockMeatChicken]);
|
||||
expect(component.selectedGroups.indexOf({
|
||||
id: mockMeatChicken.id,
|
||||
name: mockMeatChicken.name
|
||||
})).toEqual(-1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Multiple Mode with read-only', () => {
|
||||
|
||||
it('Should not show remove icon for pre-selected groups if readonly property set to true', (done) => {
|
||||
fixture.detectChanges();
|
||||
component.preSelectGroups = [
|
||||
{ id: mockIdentityGroups[0].id, name: mockIdentityGroups[0].name, readonly: true },
|
||||
{ id: mockIdentityGroups[1].id, name: mockIdentityGroups[1].name, readonly: true }
|
||||
];
|
||||
const change = new SimpleChange(null, component.preSelectGroups, false);
|
||||
it('Should not show remove icon for pre-selected groups if readonly property set to true', async () => {
|
||||
component.mode = 'multiple';
|
||||
component.ngOnChanges({ preSelectGroups: change });
|
||||
|
||||
component.preSelectGroups = [
|
||||
{ id: mockVegetableAubergine.id, name: mockVegetableAubergine.name, readonly: true },
|
||||
mockMeatChicken
|
||||
];
|
||||
const changes = new SimpleChange(null, [{ name: mockVegetableAubergine.name }], false);
|
||||
component.ngOnChanges({ preSelectGroups: changes });
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const chipList = fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip');
|
||||
const removeIcon = getElement('[data-automation-id="adf-cloud-group-chip-remove-icon-Mock Group 1"]');
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(chipList.length).toBe(2);
|
||||
expect(component.preSelectGroups[0].readonly).toBeTruthy();
|
||||
expect(component.preSelectGroups[1].readonly).toBeTruthy();
|
||||
expect(removeIcon).toBeNull();
|
||||
const chipList = fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip');
|
||||
|
||||
expect(chipList.length).toBe(2);
|
||||
const removeIconAubergine = getElement(`[data-automation-id="adf-cloud-group-chip-remove-icon-${mockVegetableAubergine.name}"]`);
|
||||
expect(removeIconAubergine).toBeNull();
|
||||
const removeIconPepper = getElement(`[data-automation-id="adf-cloud-group-chip-remove-icon-${mockMeatChicken.name}"]`);
|
||||
expect(removeIconPepper).not.toBeNull();
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('Should be able to remove preselected groups if readonly property set to false', (done) => {
|
||||
fixture.detectChanges();
|
||||
|
||||
component.preSelectGroups = [
|
||||
{ id: mockIdentityGroups[0].id, name: mockIdentityGroups[0].name, readonly: false },
|
||||
{ id: mockIdentityGroups[1].id, name: mockIdentityGroups[1].name, readonly: false }
|
||||
];
|
||||
it('Should be able to remove preselected groups if readonly property set to false', async () => {
|
||||
component.mode = 'multiple';
|
||||
component.preSelectGroups = mockFoodGroups;
|
||||
|
||||
const change = new SimpleChange(null, component.preSelectGroups, false);
|
||||
component.mode = 'multiple';
|
||||
component.ngOnChanges({ preSelectGroups: change });
|
||||
|
||||
const removeGroupSpy = spyOn(component.removeGroup, 'emit');
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable();
|
||||
fixture.detectChanges();
|
||||
|
||||
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
|
||||
const removeIcon = getElement('[data-automation-id="adf-cloud-group-chip-remove-icon-Mock Group 1"]');
|
||||
const chipList = fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip');
|
||||
expect(chipList.length).toBe(2);
|
||||
|
||||
expect(chips.length).toBe(2);
|
||||
expect(component.preSelectGroups[0].readonly).toBe(false, 'Removable');
|
||||
expect(component.preSelectGroups[1].readonly).toBe(false, 'Removable');
|
||||
const removeIcon = getElement(`[data-automation-id="adf-cloud-group-chip-remove-icon-${mockMeatChicken.name}"]`);
|
||||
removeIcon.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
removeIcon.click();
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(removeGroupSpy).toHaveBeenCalled();
|
||||
expect(fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip').length).toBe(1);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Component readonly mode', () => {
|
||||
const change = new SimpleChange(null, mockIdentityGroups, false);
|
||||
|
||||
it('should chip list be disabled and show one single chip - single mode', () => {
|
||||
component.mode = 'single';
|
||||
component.readOnly = true;
|
||||
component.preSelectGroups = mockIdentityGroups;
|
||||
component.ngOnChanges({ preSelectGroups: change });
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
|
||||
const chipList = getElement('mat-chip-list');
|
||||
|
||||
expect(chips).toBeDefined();
|
||||
expect(chipList).toBeDefined();
|
||||
expect(chips.length).toBe(1);
|
||||
expect(chipList.attributes['ng-reflect-disabled'].value).toEqual('true');
|
||||
});
|
||||
|
||||
it('should chip list be disabled and show all the chips - multiple mode', () => {
|
||||
component.mode = 'multiple';
|
||||
component.readOnly = true;
|
||||
component.preSelectGroups = mockIdentityGroups;
|
||||
component.ngOnChanges({ preSelectGroups: change });
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
|
||||
const chipList = getElement('mat-chip-list');
|
||||
|
||||
expect(chips).toBeDefined();
|
||||
expect(chipList).toBeDefined();
|
||||
expect(chips.length).toBe(5);
|
||||
expect(chipList.attributes['ng-reflect-disabled'].value).toEqual('true');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Preselected groups and validation enabled', () => {
|
||||
|
||||
it('should check validation only for the first group and emit warning when group is invalid - single mode', (done) => {
|
||||
spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of([]));
|
||||
|
||||
const expectedWarning = {
|
||||
message: 'INVALID_PRESELECTED_GROUPS',
|
||||
groups: [{
|
||||
id: mockIdentityGroups[0].id,
|
||||
name: mockIdentityGroups[0].name,
|
||||
path: mockIdentityGroups[0].path,
|
||||
subGroups: []
|
||||
}]
|
||||
};
|
||||
component.warning.subscribe(warning => {
|
||||
expect(warning).toEqual(expectedWarning);
|
||||
done();
|
||||
});
|
||||
|
||||
component.mode = 'single';
|
||||
component.validate = true;
|
||||
component.preSelectGroups = [mockIdentityGroups[0], mockIdentityGroups[1]];
|
||||
component.ngOnChanges({ preSelectGroups: new SimpleChange(null, [mockIdentityGroups[0], mockIdentityGroups[1]], false) });
|
||||
});
|
||||
|
||||
it('should check validation for all the groups and emit warning - multiple mode', (done) => {
|
||||
spyOn(identityGroupService, 'findGroupsByName').and.returnValue(of(undefined));
|
||||
|
||||
const expectedWarning = {
|
||||
message: 'INVALID_PRESELECTED_GROUPS',
|
||||
groups: [
|
||||
{
|
||||
id: mockIdentityGroups[0].id,
|
||||
name: mockIdentityGroups[0].name,
|
||||
path: mockIdentityGroups[0].path,
|
||||
subGroups: []
|
||||
},
|
||||
{
|
||||
id: mockIdentityGroups[1].id,
|
||||
name: mockIdentityGroups[1].name,
|
||||
path: mockIdentityGroups[1].path,
|
||||
subGroups: []
|
||||
}]
|
||||
};
|
||||
|
||||
component.warning.subscribe(warning => {
|
||||
expect(warning).toEqual(expectedWarning);
|
||||
done();
|
||||
});
|
||||
|
||||
component.mode = 'multiple';
|
||||
component.validate = true;
|
||||
component.preSelectGroups = [mockIdentityGroups[0], mockIdentityGroups[1]];
|
||||
component.ngOnChanges({
|
||||
preSelectGroups: new SimpleChange(null, [mockIdentityGroups[0], mockIdentityGroups[1]], false)
|
||||
});
|
||||
});
|
||||
expect(removeGroupSpy).toHaveBeenCalled();
|
||||
expect(fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should removeDuplicatedGroups return only unique groups', () => {
|
||||
const duplicatedGroups = [{ name: mockIdentityGroups[0].name }, { name: mockIdentityGroups[0].name }];
|
||||
expect(component.removeDuplicatedGroups(duplicatedGroups)).toEqual([{ name: mockIdentityGroups[0].name }]);
|
||||
const duplicatedGroups = [ mockMeatChicken, mockMeatChicken];
|
||||
expect(component.removeDuplicatedGroups(duplicatedGroups)).toEqual([mockMeatChicken]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Preselected groups and validation enabled', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
spyOn(identityGroupService, 'search').and.throwError('Invalid group');
|
||||
component.validate = true;
|
||||
component.preSelectGroups = mockFoodGroups;
|
||||
});
|
||||
|
||||
it('should check validation only for the first group and emit warning when group is invalid - single mode', async () => {
|
||||
component.mode = 'single';
|
||||
|
||||
component.ngOnChanges({ preSelectGroups: new SimpleChange(null, [mockVegetableAubergine, mockMeatChicken], false) });
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(component.invalidGroups.length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should check validation for all the groups and emit warning - multiple mode', async () => {
|
||||
component.mode = 'multiple';
|
||||
|
||||
component.ngOnChanges({ preSelectGroups: new SimpleChange(null, [mockVegetableAubergine, mockMeatChicken], false) });
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(component.invalidGroups.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Component readonly mode', () => {
|
||||
const change = new SimpleChange(null, mockFoodGroups, false);
|
||||
|
||||
it('should chip list be disabled and show one single chip - single mode', () => {
|
||||
component.mode = 'single';
|
||||
component.readOnly = true;
|
||||
component.preSelectGroups = mockFoodGroups;
|
||||
component.ngOnChanges({ preSelectGroups: change });
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
|
||||
const chipList = getElement('mat-chip-list');
|
||||
|
||||
expect(chips).toBeDefined();
|
||||
expect(chipList).toBeDefined();
|
||||
expect(chips.length).toBe(1);
|
||||
expect(chipList.attributes['ng-reflect-disabled']?.value).toEqual('true');
|
||||
});
|
||||
|
||||
it('should chip list be disabled and show all the chips - multiple mode', () => {
|
||||
component.mode = 'multiple';
|
||||
component.readOnly = true;
|
||||
component.preSelectGroups = mockFoodGroups;
|
||||
component.ngOnChanges({ preSelectGroups: change });
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
|
||||
const chipList = getElement('mat-chip-list');
|
||||
|
||||
expect(chips).toBeDefined();
|
||||
expect(chipList).toBeDefined();
|
||||
expect(chips.length).toBe(2);
|
||||
expect(chipList.attributes['ng-reflect-disabled']?.value).toEqual('true');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -16,10 +16,16 @@
|
||||
*/
|
||||
|
||||
import { Meta, moduleMetadata, Story } from '@storybook/angular';
|
||||
import { IdentityGroupService, mockIdentityGroups, IdentityGroupServiceMock } from '@alfresco/adf-core';
|
||||
import { GroupCloudModule } from '../group-cloud.module';
|
||||
import { GroupCloudComponent } from './group-cloud.component';
|
||||
import { ProcessServicesCloudStoryModule } from '../../testing/process-services-cloud-story.module';
|
||||
import { IdentityGroupService } from '../services/identity-group.service';
|
||||
import {
|
||||
IdentityGroupServiceMock,
|
||||
mockFoodGroups,
|
||||
mockMeatChicken,
|
||||
mockVegetableAubergine
|
||||
} from '../mock/group-cloud.mock';
|
||||
|
||||
export default {
|
||||
component: GroupCloudComponent,
|
||||
@ -37,15 +43,6 @@ export default {
|
||||
mode: {
|
||||
options: ['single', 'multiple'],
|
||||
control: 'radio'
|
||||
},
|
||||
roles: {
|
||||
options: ['empty', 'user', 'admin'],
|
||||
control: 'radio',
|
||||
mapping: {
|
||||
empty: [],
|
||||
user: ['MOCK-USER-ROLE'],
|
||||
admin: ['MOCK-ADMIN-ROLE']
|
||||
}
|
||||
}
|
||||
}
|
||||
} as Meta;
|
||||
@ -60,7 +57,6 @@ primary.args = {
|
||||
mode: 'single',
|
||||
preSelectGroups: [],
|
||||
readOnly: false,
|
||||
roles: [],
|
||||
title: 'Groups',
|
||||
validate: false
|
||||
};
|
||||
@ -70,7 +66,7 @@ validPreselectedGroups.args = {
|
||||
...primary.args,
|
||||
validate: true,
|
||||
mode: 'multiple',
|
||||
preSelectGroups: mockIdentityGroups
|
||||
preSelectGroups: mockFoodGroups
|
||||
};
|
||||
|
||||
export const mandatoryPreselectedGroups = template.bind({});
|
||||
@ -78,9 +74,7 @@ mandatoryPreselectedGroups.args = {
|
||||
...primary.args,
|
||||
validate: true,
|
||||
mode: 'multiple',
|
||||
preSelectGroups: [{ id: 'mock-group-id-1', name: 'Mock Group 1', path: '/mock', subGroups: [], readonly: true },
|
||||
{ id: 'mock-group-id-2', name: 'Mock Group 2', path: '', subGroups: [] },
|
||||
{ id: 'mock-group-id-3', name: 'Mock Group 3', path: '', subGroups: [], readonly: true }]
|
||||
preSelectGroups: [mockVegetableAubergine, { ...mockMeatChicken, readonly: true }]
|
||||
};
|
||||
|
||||
export const invalidPreselectedGroups = template.bind({});
|
||||
@ -88,13 +82,7 @@ invalidPreselectedGroups.args = {
|
||||
...primary.args,
|
||||
validate: true,
|
||||
mode: 'multiple',
|
||||
preSelectGroups: [{ id: 'invalid-group', name: 'invalid groups' }]
|
||||
};
|
||||
|
||||
export const adminRoleGroups = template.bind({});
|
||||
adminRoleGroups.args = {
|
||||
...primary.args,
|
||||
roles: 'admin'
|
||||
preSelectGroups: [{ id: 'invalid-group', name: 'Invalid Group' }]
|
||||
};
|
||||
|
||||
export const invalidOrEmptyAppName = template.bind({});
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ import { CoreModule } from '@alfresco/adf-core';
|
||||
import { MaterialModule } from '../material.module';
|
||||
import { GroupCloudComponent } from './components/group-cloud.component';
|
||||
import { InitialGroupNamePipe } from './pipe/group-initial.pipe';
|
||||
import { IDENTITY_GROUP_SERVICE_TOKEN } from './services/identity-group-service.token';
|
||||
import { IdentityGroupService } from './services/identity-group.service';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -35,6 +37,9 @@ import { InitialGroupNamePipe } from './pipe/group-initial.pipe';
|
||||
CoreModule
|
||||
],
|
||||
declarations: [GroupCloudComponent, InitialGroupNamePipe],
|
||||
providers: [
|
||||
{ provide: IDENTITY_GROUP_SERVICE_TOKEN, useExisting: IdentityGroupService }
|
||||
],
|
||||
exports: [GroupCloudComponent, InitialGroupNamePipe]
|
||||
})
|
||||
export class GroupCloudModule { }
|
||||
|
@ -0,0 +1,47 @@
|
||||
/*!
|
||||
* @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 { Observable, EMPTY, of } from 'rxjs';
|
||||
import { IdentityGroupModel } from '../models/identity-group.model';
|
||||
import { IdentityGroupFilterInterface } from '../services/identity-group-filter.interface';
|
||||
import { IdentityGroupServiceInterface } from '../services/identity-group.service.interface';
|
||||
|
||||
export const mockVegetableAubergine: IdentityGroupModel = { id: 'aubergine', name: 'Vegetable Aubergine'};
|
||||
export const mockMeatChicken: IdentityGroupModel = { id: 'chicken', name: 'Meat Chicken'};
|
||||
|
||||
export const mockFoodGroups = [ mockVegetableAubergine, mockMeatChicken ];
|
||||
|
||||
export const mockSearchGroupEmptyFilters: IdentityGroupFilterInterface = {
|
||||
roles: [],
|
||||
withinApplication: ''
|
||||
};
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class IdentityGroupServiceMock implements IdentityGroupServiceInterface {
|
||||
search(name: string, _filters?: IdentityGroupFilterInterface): Observable<IdentityGroupModel[]> {
|
||||
if (name.trim() === '') {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
return of(mockFoodGroups.filter(group =>
|
||||
group.name.toUpperCase().includes(name.toUpperCase())
|
||||
));
|
||||
}
|
||||
}
|
@ -0,0 +1,57 @@
|
||||
/*!
|
||||
* @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 { HttpErrorResponse } from '@angular/common/http';
|
||||
import { throwError } from 'rxjs';
|
||||
import { IdentityGroupModel } from '../models/identity-group.model';
|
||||
import { IdentityGroupFilterInterface } from '../services/identity-group-filter.interface';
|
||||
|
||||
export const mockSearchGroupByRoles: IdentityGroupFilterInterface = {
|
||||
roles: ['fake-role-1', 'fake-role-2'],
|
||||
withinApplication: ''
|
||||
};
|
||||
|
||||
export const mockSearchGroupByRolesAndApp: IdentityGroupFilterInterface = {
|
||||
roles: ['fake-role-1', 'fake-role-2'],
|
||||
withinApplication: 'fake-app-name'
|
||||
};
|
||||
|
||||
export const mockSearchGroupByApp: IdentityGroupFilterInterface = {
|
||||
roles: [],
|
||||
withinApplication: 'fake-app-name'
|
||||
};
|
||||
|
||||
export function oAuthMockApiWithIdentityGroups(groups: IdentityGroupModel[]) {
|
||||
return {
|
||||
oauth2Auth: {
|
||||
callCustomApi: () => Promise.resolve(groups)
|
||||
},
|
||||
reply: jasmine.createSpy('reply')
|
||||
};
|
||||
}
|
||||
|
||||
const errorResponse = new HttpErrorResponse({
|
||||
error: 'Mock Error',
|
||||
status: 404, statusText: 'Not Found'
|
||||
});
|
||||
|
||||
export const oAuthMockApiWithError = {
|
||||
oauth2Auth: {
|
||||
callCustomApi: () => throwError(errorResponse)
|
||||
},
|
||||
reply: jasmine.createSpy('reply')
|
||||
};
|
@ -0,0 +1,22 @@
|
||||
/*!
|
||||
* @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 interface IdentityGroupModel {
|
||||
id?: string;
|
||||
name: string;
|
||||
readonly?: boolean;
|
||||
}
|
@ -15,8 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { IdentityGroupModel } from '../models/identity-group.model';
|
||||
import { InitialGroupNamePipe } from './group-initial.pipe';
|
||||
import { IdentityGroupModel } from '@alfresco/adf-core';
|
||||
|
||||
describe('InitialGroupNamePipe', () => {
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { IdentityGroupModel } from '@alfresco/adf-core';
|
||||
import { IdentityGroupModel } from '../models/identity-group.model';
|
||||
|
||||
@Pipe({
|
||||
name: 'groupNameInitial'
|
||||
|
@ -17,4 +17,7 @@
|
||||
|
||||
export * from './components/group-cloud.component';
|
||||
export * from './pipe/group-initial.pipe';
|
||||
export * from './models/identity-group.model';
|
||||
export * from './group-cloud.module';
|
||||
export * from './services/identity-group.service';
|
||||
export * from './services/identity-group-service.token';
|
||||
|
@ -0,0 +1,21 @@
|
||||
/*!
|
||||
* @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 interface IdentityGroupFilterInterface {
|
||||
roles?: string[];
|
||||
withinApplication?: string;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*!
|
||||
* @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 { InjectionToken } from '@angular/core';
|
||||
import { IdentityGroupServiceInterface } from './identity-group.service.interface';
|
||||
|
||||
export const IDENTITY_GROUP_SERVICE_TOKEN = new InjectionToken<IdentityGroupServiceInterface>('IdentityGroup');
|
@ -0,0 +1,24 @@
|
||||
/*!
|
||||
* @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 { Observable } from 'rxjs';
|
||||
import { IdentityGroupModel } from '../models/identity-group.model';
|
||||
import { IdentityGroupFilterInterface } from './identity-group-filter.interface';
|
||||
|
||||
export interface IdentityGroupServiceInterface {
|
||||
search(name: string, filters?: IdentityGroupFilterInterface): Observable<IdentityGroupModel[]>;
|
||||
}
|
@ -0,0 +1,181 @@
|
||||
/*!
|
||||
* @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 { TestBed } from '@angular/core/testing';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { AlfrescoApiService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
|
||||
import { IdentityGroupService } from './identity-group.service';
|
||||
import {
|
||||
mockSearchGroupByApp,
|
||||
mockSearchGroupByRoles,
|
||||
mockSearchGroupByRolesAndApp,
|
||||
oAuthMockApiWithError,
|
||||
oAuthMockApiWithIdentityGroups
|
||||
} from '../mock/identity-group.service.mock';
|
||||
import { mockFoodGroups } from '../mock/group-cloud.mock';
|
||||
|
||||
describe('IdentityGroupService', () => {
|
||||
|
||||
let service: IdentityGroupService;
|
||||
let alfrescoApiService: AlfrescoApiService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
ProcessServiceCloudTestingModule
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
service = TestBed.inject(IdentityGroupService);
|
||||
alfrescoApiService = TestBed.inject(AlfrescoApiService);
|
||||
});
|
||||
|
||||
describe('Search', () => {
|
||||
|
||||
it('should fetch groups', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithIdentityGroups(mockFoodGroups));
|
||||
const searchSpy = spyOn(service, 'search').and.callThrough();
|
||||
|
||||
service.search('fake').subscribe(
|
||||
res => {
|
||||
expect(res).toBeDefined();
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake'
|
||||
});
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should not fetch groups if error occurred', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithError);
|
||||
|
||||
const searchSpy = spyOn(service, 'search').and.callThrough();
|
||||
|
||||
service.search('fake')
|
||||
.subscribe(
|
||||
() => {
|
||||
fail('expected an error, not groups');
|
||||
},
|
||||
(error) => {
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(error.status).toEqual(404);
|
||||
expect(error.statusText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch groups by roles', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithIdentityGroups(mockFoodGroups));
|
||||
const searchSpy = spyOn(service, 'search').and.callThrough();
|
||||
|
||||
service.search('fake', mockSearchGroupByRoles).subscribe(
|
||||
res => {
|
||||
expect(res).toBeDefined();
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake',
|
||||
role: 'fake-role-1,fake-role-2'
|
||||
});
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should not fetch groups by roles if error occurred', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithError);
|
||||
const searchSpy = spyOn(service, 'search').and.callThrough();
|
||||
|
||||
service.search('fake', mockSearchGroupByRoles)
|
||||
.subscribe(
|
||||
() => {
|
||||
fail('expected an error, not groups');
|
||||
},
|
||||
(error) => {
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake',
|
||||
role: 'fake-role-1,fake-role-2'
|
||||
});
|
||||
expect(error.status).toEqual(404);
|
||||
expect(error.statusText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch groups within app', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithIdentityGroups(mockFoodGroups));
|
||||
|
||||
service.search('fake', mockSearchGroupByApp).subscribe(
|
||||
res => {
|
||||
expect(res).toBeDefined();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake',
|
||||
application: 'fake-app-name'
|
||||
});
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch groups within app with roles', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithIdentityGroups(mockFoodGroups));
|
||||
|
||||
service.search('fake', mockSearchGroupByRolesAndApp).subscribe(
|
||||
res => {
|
||||
expect(res).toBeDefined();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake',
|
||||
application: 'fake-app-name',
|
||||
role: 'fake-role-1,fake-role-2'
|
||||
});
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should not fetch groups within app if error occurred', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithError);
|
||||
const searchSpy = spyOn(service, 'search').and.callThrough();
|
||||
|
||||
service.search('fake', mockSearchGroupByApp)
|
||||
.subscribe(
|
||||
() => {
|
||||
fail('expected an error, not groups');
|
||||
},
|
||||
(error) => {
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake',
|
||||
application: 'fake-app-name'
|
||||
});
|
||||
expect(error.status).toEqual(404);
|
||||
expect(error.statusText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,111 @@
|
||||
/*!
|
||||
* @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 { AppConfigService, OAuth2Service } from '@alfresco/adf-core';
|
||||
import { EMPTY, Observable, throwError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { IdentityGroupServiceInterface } from './identity-group.service.interface';
|
||||
import { IdentityGroupFilterInterface } from './identity-group-filter.interface';
|
||||
import { IdentityGroupModel } from '../models/identity-group.model';
|
||||
|
||||
const IDENTITY_MICRO_SERVICE_INGRESS = 'modeling-service';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class IdentityGroupService implements IdentityGroupServiceInterface {
|
||||
|
||||
queryParams: { search: string; application?: string; roles?: string [] };
|
||||
|
||||
constructor(
|
||||
private oAuth2Service: OAuth2Service,
|
||||
private appConfigService: AppConfigService
|
||||
) {}
|
||||
|
||||
public search(name: string, filters?: IdentityGroupFilterInterface): Observable<IdentityGroupModel[]> {
|
||||
if (name.trim() === '') {
|
||||
return EMPTY;
|
||||
} else if (filters?.withinApplication) {
|
||||
return this.searchGroupsWithinApp(name, filters.withinApplication, filters?.roles);
|
||||
} else if (filters?.roles?.length > 0) {
|
||||
return this.searchGroupsWithGlobalRoles(name, filters.roles);
|
||||
} else {
|
||||
return this.searchGroupsByName(name);
|
||||
}
|
||||
}
|
||||
|
||||
private searchGroupsByName(name: string): Observable<IdentityGroupModel[]> {
|
||||
this.buildQueryParam(name);
|
||||
|
||||
return this.invokeIdentityGroupApi().pipe(
|
||||
catchError((err) => this.handleError(err))
|
||||
);
|
||||
}
|
||||
|
||||
private searchGroupsWithGlobalRoles(name: string, roles: string []): Observable<IdentityGroupModel[]> {
|
||||
this.buildQueryParam(name, roles);
|
||||
|
||||
return this.invokeIdentityGroupApi().pipe(
|
||||
catchError((err) => this.handleError(err))
|
||||
);
|
||||
}
|
||||
|
||||
private searchGroupsWithinApp(name: string, applicationName: string, roles?: string []): Observable<IdentityGroupModel[]> {
|
||||
this.buildQueryParam(name, roles, applicationName);
|
||||
|
||||
return this.invokeIdentityGroupApi().pipe(
|
||||
catchError((err) => this.handleError(err))
|
||||
);
|
||||
}
|
||||
|
||||
private invokeIdentityGroupApi(): Observable<IdentityGroupModel[]> {
|
||||
const url = `${this.identityHost}/${IDENTITY_MICRO_SERVICE_INGRESS}/v1/identity/groups`;
|
||||
return this.oAuth2Service.get({ url, queryParams: this.queryParams });
|
||||
}
|
||||
|
||||
private buildQueryParam(name: string, roles?: string [], applicationName?: string) {
|
||||
this.queryParams = { search: name };
|
||||
this.addOptionalValueToQueryParam('application', applicationName);
|
||||
this.addOptionalCommaValueToQueryParam('role', roles);
|
||||
}
|
||||
|
||||
private addOptionalCommaValueToQueryParam(key: string, values: string []) {
|
||||
if (values?.length > 0) {
|
||||
const valuesNotEmpty = this.filterOutEmptyValue(values);
|
||||
if (valuesNotEmpty?.length > 0) {
|
||||
this.queryParams[key] = valuesNotEmpty.join(',');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private addOptionalValueToQueryParam(key: string, value: string) {
|
||||
if (value?.trim()) {
|
||||
this.queryParams[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
private filterOutEmptyValue(roles: string []): string [] {
|
||||
return roles.filter( role => role.trim() ? true : false);
|
||||
}
|
||||
|
||||
private handleError(error: any) {
|
||||
return throwError(error || 'Server error');
|
||||
}
|
||||
|
||||
private get identityHost(): string {
|
||||
return `${this.appConfigService.get('bpmHost')}`;
|
||||
}
|
||||
}
|
@ -16,7 +16,8 @@
|
||||
*/
|
||||
|
||||
import { Pagination } from '@alfresco/js-api';
|
||||
import { IdentityGroupModel, IdentityUserModel } from '@alfresco/adf-core';
|
||||
import { IdentityGroupModel } from '../group/models/identity-group.model';
|
||||
import { IdentityUserModel } from '../people/models/identity-user.model';
|
||||
import { ProcessInstanceVariable } from './process-instance-variable.model';
|
||||
|
||||
export class TaskCloudNodePaging {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -16,10 +16,17 @@
|
||||
*/
|
||||
|
||||
import { Meta, moduleMetadata, Story } from '@storybook/angular';
|
||||
import { IdentityUserService, IdentityUserServiceMock, mockIdentityUsers } from '@alfresco/adf-core';
|
||||
import { PeopleCloudComponent } from './people-cloud.component';
|
||||
import { PeopleCloudModule } from '../people-cloud.module';
|
||||
import { ProcessServicesCloudStoryModule } from '../../testing/process-services-cloud-story.module';
|
||||
import { IdentityUserService } from '../services/identity-user.service';
|
||||
import {
|
||||
IdentityUserServiceMock,
|
||||
mockFoodUsers,
|
||||
mockKielbasaSausage,
|
||||
mockShepherdsPie,
|
||||
mockYorkshirePudding
|
||||
} from '../mock/people-cloud.mock';
|
||||
|
||||
export default {
|
||||
component: PeopleCloudComponent,
|
||||
@ -28,7 +35,7 @@ export default {
|
||||
moduleMetadata({
|
||||
imports: [ProcessServicesCloudStoryModule, PeopleCloudModule],
|
||||
providers: [
|
||||
{ provide: IdentityUserService, useClass: IdentityUserServiceMock }
|
||||
{ provide: IdentityUserService, useClass: IdentityUserServiceMock}
|
||||
]
|
||||
})
|
||||
],
|
||||
@ -37,15 +44,6 @@ export default {
|
||||
mode: {
|
||||
options: ['single', 'multiple'],
|
||||
control: 'radio'
|
||||
},
|
||||
roles: {
|
||||
options: ['empty', 'user', 'admin'],
|
||||
control: 'radio',
|
||||
mapping: {
|
||||
empty: [],
|
||||
user: ['MOCK-USER-ROLE'],
|
||||
admin: ['MOCK-ADMIN-ROLE']
|
||||
}
|
||||
}
|
||||
}
|
||||
} as Meta;
|
||||
@ -61,7 +59,6 @@ primary.args = {
|
||||
mode: 'single',
|
||||
preSelectUsers: [],
|
||||
readOnly: false,
|
||||
roles: [],
|
||||
title: 'Users',
|
||||
validate: false
|
||||
};
|
||||
@ -71,7 +68,7 @@ validPreselectedUsers.args = {
|
||||
...primary.args,
|
||||
validate: true,
|
||||
mode: 'multiple',
|
||||
preSelectUsers: mockIdentityUsers
|
||||
preSelectUsers: mockFoodUsers
|
||||
};
|
||||
|
||||
export const mandatoryPreselectedUsers = template.bind({});
|
||||
@ -79,8 +76,7 @@ mandatoryPreselectedUsers.args = {
|
||||
...primary.args,
|
||||
validate: true,
|
||||
mode: 'multiple',
|
||||
preSelectUsers: [{ id: 'mock-user-id-1', username: 'userName1', firstName: 'first-name-1', lastName: 'last-name-1', email: 'abc@xyz.com', readonly: true },
|
||||
{ id: 'mock-user-id-2', username: 'userName2', firstName: 'first-name-2', lastName: 'last-name-2', email: 'abcd@xyz.com' }]
|
||||
preSelectUsers: [{ ...mockKielbasaSausage, readonly: true }, mockShepherdsPie]
|
||||
};
|
||||
|
||||
export const invalidPreselectedUsers = template.bind({});
|
||||
@ -88,28 +84,22 @@ invalidPreselectedUsers.args = {
|
||||
...primary.args,
|
||||
validate: true,
|
||||
mode: 'multiple',
|
||||
preSelectUsers: [{ id: 'invalid-user', username: 'invalid user', firstName: 'invalid', lastName: 'user', email: 'invalid@xyz.com' }]
|
||||
preSelectUsers: [{ id: 'invalid-user', username: 'Invalid User', firstName: 'Invalid', lastName: 'User', email: 'invalid@xyz.com' }]
|
||||
};
|
||||
|
||||
export const excludedUsers = template.bind({});
|
||||
excludedUsers.args = {
|
||||
...primary.args,
|
||||
excludedUsers: [
|
||||
{ id: 'mock-user-id-2' },
|
||||
{ id: 'mock-user-id-3' }
|
||||
mockKielbasaSausage,
|
||||
mockYorkshirePudding
|
||||
]
|
||||
};
|
||||
|
||||
export const adminRoleUser = template.bind({});
|
||||
adminRoleUser.args = {
|
||||
...primary.args,
|
||||
roles: 'admin'
|
||||
};
|
||||
|
||||
export const noUsers = template.bind({});
|
||||
noUsers.args = {
|
||||
...primary.args,
|
||||
excludedUsers: mockIdentityUsers
|
||||
excludedUsers: mockFoodUsers
|
||||
};
|
||||
|
||||
export const invalidOrEmptyAppName = template.bind({});
|
||||
|
@ -26,18 +26,21 @@ import {
|
||||
SimpleChanges,
|
||||
OnChanges,
|
||||
OnDestroy,
|
||||
ViewChild, ElementRef, SimpleChange
|
||||
ViewChild,
|
||||
ElementRef,
|
||||
Inject
|
||||
} from '@angular/core';
|
||||
import { Observable, of, BehaviorSubject, Subject } from 'rxjs';
|
||||
import { switchMap, debounceTime, distinctUntilChanged, mergeMap, tap, filter, map, takeUntil } from 'rxjs/operators';
|
||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
import { switchMap, debounceTime, distinctUntilChanged, mergeMap, tap, filter, takeUntil } from 'rxjs/operators';
|
||||
import {
|
||||
FullNamePipe,
|
||||
IdentityUserModel,
|
||||
IdentityUserService,
|
||||
LogService
|
||||
} from '@alfresco/adf-core';
|
||||
import { trigger, state, style, transition, animate } from '@angular/animations';
|
||||
import { ComponentSelectionMode } from '../../types';
|
||||
import { IdentityUserModel } from '../models/identity-user.model';
|
||||
import { IdentityUserServiceInterface } from '../services/identity-user.service.interface';
|
||||
import { IDENTITY_USER_SERVICE_TOKEN } from '../services/identity-user-service.token';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-cloud-people',
|
||||
@ -139,15 +142,14 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@ViewChild('userInput')
|
||||
private userInput: ElementRef<HTMLInputElement>;
|
||||
|
||||
private _searchUsers: IdentityUserModel[] = [];
|
||||
private searchUsers: IdentityUserModel[] = [];
|
||||
private onDestroy$ = new Subject<boolean>();
|
||||
|
||||
selectedUsers: IdentityUserModel[] = [];
|
||||
invalidUsers: IdentityUserModel[] = [];
|
||||
|
||||
searchUsers$ = new BehaviorSubject<IdentityUserModel[]>(this._searchUsers);
|
||||
searchUsers$ = new BehaviorSubject<IdentityUserModel[]>(this.searchUsers);
|
||||
subscriptAnimationState: string = 'enter';
|
||||
clientId: string;
|
||||
isFocused: boolean;
|
||||
touched: boolean = false;
|
||||
|
||||
@ -157,20 +159,19 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
validationLoading = false;
|
||||
searchLoading = false;
|
||||
|
||||
typingUniqueValueNotEmpty$: Observable<string>;
|
||||
|
||||
constructor(
|
||||
private identityUserService: IdentityUserService,
|
||||
@Inject(IDENTITY_USER_SERVICE_TOKEN)
|
||||
private identityUserService: IdentityUserServiceInterface,
|
||||
private logService: LogService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadClientId();
|
||||
this.initSearch();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (this.valueChanged(changes.preSelectUsers)
|
||||
|| this.valueChanged(changes.mode)
|
||||
|| this.valueChanged(changes.validate)
|
||||
) {
|
||||
if (this.hasPreselectedUsersChanged(changes) || this.hasModeChanged(changes) || this.isValidationChanged(changes)) {
|
||||
if (this.hasPreSelectUsers()) {
|
||||
this.loadPreSelectUsers();
|
||||
} else if (this.hasPreselectedUsersCleared(changes)) {
|
||||
@ -182,32 +183,51 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.invalidUsers = [];
|
||||
}
|
||||
}
|
||||
|
||||
if (changes.appName && this.isAppNameChanged(changes.appName)) {
|
||||
this.loadClientId();
|
||||
}
|
||||
}
|
||||
|
||||
private async loadClientId(): Promise<void> {
|
||||
this.clientId = await this.identityUserService.getClientIdByApplicationName(this.appName).toPromise();
|
||||
if (this.clientId) {
|
||||
this.searchUserCtrl.enable();
|
||||
}
|
||||
}
|
||||
|
||||
private initSearch(): void {
|
||||
this.searchUserCtrl.valueChanges.pipe(
|
||||
filter((value) => {
|
||||
this.initializeStream();
|
||||
this.typingUniqueValueNotEmpty$.pipe(
|
||||
switchMap((name: string) =>
|
||||
this.identityUserService.search(name, { roles: this.roles, withinApplication: this.appName, groups: this.groupsRestriction })
|
||||
),
|
||||
mergeMap((users: IdentityUserModel[]) => {
|
||||
this.resetSearchUsers();
|
||||
this.searchLoading = false;
|
||||
return users;
|
||||
}),
|
||||
filter(user => !this.isUserAlreadySelected(user) && !this.isExcludedUser(user)),
|
||||
takeUntil(this.onDestroy$)
|
||||
).subscribe((user: IdentityUserModel) => {
|
||||
this.searchUsers.push(user);
|
||||
this.searchUsers$.next(this.searchUsers);
|
||||
});
|
||||
}
|
||||
|
||||
private initializeStream() {
|
||||
const typingValueFromControl$ = this.searchUserCtrl.valueChanges;
|
||||
|
||||
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(),
|
||||
distinctUntilChanged()
|
||||
);
|
||||
|
||||
this.typingUniqueValueNotEmpty$ = typingValueDebouncedUnique$.pipe(
|
||||
tap((value: string) => {
|
||||
if (value.trim()) {
|
||||
this.searchedValue = value;
|
||||
@ -216,87 +236,17 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.searchUserCtrl.markAsUntouched();
|
||||
}
|
||||
}),
|
||||
tap(() => {
|
||||
this.resetSearchUsers();
|
||||
}),
|
||||
switchMap((search) =>
|
||||
this.findUsers(search)
|
||||
),
|
||||
mergeMap((users) => {
|
||||
this.resetSearchUsers();
|
||||
this.searchLoading = false;
|
||||
return users;
|
||||
}),
|
||||
filter(user => !this.isUserAlreadySelected(user) && !this.isExcludedUser(user)),
|
||||
mergeMap(user => this.filterUsersByGroupsRestriction(user)),
|
||||
mergeMap(user => {
|
||||
if (this.appName) {
|
||||
return this.checkUserHasAccess(user.id).pipe(
|
||||
mergeMap(
|
||||
hasRole => hasRole ? of(user) : of()
|
||||
)
|
||||
);
|
||||
} else if (this.hasRoles()) {
|
||||
return this.filterUsersByRoles(user);
|
||||
} else {
|
||||
return of(user);
|
||||
}
|
||||
}),
|
||||
takeUntil(this.onDestroy$)
|
||||
).subscribe(user => {
|
||||
this._searchUsers.push(user);
|
||||
this.searchUsers$.next(this._searchUsers);
|
||||
});
|
||||
tap(() => this.resetSearchUsers())
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy$.next(true);
|
||||
this.onDestroy$.complete();
|
||||
}
|
||||
|
||||
private isAppNameChanged(change: SimpleChange): boolean {
|
||||
return change && change.previousValue !== change.currentValue && this.appName && this.appName.length > 0;
|
||||
}
|
||||
|
||||
isValidationEnabled(): boolean {
|
||||
private isValidationEnabled(): boolean {
|
||||
return this.validate === true;
|
||||
}
|
||||
|
||||
private checkUserHasAccess(userId: string): Observable<boolean> {
|
||||
if (this.hasRoles()) {
|
||||
return this.identityUserService.checkUserHasAnyClientAppRole(userId, this.clientId, this.roles);
|
||||
} else {
|
||||
return this.identityUserService.checkUserHasClientApp(userId, this.clientId);
|
||||
}
|
||||
}
|
||||
|
||||
private hasRoles(): boolean {
|
||||
return this.roles && this.roles.length > 0;
|
||||
}
|
||||
|
||||
filterUsersByRoles(user: IdentityUserModel): Observable<IdentityUserModel> {
|
||||
return this.identityUserService.checkUserHasRole(user.id, this.roles).pipe(
|
||||
map((hasRole: boolean) => ({ hasRole, user })),
|
||||
filter((filteredUser: { hasRole: boolean; user: IdentityUserModel }) => filteredUser.hasRole),
|
||||
map((filteredUser: { hasRole: boolean; user: IdentityUserModel }) => filteredUser.user));
|
||||
}
|
||||
|
||||
private filterUsersByGroupsRestriction(user: IdentityUserModel): Observable<IdentityUserModel> {
|
||||
if (this.groupsRestriction?.length) {
|
||||
return this.isUserPartOfAllRestrictedGroups(user).pipe(
|
||||
mergeMap(isPartOfAllGroups => isPartOfAllGroups ? of(user) : of())
|
||||
);
|
||||
}
|
||||
return of(user);
|
||||
}
|
||||
|
||||
private findUsers(search: string): Observable<IdentityUserModel[]> {
|
||||
return this.identityUserService.findUsersByName(search.trim());
|
||||
}
|
||||
|
||||
private isUserAlreadySelected(searchUser: IdentityUserModel): boolean {
|
||||
if (this.selectedUsers && this.selectedUsers.length > 0) {
|
||||
const result = this.selectedUsers.find((selectedUser) => this.compare(selectedUser, searchUser));
|
||||
const result = this.selectedUsers.find((selectedUser) => this.equalsUsers(selectedUser, searchUser));
|
||||
|
||||
return !!result;
|
||||
}
|
||||
@ -305,7 +255,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
private isExcludedUser(searchUser: IdentityUserModel): boolean {
|
||||
if (this.excludedUsers?.length > 0) {
|
||||
return !!this.excludedUsers.find(excludedUser => this.compare(excludedUser, searchUser));
|
||||
return !!this.excludedUsers.find(excludedUser => this.equalsUsers(excludedUser, searchUser));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -334,28 +284,14 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
async validatePreselectUsers(): Promise<any> {
|
||||
private async validatePreselectUsers(): Promise<any> {
|
||||
this.invalidUsers = [];
|
||||
const validUsers: IdentityUserModel[] = [];
|
||||
|
||||
for (const user of this.getPreselectedUsers()) {
|
||||
try {
|
||||
const validationResult = await this.searchUser(user);
|
||||
const validationResult = (await this.identityUserService.search(user.username, { roles: this.roles, withinApplication: this.appName, groups: this.groupsRestriction }).toPromise())[0];
|
||||
|
||||
if (this.compare(user, validationResult)) {
|
||||
validationResult.readonly = user.readonly;
|
||||
if (this.groupsRestriction?.length) {
|
||||
const isUserPartOfAllRestrictedGroups = await this.isUserPartOfAllRestrictedGroups(validationResult).toPromise();
|
||||
|
||||
if (isUserPartOfAllRestrictedGroups) {
|
||||
validUsers.push(user);
|
||||
} else {
|
||||
this.invalidUsers.push(user);
|
||||
}
|
||||
} else {
|
||||
validUsers.push(validationResult);
|
||||
}
|
||||
} else {
|
||||
if (!this.equalsUsers(user, validationResult)) {
|
||||
this.invalidUsers.push(user);
|
||||
}
|
||||
} catch (error) {
|
||||
@ -365,10 +301,9 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
|
||||
this.checkPreselectValidationErrors();
|
||||
this.selectedUsers = validUsers.concat(this.invalidUsers);
|
||||
}
|
||||
|
||||
compare(preselectedUser: IdentityUserModel, identityUser: IdentityUserModel): boolean {
|
||||
equalsUsers(preselectedUser: IdentityUserModel, identityUser: IdentityUserModel): boolean {
|
||||
if (preselectedUser && identityUser) {
|
||||
const uniquePropertyIdentifiers = ['id', 'username', 'email'];
|
||||
for (const property of Object.keys(preselectedUser)) {
|
||||
@ -380,33 +315,6 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
return false;
|
||||
}
|
||||
|
||||
private getSearchKey(user: IdentityUserModel): string {
|
||||
if (user.id) {
|
||||
return 'id';
|
||||
} else if (user.email) {
|
||||
return 'email';
|
||||
} else if (user.username) {
|
||||
return 'username';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async searchUser(user: IdentityUserModel): Promise<IdentityUserModel> {
|
||||
const key = this.getSearchKey(user);
|
||||
|
||||
switch (key) {
|
||||
case 'id':
|
||||
return this.identityUserService.findUserById(user[key]).toPromise();
|
||||
case 'username':
|
||||
return (await this.identityUserService.findUserByUsername(user[key]).toPromise())[0];
|
||||
case 'email':
|
||||
return (await this.identityUserService.findUserByEmail(user[key]).toPromise())[0];
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
removeDuplicatedUsers(users: IdentityUserModel[]): IdentityUserModel[] {
|
||||
return users.filter((user, index, self) =>
|
||||
index === self.findIndex(auxUser =>
|
||||
@ -414,19 +322,6 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
));
|
||||
}
|
||||
|
||||
checkPreselectValidationErrors(): void {
|
||||
this.invalidUsers = this.removeDuplicatedUsers(this.invalidUsers);
|
||||
|
||||
if (this.invalidUsers.length > 0) {
|
||||
this.generateInvalidUsersMessage();
|
||||
}
|
||||
|
||||
this.warning.emit({
|
||||
message: 'INVALID_PRESELECTED_USERS',
|
||||
users: this.invalidUsers
|
||||
});
|
||||
}
|
||||
|
||||
onSelect(user: IdentityUserModel): void {
|
||||
if (user) {
|
||||
this.selectUser.emit(user);
|
||||
@ -442,7 +337,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
this.userInput.nativeElement.value = '';
|
||||
this.searchUserCtrl.setValue('');
|
||||
this.userChipsCtrlValue(this.selectedUsers[0].username);
|
||||
this.userChipsControlValue(this.selectedUsers[0].username);
|
||||
|
||||
this.changedUsers.emit(this.selectedUsers);
|
||||
this.resetSearchUsers();
|
||||
@ -454,10 +349,10 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.removeUserFromSelected(userToRemove);
|
||||
this.changedUsers.emit(this.selectedUsers);
|
||||
if (this.selectedUsers.length === 0) {
|
||||
this.userChipsCtrlValue('');
|
||||
this.userChipsControlValue('');
|
||||
|
||||
} else {
|
||||
this.userChipsCtrlValue(this.selectedUsers[0].username);
|
||||
this.userChipsControlValue(this.selectedUsers[0].username);
|
||||
}
|
||||
this.searchUserCtrl.markAsDirty();
|
||||
this.searchUserCtrl.markAsTouched();
|
||||
@ -468,10 +363,17 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private userChipsCtrlValue(value: string) {
|
||||
this.userChipsCtrl.setValue(value);
|
||||
this.userChipsCtrl.markAsDirty();
|
||||
this.userChipsCtrl.markAsTouched();
|
||||
private checkPreselectValidationErrors(): void {
|
||||
this.invalidUsers = this.removeDuplicatedUsers(this.invalidUsers);
|
||||
|
||||
if (this.invalidUsers.length > 0) {
|
||||
this.generateInvalidUsersMessage();
|
||||
}
|
||||
|
||||
this.warning.emit({
|
||||
message: 'INVALID_PRESELECTED_USERS',
|
||||
users: this.invalidUsers
|
||||
});
|
||||
}
|
||||
|
||||
private removeUserFromSelected({ id, username, email }: IdentityUserModel): void {
|
||||
@ -494,7 +396,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
generateInvalidUsersMessage(): void {
|
||||
private generateInvalidUsersMessage(): void {
|
||||
this.validateUsersMessage = '';
|
||||
|
||||
this.invalidUsers.forEach((invalidUser, index) => {
|
||||
@ -506,13 +408,6 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
setTypingError(): void {
|
||||
this.searchUserCtrl.setErrors({
|
||||
searchTypingError: true,
|
||||
...this.searchUserCtrl.errors
|
||||
});
|
||||
}
|
||||
|
||||
hasPreselectError(): boolean {
|
||||
return this.invalidUsers
|
||||
&& this.invalidUsers.length > 0;
|
||||
@ -522,11 +417,11 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
return FullNamePipe.prototype.transform(user);
|
||||
}
|
||||
|
||||
isMultipleMode(): boolean {
|
||||
private isMultipleMode(): boolean {
|
||||
return this.mode === 'multiple';
|
||||
}
|
||||
|
||||
isSingleMode(): boolean {
|
||||
private isSingleMode(): boolean {
|
||||
return this.mode === 'single';
|
||||
}
|
||||
|
||||
@ -541,9 +436,22 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
&& this.preSelectUsers.length > 0;
|
||||
}
|
||||
|
||||
private valueChanged(change: SimpleChange): boolean {
|
||||
return change
|
||||
&& change.currentValue !== change.previousValue;
|
||||
private hasModeChanged(changes: SimpleChanges): boolean {
|
||||
return changes
|
||||
&& changes.mode
|
||||
&& changes.mode.currentValue !== changes.mode.previousValue;
|
||||
}
|
||||
|
||||
private isValidationChanged(changes: SimpleChanges): boolean {
|
||||
return changes
|
||||
&& changes.validate
|
||||
&& changes.validate.currentValue !== changes.validate.previousValue;
|
||||
}
|
||||
|
||||
private hasPreselectedUsersChanged(changes: SimpleChanges): boolean {
|
||||
return changes
|
||||
&& changes.preSelectUsers
|
||||
&& changes.preSelectUsers.currentValue !== changes.preSelectUsers.previousValue;
|
||||
}
|
||||
|
||||
private hasPreselectedUsersCleared(changes: SimpleChanges): boolean {
|
||||
@ -554,20 +462,21 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
|
||||
private resetSearchUsers(): void {
|
||||
this._searchUsers = [];
|
||||
this.searchUsers$.next(this._searchUsers);
|
||||
this.searchUsers = [];
|
||||
this.searchUsers$.next(this.searchUsers);
|
||||
}
|
||||
|
||||
private isUserPartOfAllRestrictedGroups(user: IdentityUserModel): Observable<boolean> {
|
||||
return this.getUserGroups(user.id).pipe(
|
||||
map(userGroups => this.groupsRestriction.every(restricted => userGroups.includes(restricted)))
|
||||
);
|
||||
private setTypingError(): void {
|
||||
this.searchUserCtrl.setErrors({
|
||||
searchTypingError: true,
|
||||
...this.searchUserCtrl.errors
|
||||
});
|
||||
}
|
||||
|
||||
private getUserGroups(userId: string): Observable<string[]> {
|
||||
return this.identityUserService.getInvolvedGroups(userId).pipe(
|
||||
map(groups => groups.map((group) => group.name))
|
||||
);
|
||||
private userChipsControlValue(value: string) {
|
||||
this.userChipsCtrl.setValue(value);
|
||||
this.userChipsCtrl.markAsDirty();
|
||||
this.userChipsCtrl.markAsTouched();
|
||||
}
|
||||
|
||||
getSelectedUsers(): IdentityUserModel[] {
|
||||
@ -617,4 +526,9 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
||||
getValidationMinLength(): string {
|
||||
return this.searchUserCtrl.errors.minlength.requiredLength;
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.onDestroy$.next(true);
|
||||
this.onDestroy$.complete();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,90 @@
|
||||
/*!
|
||||
* @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 { HttpErrorResponse } from '@angular/common/http';
|
||||
import { throwError } from 'rxjs';
|
||||
import { IdentityUserModel } from '../models/identity-user.model';
|
||||
import { IdentityUserFilterInterface } from '../services/identity-user-filter.interface';
|
||||
|
||||
export const mockSearchUserEmptyFilters: IdentityUserFilterInterface = {
|
||||
roles: [],
|
||||
groups: [],
|
||||
withinApplication: ''
|
||||
};
|
||||
|
||||
export const mockSearchUserByGroups: IdentityUserFilterInterface = {
|
||||
roles: [],
|
||||
groups: ['fake-group-1', 'fake-group-2'],
|
||||
withinApplication: ''
|
||||
};
|
||||
|
||||
export const mockSearchUserByGroupsAndRoles: IdentityUserFilterInterface = {
|
||||
roles: ['fake-role-1', 'fake-role-2'],
|
||||
groups: ['fake-group-1', 'fake-group-2'],
|
||||
withinApplication: ''
|
||||
};
|
||||
|
||||
export const mockSearchUserByGroupsAndRolesAndApp: IdentityUserFilterInterface = {
|
||||
roles: ['fake-role-1', 'fake-role-2'],
|
||||
groups: ['fake-group-1', 'fake-group-2'],
|
||||
withinApplication: 'fake-app-name'
|
||||
};
|
||||
|
||||
export const mockSearchUserByRoles: IdentityUserFilterInterface = {
|
||||
roles: ['fake-role-1', 'fake-role-2'],
|
||||
groups: [],
|
||||
withinApplication: ''
|
||||
};
|
||||
|
||||
export const mockSearchUserByRolesAndApp: IdentityUserFilterInterface = {
|
||||
roles: ['fake-role-1', 'fake-role-2'],
|
||||
groups: [],
|
||||
withinApplication: 'fake-app-name'
|
||||
};
|
||||
|
||||
export const mockSearchUserByApp: IdentityUserFilterInterface = {
|
||||
roles: [],
|
||||
groups: [],
|
||||
withinApplication: 'fake-app-name'
|
||||
};
|
||||
|
||||
export const mockSearchUserByAppAndGroups: IdentityUserFilterInterface = {
|
||||
roles: [],
|
||||
groups: ['fake-group-1', 'fake-group-2'],
|
||||
withinApplication: 'fake-app-name'
|
||||
};
|
||||
|
||||
export function oAuthMockApiWithIdentityUsers(users: IdentityUserModel[]) {
|
||||
return {
|
||||
oauth2Auth: {
|
||||
callCustomApi: () => Promise.resolve(users)
|
||||
},
|
||||
reply: jasmine.createSpy('reply')
|
||||
};
|
||||
}
|
||||
|
||||
const errorResponse = new HttpErrorResponse({
|
||||
error: 'Mock Error',
|
||||
status: 404, statusText: 'Not Found'
|
||||
});
|
||||
|
||||
export const oAuthMockApiWithError = {
|
||||
oauth2Auth: {
|
||||
callCustomApi: () => throwError(errorResponse)
|
||||
},
|
||||
reply: jasmine.createSpy('reply')
|
||||
};
|
@ -0,0 +1,55 @@
|
||||
/*!
|
||||
* @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 { Observable, EMPTY, of } from 'rxjs';
|
||||
import { IdentityUserModel } from '../models/identity-user.model';
|
||||
import { IdentityUserFilterInterface } from '../services/identity-user-filter.interface';
|
||||
import { IdentityUserServiceInterface } from '../services/identity-user.service.interface';
|
||||
|
||||
export const mockYorkshirePudding: IdentityUserModel = { id: 'yorkshire', username: 'Yorkshire Pudding', firstName: 'Yorkshire', lastName: 'Pudding', email: 'pudding@food.com' };
|
||||
export const mockShepherdsPie: IdentityUserModel = { id: 'shepherds', username: 'Shepherds Pie', firstName: 'Shepherds', lastName: 'Pie', email: 'shepherds@food.com'};
|
||||
export const mockKielbasaSausage: IdentityUserModel = { id: 'kielbasa', username: 'Kielbasa Sausage', firstName: 'Kielbasa', lastName: 'Sausage', email: 'sausage@food.com' };
|
||||
|
||||
export const mockFoodUsers: IdentityUserModel[] = [mockYorkshirePudding, mockShepherdsPie, mockKielbasaSausage];
|
||||
|
||||
export const mockPreselectedFoodUsers = [
|
||||
{ ...mockYorkshirePudding, readonly: false },
|
||||
{ ...mockKielbasaSausage, readonly: false }
|
||||
];
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class IdentityUserServiceMock implements IdentityUserServiceInterface {
|
||||
|
||||
queryParams: { search: string; application?: string; roles?: string[]; groups?: string[] };
|
||||
|
||||
getCurrentUserInfo(): IdentityUserModel {
|
||||
return mockKielbasaSausage;
|
||||
}
|
||||
|
||||
search(name: string, _filters?: IdentityUserFilterInterface): Observable<IdentityUserModel[]> {
|
||||
if (name.trim() === '') {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
return of(mockFoodUsers.filter(group =>
|
||||
group.username.toUpperCase().includes(name.toUpperCase())
|
||||
));
|
||||
}
|
||||
}
|
@ -1,52 +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 const mockUsers = [
|
||||
{ id: 'fake-id-1', username: 'first-name-1 last-name-1', firstName: 'first-name-1', lastName: 'last-name-1', email: 'abc@xyz.com' },
|
||||
{ id: 'fake-id-2', username: 'first-name-2 last-name-2', firstName: 'first-name-2', lastName: 'last-name-2', email: 'abcd@xyz.com'},
|
||||
{ id: 'fake-id-3', username: 'first-name-3 last-name-3', firstName: 'first-name-3', lastName: 'last-name-3', email: 'abcde@xyz.com' }
|
||||
];
|
||||
|
||||
export const cloudMockUser = {
|
||||
id: 'fake-id-1', username: 'AssignedTaskUser', firstName: 'first-name-1', lastName: 'last-name-1', email: 'abc@xyz.com'
|
||||
};
|
||||
|
||||
export const mockRoles = [
|
||||
{ id: 'id-1', name: 'MOCK-ADMIN-ROLE'},
|
||||
{ id: 'id-2', name: 'MOCK-USER-ROLE'},
|
||||
{ id: 'id-3', name: 'MOCK_MODELER-ROLE' },
|
||||
{ id: 'id-4', name: 'MOCK-ROLE-1' },
|
||||
{ id: 'id-5', name: 'MOCK-ROLE-2'}
|
||||
];
|
||||
|
||||
export const mockOAuth2: any = {
|
||||
oauth2Auth: {
|
||||
callCustomApi: () => Promise.resolve(mockUsers)
|
||||
},
|
||||
isEcmLoggedIn: () => false,
|
||||
reply: jasmine.createSpy('reply')
|
||||
};
|
||||
|
||||
export const mockPreselectedUsers = [
|
||||
{ id: mockUsers[1].id, username: mockUsers[1].username, readonly: false },
|
||||
{ id: mockUsers[2].id, username: mockUsers[2].username, readonly: false }
|
||||
];
|
||||
|
||||
export const mockInvolvedGroups = [
|
||||
{ id: 'mock-group-id-1', name: 'Mock Group 1', path: '/mock', subGroups: [] },
|
||||
{ id: 'mock-group-id-2', name: 'Mock Group 2', path: '', subGroups: [] }
|
||||
];
|
@ -0,0 +1,25 @@
|
||||
/*!
|
||||
* @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 interface IdentityUserModel {
|
||||
id?: string;
|
||||
username: string;
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
email?: string;
|
||||
readonly?: boolean;
|
||||
}
|
@ -22,6 +22,8 @@ import { MaterialModule } from '../material.module';
|
||||
import { CoreModule } from '@alfresco/adf-core';
|
||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
import { IdentityUserService } from './services/identity-user.service';
|
||||
import { IDENTITY_USER_SERVICE_TOKEN } from './services/identity-user-service.token';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -35,6 +37,9 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
declarations: [PeopleCloudComponent],
|
||||
exports: [
|
||||
PeopleCloudComponent
|
||||
],
|
||||
providers: [
|
||||
{ provide: IDENTITY_USER_SERVICE_TOKEN, useExisting: IdentityUserService }
|
||||
]
|
||||
})
|
||||
export class PeopleCloudModule {
|
||||
|
@ -16,5 +16,7 @@
|
||||
*/
|
||||
|
||||
export * from './components/people-cloud.component';
|
||||
|
||||
export * from './people-cloud.module';
|
||||
export * from './models/identity-user.model';
|
||||
export * from './services/identity-user.service';
|
||||
export * from './services/identity-user-service.token';
|
||||
|
@ -0,0 +1,22 @@
|
||||
/*!
|
||||
* @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 interface IdentityUserFilterInterface {
|
||||
roles?: string[];
|
||||
withinApplication?: string;
|
||||
groups?: string[];
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*!
|
||||
* @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 { InjectionToken } from '@angular/core';
|
||||
import { IdentityUserServiceInterface } from './identity-user.service.interface';
|
||||
|
||||
export const IDENTITY_USER_SERVICE_TOKEN = new InjectionToken<IdentityUserServiceInterface>('identity-user-service-token');
|
@ -0,0 +1,25 @@
|
||||
/*!
|
||||
* @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 { Observable } from 'rxjs';
|
||||
import { IdentityUserModel } from '../models/identity-user.model';
|
||||
import { IdentityUserFilterInterface } from './identity-user-filter.interface';
|
||||
|
||||
export interface IdentityUserServiceInterface {
|
||||
getCurrentUserInfo(): IdentityUserModel;
|
||||
search(name: string, filters?: IdentityUserFilterInterface): Observable<IdentityUserModel[]>;
|
||||
}
|
@ -0,0 +1,293 @@
|
||||
/*!
|
||||
* @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 { TestBed } from '@angular/core/testing';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { AlfrescoApiService, JwtHelperService, mockToken, setupTestBed } from '@alfresco/adf-core';
|
||||
import { IdentityUserService } from './identity-user.service';
|
||||
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
|
||||
import {
|
||||
mockSearchUserByApp,
|
||||
mockSearchUserByAppAndGroups,
|
||||
mockSearchUserByGroups,
|
||||
mockSearchUserByGroupsAndRoles,
|
||||
mockSearchUserByGroupsAndRolesAndApp,
|
||||
mockSearchUserByRoles,
|
||||
mockSearchUserByRolesAndApp,
|
||||
oAuthMockApiWithError,
|
||||
oAuthMockApiWithIdentityUsers
|
||||
} from '../mock/identity-user.service.mock';
|
||||
import { mockFoodUsers } from '../mock/people-cloud.mock';
|
||||
|
||||
describe('IdentityUserService', () => {
|
||||
|
||||
let service: IdentityUserService;
|
||||
let alfrescoApiService: AlfrescoApiService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
ProcessServiceCloudTestingModule
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
service = TestBed.inject(IdentityUserService);
|
||||
alfrescoApiService = TestBed.inject(AlfrescoApiService);
|
||||
});
|
||||
|
||||
describe('Current user info (JWT token)', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
const store = {};
|
||||
|
||||
spyOn(localStorage, 'getItem').and.callFake((key: string): string => store[key] || null);
|
||||
spyOn(localStorage, 'setItem').and.callFake((key: string, value: string): string => store[key] = value);
|
||||
});
|
||||
|
||||
it('should fetch identity user info from Jwt id token', () => {
|
||||
localStorage.setItem(JwtHelperService.USER_ID_TOKEN, mockToken);
|
||||
const user = service.getCurrentUserInfo();
|
||||
expect(user).toBeDefined();
|
||||
expect(user.firstName).toEqual('John');
|
||||
expect(user.lastName).toEqual('Doe');
|
||||
expect(user.email).toEqual('johnDoe@gmail.com');
|
||||
expect(user.username).toEqual('johnDoe1');
|
||||
});
|
||||
|
||||
it('should fallback on Jwt access token for identity user info', () => {
|
||||
localStorage.setItem(JwtHelperService.USER_ACCESS_TOKEN, mockToken);
|
||||
const user = service.getCurrentUserInfo();
|
||||
expect(user).toBeDefined();
|
||||
expect(user.firstName).toEqual('John');
|
||||
expect(user.lastName).toEqual('Doe');
|
||||
expect(user.email).toEqual('johnDoe@gmail.com');
|
||||
expect(user.username).toEqual('johnDoe1');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('Search', () => {
|
||||
|
||||
it('should fetch users', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithIdentityUsers(mockFoodUsers));
|
||||
const searchSpy = spyOn(service, 'search').and.callThrough();
|
||||
|
||||
service.search('fake').subscribe(
|
||||
res => {
|
||||
expect(res).toBeDefined();
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake'
|
||||
});
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should not fetch users if error occurred', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithError);
|
||||
|
||||
const searchSpy = spyOn(service, 'search').and.callThrough();
|
||||
|
||||
service.search('fake')
|
||||
.subscribe(
|
||||
() => {
|
||||
fail('expected an error, not users');
|
||||
},
|
||||
(error) => {
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(error.status).toEqual(404);
|
||||
expect(error.statusText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch users by roles', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithIdentityUsers(mockFoodUsers));
|
||||
const searchSpy = spyOn(service, 'search').and.callThrough();
|
||||
|
||||
service.search('fake', mockSearchUserByRoles).subscribe(
|
||||
res => {
|
||||
expect(res).toBeDefined();
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake',
|
||||
role: 'fake-role-1,fake-role-2'
|
||||
});
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should not fetch users by roles if error occurred', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithError);
|
||||
|
||||
service.search('fake', mockSearchUserByRoles)
|
||||
.subscribe(
|
||||
() => {
|
||||
fail('expected an error, not users');
|
||||
},
|
||||
(error) => {
|
||||
expect(error.status).toEqual(404);
|
||||
expect(error.statusText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch users by groups', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithIdentityUsers(mockFoodUsers));
|
||||
const searchSpy = spyOn(service, 'search').and.callThrough();
|
||||
|
||||
service.search('fake', mockSearchUserByGroups).subscribe(
|
||||
res => {
|
||||
expect(res).toBeDefined();
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake',
|
||||
group: 'fake-group-1,fake-group-2'
|
||||
});
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch users by roles with groups', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithIdentityUsers(mockFoodUsers));
|
||||
const searchSpy = spyOn(service, 'search').and.callThrough();
|
||||
|
||||
service.search('fake', mockSearchUserByGroupsAndRoles).subscribe(
|
||||
res => {
|
||||
expect(res).toBeDefined();
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake',
|
||||
role: 'fake-role-1,fake-role-2',
|
||||
group: 'fake-group-1,fake-group-2'
|
||||
});
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch users by roles with groups and appName', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithIdentityUsers(mockFoodUsers));
|
||||
const searchSpy = spyOn(service, 'search').and.callThrough();
|
||||
|
||||
service.search('fake', mockSearchUserByGroupsAndRolesAndApp).subscribe(
|
||||
res => {
|
||||
expect(res).toBeDefined();
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake',
|
||||
role: 'fake-role-1,fake-role-2',
|
||||
application: 'fake-app-name',
|
||||
group: 'fake-group-1,fake-group-2'
|
||||
});
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should not fetch users by groups if error occurred', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithError);
|
||||
|
||||
service.search('fake', mockSearchUserByGroups)
|
||||
.subscribe(
|
||||
() => {
|
||||
fail('expected an error, not users');
|
||||
},
|
||||
(error) => {
|
||||
expect(error.status).toEqual(404);
|
||||
expect(error.statusText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch users within app', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithIdentityUsers(mockFoodUsers));
|
||||
|
||||
service.search('fake', mockSearchUserByApp).subscribe(
|
||||
res => {
|
||||
expect(res).toBeDefined();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake',
|
||||
application: 'fake-app-name'
|
||||
});
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch users within app with roles', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithIdentityUsers(mockFoodUsers));
|
||||
|
||||
service.search('fake', mockSearchUserByRolesAndApp).subscribe(
|
||||
res => {
|
||||
expect(res).toBeDefined();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake',
|
||||
application: 'fake-app-name',
|
||||
role: 'fake-role-1,fake-role-2'
|
||||
});
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should fetch users within app with groups', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithIdentityUsers(mockFoodUsers));
|
||||
const searchSpy = spyOn(service, 'search').and.callThrough();
|
||||
|
||||
service.search('fake', mockSearchUserByAppAndGroups).subscribe(
|
||||
res => {
|
||||
expect(res).toBeDefined();
|
||||
expect(searchSpy).toHaveBeenCalled();
|
||||
expect(service.queryParams).toEqual({
|
||||
search: 'fake',
|
||||
application: 'fake-app-name',
|
||||
group: 'fake-group-1,fake-group-2'
|
||||
});
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should not fetch users within app if error occurred', (done) => {
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(oAuthMockApiWithError);
|
||||
|
||||
service.search('fake', mockSearchUserByApp)
|
||||
.subscribe(
|
||||
() => {
|
||||
fail('expected an error, not users');
|
||||
},
|
||||
(error) => {
|
||||
expect(error.status).toEqual(404);
|
||||
expect(error.statusText).toEqual('Not Found');
|
||||
expect(error.error).toEqual('Mock Error');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,150 @@
|
||||
/*!
|
||||
* @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 {
|
||||
AppConfigService,
|
||||
JwtHelperService,
|
||||
OAuth2Service
|
||||
} from '@alfresco/adf-core';
|
||||
import { EMPTY, Observable, throwError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { IdentityUserServiceInterface } from './identity-user.service.interface';
|
||||
import { IdentityUserModel } from '../models/identity-user.model';
|
||||
import { IdentityUserFilterInterface } from './identity-user-filter.interface';
|
||||
|
||||
const IDENTITY_MICRO_SERVICE_INGRESS = 'modeling-service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class IdentityUserService implements IdentityUserServiceInterface {
|
||||
|
||||
queryParams: { search: string; application?: string; roles?: string[]; groups?: string[] };
|
||||
|
||||
constructor(
|
||||
private jwtHelperService: JwtHelperService,
|
||||
private oAuth2Service: OAuth2Service,
|
||||
private appConfigService: AppConfigService) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name and other basic details of the current user.
|
||||
*
|
||||
* @returns The user's details
|
||||
*/
|
||||
public getCurrentUserInfo(): IdentityUserModel {
|
||||
const familyName = this.jwtHelperService.getValueFromLocalToken<string>(JwtHelperService.FAMILY_NAME);
|
||||
const givenName = this.jwtHelperService.getValueFromLocalToken<string>(JwtHelperService.GIVEN_NAME);
|
||||
const email = this.jwtHelperService.getValueFromLocalToken<string>(JwtHelperService.USER_EMAIL);
|
||||
const username = this.jwtHelperService.getValueFromLocalToken<string>(JwtHelperService.USER_PREFERRED_USERNAME);
|
||||
return { firstName: givenName, lastName: familyName, email, username };
|
||||
}
|
||||
|
||||
/**
|
||||
* Search users based on name input and filters.
|
||||
*
|
||||
* @param name Search query string
|
||||
* @param [filters] Search query filters
|
||||
* @returns List of users
|
||||
*/
|
||||
public search(name: string, filters?: IdentityUserFilterInterface): Observable<IdentityUserModel[]> {
|
||||
if (name.trim() === '') {
|
||||
return EMPTY;
|
||||
} else if (filters?.groups?.length > 0) {
|
||||
return this.searchUsersWithGroups(name, filters);
|
||||
} else if (filters?.withinApplication) {
|
||||
return this.searchUsersWithinApp(name, filters.withinApplication, filters?.roles);
|
||||
} else if (filters?.roles?.length > 0) {
|
||||
return this.searchUsersWithGlobalRoles(name, filters.roles);
|
||||
} else {
|
||||
return this.searchUsersByName(name);
|
||||
}
|
||||
}
|
||||
|
||||
private searchUsersByName(name: string): Observable<IdentityUserModel[]> {
|
||||
this.buildQueryParam(name);
|
||||
|
||||
return this.invokeIdentityUserApi().pipe(
|
||||
catchError((err) => this.handleError(err))
|
||||
);
|
||||
}
|
||||
|
||||
private searchUsersWithGlobalRoles(name: string, roles: string []): Observable<IdentityUserModel[]> {
|
||||
this.buildQueryParam(name, {roles});
|
||||
|
||||
return this.invokeIdentityUserApi().pipe(
|
||||
catchError((err) => this.handleError(err))
|
||||
);
|
||||
}
|
||||
|
||||
private searchUsersWithinApp(name: string, withinApplication: string, roles?: string []): Observable<IdentityUserModel[]> {
|
||||
this.buildQueryParam(name, {roles, withinApplication});
|
||||
|
||||
return this.invokeIdentityUserApi().pipe(
|
||||
catchError((err) => this.handleError(err))
|
||||
);
|
||||
}
|
||||
|
||||
private searchUsersWithGroups(name: string, filters: IdentityUserFilterInterface): Observable<IdentityUserModel[]> {
|
||||
this.buildQueryParam(name, filters);
|
||||
|
||||
return this.invokeIdentityUserApi().pipe(
|
||||
catchError((err) => this.handleError(err))
|
||||
);
|
||||
}
|
||||
|
||||
private invokeIdentityUserApi(): Observable<any> {
|
||||
const url = `${this.identityHost}/${IDENTITY_MICRO_SERVICE_INGRESS}/v1/identity/users`;
|
||||
return this.oAuth2Service.get({ url, queryParams: this.queryParams });
|
||||
}
|
||||
|
||||
private buildQueryParam(name: string, filters?: IdentityUserFilterInterface) {
|
||||
this.queryParams = { search: name };
|
||||
this.addOptionalValueToQueryParam('application', filters?.withinApplication);
|
||||
this.addOptionalCommaValueToQueryParam('role', filters?.roles);
|
||||
this.addOptionalCommaValueToQueryParam('group', filters?.groups);
|
||||
}
|
||||
|
||||
private addOptionalCommaValueToQueryParam(key: string, values: string []) {
|
||||
if (values?.length > 0) {
|
||||
const valuesNotEmpty = this.filterOutEmptyValue(values);
|
||||
if (valuesNotEmpty?.length > 0) {
|
||||
this.queryParams[key] = valuesNotEmpty.join(',');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private addOptionalValueToQueryParam(key: string, value: string) {
|
||||
if (value?.trim()) {
|
||||
this.queryParams[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
private filterOutEmptyValue(values: string []): string [] {
|
||||
return values.filter( value => value.trim() ? true : false);
|
||||
}
|
||||
|
||||
private get identityHost(): string {
|
||||
return `${this.appConfigService.get('bpmHost')}`;
|
||||
}
|
||||
|
||||
private handleError(error: any) {
|
||||
return throwError(error || 'Server error');
|
||||
}
|
||||
}
|
@ -17,11 +17,12 @@
|
||||
|
||||
import { Component, ViewChild } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { IdentityUserService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { CancelProcessDirective } from './cancel-process.directive';
|
||||
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ProcessInstanceCloud } from '../start-process/models/process-instance-cloud.model';
|
||||
import { IdentityUserService } from '../../people/services/identity-user.service';
|
||||
|
||||
const processDetailsMockRunning: ProcessInstanceCloud = { initiator: 'usermock', status: 'RUNNING' };
|
||||
const processDetailsMockCompleted: ProcessInstanceCloud = { initiator: 'usermock', status: 'COMPLETED' };
|
||||
|
@ -15,11 +15,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Directive, HostListener, Output, EventEmitter, OnInit, OnDestroy, ElementRef } from '@angular/core';
|
||||
import { IdentityUserService } from '@alfresco/adf-core';
|
||||
import { ProcessCloudService } from '../services/process-cloud.service';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { Subject } from 'rxjs';
|
||||
import { ProcessInstanceCloud } from '../start-process/models/process-instance-cloud.model';
|
||||
import { IdentityUserService } from '../../people/services/identity-user.service';
|
||||
|
||||
@Directive({
|
||||
// eslint-disable-next-line @angular-eslint/directive-selector
|
||||
|
@ -25,11 +25,12 @@ import moment from 'moment-es6';
|
||||
import { Moment } from 'moment';
|
||||
import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service';
|
||||
import { ProcessFilterCloudModel, ProcessFilterProperties, ProcessFilterAction, ProcessFilterOptions, ProcessSortFilterProperty } from '../models/process-filter-cloud.model';
|
||||
import { IdentityUserModel, TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core';
|
||||
import { TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core';
|
||||
import { ProcessFilterCloudService } from '../services/process-filter-cloud.service';
|
||||
import { ProcessFilterDialogCloudComponent } from './process-filter-dialog-cloud.component';
|
||||
import { ProcessCloudService } from '../../services/process-cloud.service';
|
||||
import { DateCloudFilterType, DateRangeFilter } from '../../../models/date-cloud-filter.model';
|
||||
import { IdentityUserModel } from '../../../people/models/identity-user.model';
|
||||
|
||||
export const PROCESS_FILTER_ACTION_SAVE = 'save';
|
||||
export const PROCESS_FILTER_ACTION_SAVE_AS = 'saveAs';
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { setupTestBed, IdentityUserService } from '@alfresco/adf-core';
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { of } from 'rxjs';
|
||||
import { ProcessFilterCloudService } from './process-filter-cloud.service';
|
||||
import { PROCESS_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
||||
@ -25,6 +25,7 @@ import { ProcessServiceCloudTestingModule } from '../../../testing/process-servi
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { fakeEmptyProcessCloudFilterEntries, fakeProcessCloudFilterEntries, fakeProcessCloudFilters, fakeProcessCloudFilterWithDifferentEntries, fakeProcessFilter } from '../mock/process-filters-cloud.mock';
|
||||
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
|
||||
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||
|
||||
describe('ProcessFilterCloudService', () => {
|
||||
let service: ProcessFilterCloudService;
|
||||
|
@ -15,13 +15,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { IdentityUserService } from '@alfresco/adf-core';
|
||||
import { Injectable, Inject } from '@angular/core';
|
||||
import { Observable, of, BehaviorSubject, throwError } from 'rxjs';
|
||||
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
|
||||
import { switchMap, map, catchError } from 'rxjs/operators';
|
||||
import { PROCESS_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
||||
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
|
||||
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
|
@ -39,8 +39,7 @@ export class BaseCloudService {
|
||||
path: '',
|
||||
httpMethod: '',
|
||||
contentTypes: ['application/json'],
|
||||
accepts: ['application/json'],
|
||||
returnType: Object
|
||||
accepts: ['application/json']
|
||||
};
|
||||
|
||||
constructor(
|
||||
|
@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Directive, Input, HostListener, Output, EventEmitter, OnInit } from '@angular/core';
|
||||
import { IdentityUserService } from '@alfresco/adf-core';
|
||||
import { IdentityUserService } from '../../people/services/identity-user.service';
|
||||
import { TaskCloudService } from '../services/task-cloud.service';
|
||||
|
||||
@Directive({
|
||||
|
@ -18,8 +18,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AppConfigService, CardViewArrayItem, LogService } from '@alfresco/adf-core';
|
||||
import { from, Observable, of, Subject, throwError } from 'rxjs';
|
||||
import { DEFAULT_TASK_PRIORITIES, TaskPriorityOption, TASK_ASSIGNED_STATE, TASK_CREATED_STATE } from '../models/task.model';
|
||||
import { TaskDetailsCloudModel } from '../start-task/public-api';
|
||||
import { DEFAULT_TASK_PRIORITIES, TaskPriorityOption } from '../models/task.model';
|
||||
import { TaskDetailsCloudModel, TASK_ASSIGNED_STATE, TASK_CREATED_STATE } from '../start-task/models/task-details-cloud.model';
|
||||
import { taskDetailsContainer } from '../task-header/mocks/task-details-cloud.mock';
|
||||
import { ProcessDefinitionCloud } from '../../models/process-definition-cloud.model';
|
||||
import { StartTaskCloudRequestModel } from '../start-task/models/start-task-cloud-request.model';
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { setupTestBed, IdentityUserService, TranslationService, AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { setupTestBed, TranslationService, AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { TaskCloudService } from './task-cloud.service';
|
||||
import { taskCompleteCloudMock } from '../task-header/mocks/fake-complete-task.mock';
|
||||
import { assignedTaskDetailsCloudMock, createdTaskDetailsCloudMock, emptyOwnerTaskDetailsCloudMock } from '../task-header/mocks/task-details-cloud.mock';
|
||||
@ -24,6 +24,7 @@ import { fakeTaskDetailsCloud } from '../task-header/mocks/fake-task-details-res
|
||||
import { cloudMockUser } from '../start-task/mock/user-cloud.mock';
|
||||
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { IdentityUserService } from '../../people/services/identity-user.service';
|
||||
|
||||
describe('Task Cloud Service', () => {
|
||||
|
||||
|
@ -16,9 +16,9 @@
|
||||
*/
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AlfrescoApiService, LogService, AppConfigService, IdentityUserService, CardViewArrayItem, TranslationService } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiService, LogService, AppConfigService, CardViewArrayItem, TranslationService } from '@alfresco/adf-core';
|
||||
import { throwError, Observable, of, Subject } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { catchError, map } from 'rxjs/operators';
|
||||
import {
|
||||
TaskDetailsCloudModel,
|
||||
StartTaskCloudResponseModel,
|
||||
@ -35,6 +35,7 @@ import {
|
||||
TaskPriorityOption
|
||||
} from '../models/task.model';
|
||||
import { TaskCloudServiceInterface } from './task-cloud.service.interface';
|
||||
import { IdentityUserService } from '../../people/services/identity-user.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@ -242,7 +243,9 @@ export class TaskCloudService extends BaseCloudService implements TaskCloudServi
|
||||
getCandidateUsers(appName: string, taskId: string): Observable<string[]> {
|
||||
if ((appName || appName === '') && taskId) {
|
||||
const queryUrl = `${this.getBasePath(appName)}/query/v1/tasks/${taskId}/candidate-users`;
|
||||
return this.get<string[]>(queryUrl);
|
||||
return this.get<string[]>(queryUrl).pipe(
|
||||
catchError((err) => this.handleError(err))
|
||||
);
|
||||
} else {
|
||||
this.logService.error('AppName and TaskId are mandatory to get candidate user');
|
||||
return of([]);
|
||||
@ -320,4 +323,9 @@ export class TaskCloudService extends BaseCloudService implements TaskCloudServi
|
||||
const currentUser = this.identityUserService.getCurrentUserInfo().username;
|
||||
return assignee === currentUser;
|
||||
}
|
||||
|
||||
private handleError(error?: any) {
|
||||
this.logService.error(error);
|
||||
return throwError(error || 'Server error');
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { setupTestBed, IdentityUserService, AlfrescoApiService, IdentityUserModel } from '@alfresco/adf-core';
|
||||
import { setupTestBed, AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { StartTaskCloudComponent } from './start-task-cloud.component';
|
||||
import { of, throwError } from 'rxjs';
|
||||
import { taskDetailsMock } from '../mock/task-details.mock';
|
||||
@ -26,6 +26,8 @@ import { FormDefinitionSelectorCloudService } from '../../../form/services/form-
|
||||
import { TaskCloudService } from '../../services/task-cloud.service';
|
||||
import { StartTaskCloudRequestModel } from '../models/start-task-cloud-request.model';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||
import { IdentityUserModel } from '../../../people/models/identity-user.model';
|
||||
|
||||
describe('StartTaskCloudComponent', () => {
|
||||
|
||||
|
@ -24,8 +24,6 @@ import {
|
||||
MOMENT_DATE_FORMATS, MomentDateAdapter,
|
||||
LogService,
|
||||
UserPreferencesService,
|
||||
IdentityUserService,
|
||||
IdentityUserModel,
|
||||
UserPreferenceValues
|
||||
} from '@alfresco/adf-core';
|
||||
import { PeopleCloudComponent } from '../../../people/components/people-cloud.component';
|
||||
@ -34,6 +32,8 @@ import { TaskCloudService } from '../../services/task-cloud.service';
|
||||
import { StartTaskCloudRequestModel } from '../models/start-task-cloud-request.model';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { TaskPriorityOption } from '../../models/task.model';
|
||||
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||
import { IdentityUserModel } from '../../../people/models/identity-user.model';
|
||||
|
||||
const MAX_NAME_LENGTH = 255;
|
||||
const DATE_FORMAT: string = 'DD/MM/YYYY';
|
||||
|
@ -25,9 +25,11 @@ import { AbstractControl, FormBuilder, FormGroup } from '@angular/forms';
|
||||
import { debounceTime, filter, finalize, switchMap, takeUntil } from 'rxjs/operators';
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { DateAdapter } from '@angular/material/core';
|
||||
import { IdentityGroupModel, IdentityUserModel, TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core';
|
||||
import { TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core';
|
||||
import { TaskFilterDialogCloudComponent } from '../task-filter-dialog/task-filter-dialog-cloud.component';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { IdentityUserModel } from '../../../../people/models/identity-user.model';
|
||||
import { IdentityGroupModel } from '../../../../group/models/identity-group.model';
|
||||
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
|
||||
|
@ -19,7 +19,7 @@ import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
|
||||
import { SimpleChange } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { AlfrescoApiService, IdentityUserModel, setupTestBed } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { of } from 'rxjs';
|
||||
import { debounceTime } from 'rxjs/operators';
|
||||
@ -41,6 +41,7 @@ import { TaskFilterCloudModel } from '../../models/filter-cloud.model';
|
||||
import { PeopleCloudModule } from '../../../../people/people-cloud.module';
|
||||
import { ProcessDefinitionCloud } from '../../../../models/process-definition-cloud.model';
|
||||
import { MatIconTestingModule } from '@angular/material/icon/testing';
|
||||
import { IdentityUserModel } from '../../../../people/models/identity-user.model';
|
||||
|
||||
describe('EditTaskFilterCloudComponent', () => {
|
||||
let component: EditTaskFilterCloudComponent;
|
||||
@ -537,7 +538,10 @@ describe('EditTaskFilterCloudComponent', () => {
|
||||
|
||||
const mockUser: IdentityUserModel[] = [{
|
||||
id: 'id',
|
||||
username: 'test'
|
||||
username: 'test',
|
||||
firstName: 'first-name',
|
||||
lastName: 'last-name',
|
||||
email: 'email@fake.com'
|
||||
}];
|
||||
|
||||
const startedDateTypeControl: AbstractControl = component.editTaskFilterForm.get('completedBy');
|
||||
|
@ -16,13 +16,14 @@
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { setupTestBed, IdentityUserService, TranslationService, TranslationMock } from '@alfresco/adf-core';
|
||||
import { setupTestBed, TranslationService, TranslationMock } from '@alfresco/adf-core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { TaskAssignmentFilterCloudComponent } from './task-assignment-filter.component';
|
||||
import { GroupCloudModule } from 'process-services-cloud/src/lib/group/public-api';
|
||||
import { TaskFiltersCloudModule } from '../../task-filters-cloud.module';
|
||||
import { AssignmentType } from '../../models/filter-cloud.model';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { IdentityUserService } from '../../../../people/services/identity-user.service';
|
||||
|
||||
describe('EditTaskFilterCloudComponent', () => {
|
||||
let component: TaskAssignmentFilterCloudComponent;
|
||||
|
@ -17,8 +17,10 @@
|
||||
|
||||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { IdentityGroupModel, IdentityUserModel, IdentityUserService } from '@alfresco/adf-core';
|
||||
import { AssignmentType, TaskFilterProperties } from '../../models/filter-cloud.model';
|
||||
import { IdentityUserModel } from '../../../../people/models/identity-user.model';
|
||||
import { IdentityUserService } from '../../../../people/services/identity-user.service';
|
||||
import { IdentityGroupModel } from '../../../../group/models/identity-group.model';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-cloud-task-assignment-filter',
|
||||
|
@ -22,7 +22,8 @@
|
||||
import { DateCloudFilterType } from '../../../models/date-cloud-filter.model';
|
||||
import { DateRangeFilterService } from '../../../common/date-range-filter/date-range-filter.service';
|
||||
import { ComponentSelectionMode } from '../../../types';
|
||||
import { IdentityUserModel, IdentityGroupModel } from '@alfresco/adf-core';
|
||||
import { IdentityGroupModel } from '../../../group/models/identity-group.model';
|
||||
import { IdentityUserModel } from '../../../people/models/identity-user.model';
|
||||
|
||||
export class TaskFilterCloudModel {
|
||||
id: string;
|
||||
|
@ -15,13 +15,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { IdentityUserService } from '@alfresco/adf-core';
|
||||
import { Injectable, Inject } from '@angular/core';
|
||||
import { Observable, of, BehaviorSubject } from 'rxjs';
|
||||
import { ServiceTaskFilterCloudModel } from '../models/filter-cloud.model';
|
||||
import { switchMap, map } from 'rxjs/operators';
|
||||
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
|
||||
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
||||
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { IdentityUserService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { of } from 'rxjs';
|
||||
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
||||
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
|
||||
@ -36,6 +36,7 @@ import { TaskFilterCloudModel } from '../models/filter-cloud.model';
|
||||
import { NotificationCloudService } from '../../../services/notification-cloud.service';
|
||||
import { TaskCloudEngineEvent } from './../../../models/engine-event-cloud.model';
|
||||
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
||||
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||
|
||||
describe('TaskFilterCloudService', () => {
|
||||
let service: TaskFilterCloudService;
|
||||
|
@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AlfrescoApiService, AppConfigService, IdentityUserService } from '@alfresco/adf-core';
|
||||
import { AlfrescoApiService, AppConfigService } from '@alfresco/adf-core';
|
||||
import { Injectable, Inject } from '@angular/core';
|
||||
import { Observable, of, BehaviorSubject, throwError } from 'rxjs';
|
||||
import { TaskFilterCloudModel } from '../models/filter-cloud.model';
|
||||
@ -26,6 +26,7 @@ import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.servic
|
||||
import { TaskCloudNodePaging } from '../../../models/task-cloud.model';
|
||||
import { NotificationCloudService } from '../../../services/notification-cloud.service';
|
||||
import { TaskCloudEngineEvent } from '../../../models/engine-event-cloud.model';
|
||||
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||
|
||||
const TASK_EVENT_SUBSCRIPTION_QUERY = `
|
||||
subscription {
|
||||
|
@ -19,7 +19,7 @@ import { DebugElement, SimpleChange } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { of } from 'rxjs';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { IdentityUserService, setupTestBed } from '@alfresco/adf-core';
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
||||
import { TaskFormCloudComponent } from './task-form-cloud.component';
|
||||
import {
|
||||
@ -32,6 +32,7 @@ import {
|
||||
} from '../../start-task/models/task-details-cloud.model';
|
||||
import { TaskCloudService } from '../../services/task-cloud.service';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||
|
||||
const taskDetails: TaskDetailsCloudModel = {
|
||||
appName: 'simple-app',
|
||||
|
@ -25,11 +25,7 @@ import {
|
||||
AppConfigServiceMock,
|
||||
TranslationService,
|
||||
TranslationMock,
|
||||
CoreModule,
|
||||
IdentityUserService,
|
||||
IdentityUserServiceMock,
|
||||
IdentityGroupService,
|
||||
IdentityGroupServiceMock
|
||||
CoreModule
|
||||
} from '@alfresco/adf-core';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ProcessServicesCloudModule } from '../process-services-cloud.module';
|
||||
@ -47,9 +43,7 @@ import { RouterTestingModule } from '@angular/router/testing';
|
||||
providers: [
|
||||
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
|
||||
{ provide: AppConfigService, useClass: AppConfigServiceMock },
|
||||
{ provide: TranslationService, useClass: TranslationMock },
|
||||
{ provide: IdentityUserService, useClass: IdentityUserServiceMock },
|
||||
{ provide: IdentityGroupService, useClass: IdentityGroupServiceMock }
|
||||
{ provide: TranslationService, useClass: TranslationMock }
|
||||
],
|
||||
exports: [
|
||||
NoopAnimationsModule,
|
||||
|
Loading…
x
Reference in New Issue
Block a user