mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-09-17 14:21:29 +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:
@@ -181,7 +181,7 @@ jobs:
|
|||||||
use:
|
use:
|
||||||
- built_libs_cache
|
- built_libs_cache
|
||||||
- built_demo_shell_cache
|
- built_demo_shell_cache
|
||||||
|
|
||||||
- stage: "e2e Test"
|
- stage: "e2e Test"
|
||||||
name: "Core"
|
name: "Core"
|
||||||
before_script:
|
before_script:
|
||||||
|
@@ -843,8 +843,5 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"defaultProject": "demoshell",
|
"defaultProject": "demoshell"
|
||||||
"cli": {
|
}
|
||||||
"analytics": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -332,7 +332,8 @@
|
|||||||
"ALL_PRESELECTED_GROUPS": "All Preselected Groups",
|
"ALL_PRESELECTED_GROUPS": "All Preselected Groups",
|
||||||
"INVALID_USERS": "Invalid Users",
|
"INVALID_USERS": "Invalid Users",
|
||||||
"INVALID_GROUPS": "Invalid Groups",
|
"INVALID_GROUPS": "Invalid Groups",
|
||||||
"READONLY_MODE": "Read-only Mode"
|
"READONLY_MODE": "Read-only Mode",
|
||||||
|
"GROUPS_RESTRICTION": "Groups Restriction"
|
||||||
},
|
},
|
||||||
"SETTINGS_CLOUD": {
|
"SETTINGS_CLOUD": {
|
||||||
"MULTISELECTION": "Multiselection",
|
"MULTISELECTION": "Multiselection",
|
||||||
|
@@ -12,20 +12,22 @@
|
|||||||
'PEOPLE_GROUPS_CLOUD.MULTI' | translate }}</mat-radio-button>
|
'PEOPLE_GROUPS_CLOUD.MULTI' | translate }}</mat-radio-button>
|
||||||
</mat-radio-group>
|
</mat-radio-group>
|
||||||
<div class="app-people-control-options">
|
<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-form-field *ngIf="isPeopleAppNameSelected()" class="app-preselect-value">
|
||||||
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.APP_NAME' | translate }}</mat-label>
|
<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" />
|
<input matInput (input)="setPeopleAppName($any($event).target?.value)" data-automation-id="app-people-app-input" />
|
||||||
</mat-form-field>
|
</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-form-field class="app-preselect-value-full">
|
||||||
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.PRESELECTED_VALUE' | translate }} {{ defaultPeoplePlaceholder }}</mat-label>
|
<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" />
|
<input matInput (input)="setPeoplePreselectValue($any($event).target?.value)" data-automation-id="app-people-preselect-input" />
|
||||||
@@ -42,6 +44,7 @@
|
|||||||
[validate]="peoplePreselectValidation"
|
[validate]="peoplePreselectValidation"
|
||||||
[appName]="peopleAppName"
|
[appName]="peopleAppName"
|
||||||
[roles]="peopleRoles"
|
[roles]="peopleRoles"
|
||||||
|
[groupsRestriction]="groupsRestriction"
|
||||||
[title]="'ADF_TASK_LIST.START_TASK.FORM.LABEL.ASSIGNEE'"
|
[title]="'ADF_TASK_LIST.START_TASK.FORM.LABEL.ASSIGNEE'"
|
||||||
[mode]="peopleMode"
|
[mode]="peopleMode"
|
||||||
(warning)="onUsersWarning($event)"></adf-cloud-people>
|
(warning)="onUsersWarning($event)"></adf-cloud-people>
|
||||||
@@ -83,24 +86,18 @@
|
|||||||
'PEOPLE_GROUPS_CLOUD.MULTI' | translate }}</mat-radio-button>
|
'PEOPLE_GROUPS_CLOUD.MULTI' | translate }}</mat-radio-button>
|
||||||
</mat-radio-group>
|
</mat-radio-group>
|
||||||
<div class="app-groups-control-options">
|
<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-form-field *ngIf="isGroupAppNameSelected()" class="app-preselect-value">
|
||||||
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.APP_NAME' | translate }}</mat-label>
|
<mat-label>{{ 'PEOPLE_GROUPS_CLOUD.APP_NAME' | translate }}</mat-label>
|
||||||
<input matInput
|
<input matInput
|
||||||
(input)="setGroupAppName($any($event).target?.value)"
|
(input)="setGroupAppName($any($event).target?.value)"
|
||||||
data-automation-id="app-group-app-input"/>
|
data-automation-id="app-group-app-input"/>
|
||||||
</mat-form-field>
|
</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-form-field class="app-preselect-value-full">
|
||||||
<mat-label>Preselect: {{ defaultGroupPlaceholder }}</mat-label>
|
<mat-label>Preselect: {{ defaultGroupPlaceholder }}</mat-label>
|
||||||
<input matInput
|
<input matInput
|
||||||
|
@@ -16,10 +16,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, ViewEncapsulation } from '@angular/core';
|
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 { MatCheckboxChange } from '@angular/material/checkbox';
|
||||||
import { MatRadioChange } from '@angular/material/radio';
|
import { MatRadioChange } from '@angular/material/radio';
|
||||||
import { IdentityGroupModel, IdentityUserModel } from '@alfresco/adf-core';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-people-groups-cloud',
|
selector: 'app-people-groups-cloud',
|
||||||
@@ -37,6 +36,7 @@ export class PeopleGroupCloudDemoComponent {
|
|||||||
preSelectUsers: IdentityUserModel[] = [];
|
preSelectUsers: IdentityUserModel[] = [];
|
||||||
invalidUsers: IdentityUserModel[] = [];
|
invalidUsers: IdentityUserModel[] = [];
|
||||||
peopleRoles: string[] = [];
|
peopleRoles: string[] = [];
|
||||||
|
groupsRestriction: string[] = [];
|
||||||
peopleAppName: string;
|
peopleAppName: string;
|
||||||
peopleFilterMode: string = this.defaultFilterMode;
|
peopleFilterMode: string = this.defaultFilterMode;
|
||||||
peoplePreselectValidation = false;
|
peoplePreselectValidation = false;
|
||||||
@@ -75,6 +75,10 @@ export class PeopleGroupCloudDemoComponent {
|
|||||||
this.groupAppName = value;
|
this.groupAppName = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setPeopleGroupsRestriction(value: string): void {
|
||||||
|
this.groupsRestriction = this.getArrayFromString(value);
|
||||||
|
}
|
||||||
|
|
||||||
onChangePeopleMode(event: MatRadioChange): void {
|
onChangePeopleMode(event: MatRadioChange): void {
|
||||||
this.peopleMode = event.value;
|
this.peopleMode = event.value;
|
||||||
}
|
}
|
||||||
|
@@ -23,6 +23,6 @@ export class PeopleComponent extends BaseComponent {
|
|||||||
super(page, rootElement);
|
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.describe('Groups component stories tests', () => {
|
||||||
test('Valid Preselected Groups', async ({ processServicesCloud, groupComponent }) => {
|
test('Valid Preselected Groups', async ({ processServicesCloud, groupComponent }) => {
|
||||||
const expectedUsersName = `
|
const expectedUsersName = `
|
||||||
Mock Group 1 cancel
|
Vegetable Aubergine cancel
|
||||||
Mock Group 2 cancel
|
Meat Chicken cancel
|
||||||
Mock Group 3 cancel
|
|
||||||
Mock Group 4 cancel
|
|
||||||
Mock Group 5
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
await processServicesCloud.navigateTo({ componentName: 'group', story: 'valid-preselected-groups' });
|
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 }) => {
|
test('Mandatory Preselected Groups', async ({ processServicesCloud, groupComponent }) => {
|
||||||
const expectedUsersName = `
|
const expectedUsersName = `
|
||||||
Mock Group 1
|
Vegetable Aubergine cancel
|
||||||
Mock Group 2 cancel
|
Meat Chicken
|
||||||
Mock Group 3
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
await processServicesCloud.navigateTo({ componentName: 'group', story: 'mandatory-preselected-groups' });
|
await processServicesCloud.navigateTo({ componentName: 'group', story: 'mandatory-preselected-groups' });
|
||||||
await expect.soft(groupComponent.groupNaming).toContainText(expectedUsersName);
|
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');
|
await expect(groupComponent.tooltip.content).toContainText('Mandatory');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Invalid Preselected Groups', async ({ processServicesCloud, groupComponent }) => {
|
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';
|
const expectedWarningIcon = 'error_outline';
|
||||||
|
|
||||||
await processServicesCloud.navigateTo({ componentName: 'group', story: 'invalid-preselected-groups' });
|
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.describe('People component stories tests', () => {
|
||||||
test('Valid Preselected Users', async ({ processServicesCloud, peopleComponent }) => {
|
test('Valid Preselected Users', async ({ processServicesCloud, peopleComponent }) => {
|
||||||
const expectedUsersName = `
|
const expectedUsersName = `
|
||||||
first-name-1 last-name-1 cancel
|
Yorkshire Pudding cancel
|
||||||
first-name-2 last-name-2 cancel
|
Shepherds Pie cancel
|
||||||
first-name-3 last-name-3 cancel
|
Kielbasa Sausage cancel
|
||||||
first-name-4 last-name-4 cancel
|
|
||||||
first-name-5 last-name-5
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
await processServicesCloud.navigateTo({ componentName: 'people', story: 'valid-preselected-users' });
|
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 }) => {
|
test('Mandatory Preselected Users', async ({ processServicesCloud, peopleComponent }) => {
|
||||||
const expectedUsersName = `
|
const expectedUsersName = `
|
||||||
first-name-1 last-name-1
|
Kielbasa Sausage
|
||||||
first-name-2 last-name-2 cancel
|
Shepherds Pie cancel
|
||||||
`;
|
`;
|
||||||
|
|
||||||
await processServicesCloud.navigateTo({ componentName: 'people', story: 'mandatory-preselected-users' });
|
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.soft(peopleComponent.usersNaming).toContainText(expectedUsersName);
|
||||||
await expect(peopleComponent.tooltip.content).toContainText('Mandatory');
|
await expect(peopleComponent.tooltip.content).toContainText('Mandatory');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Invalid Preselected Users', async ({ processServicesCloud, peopleComponent }) => {
|
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';
|
const expectedWarningIcon = 'error_outline';
|
||||||
|
|
||||||
await processServicesCloud.navigateTo({ componentName: 'people', story: 'invalid-preselected-users' });
|
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 }) => {
|
test('Excluded Users', async ({ processServicesCloud, peopleComponent }) => {
|
||||||
const expectedExcludedUsers = `
|
const expectedExcludedUsers = `
|
||||||
mocked-user-id-2
|
kielbasa
|
||||||
mocked-user-id-3
|
yorkshire
|
||||||
`;
|
`;
|
||||||
|
|
||||||
await processServicesCloud.navigateTo({ componentName: 'people', story: 'excluded-users' });
|
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"]');
|
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"]');
|
peopleCloudSingleSelection = $('mat-radio-button[data-automation-id="app-people-single-mode"]');
|
||||||
peopleCloudMultipleSelection = $('mat-radio-button[data-automation-id="app-people-multiple-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"]');
|
groupCloudSingleSelection = $('mat-radio-button[data-automation-id="app-group-single-mode"]');
|
||||||
groupCloudMultipleSelection = $('mat-radio-button[data-automation-id="app-group-multiple-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"]');
|
peopleRoleInput = $('input[data-automation-id="app-people-roles-input"]');
|
||||||
peopleAppInput = $('input[data-automation-id="app-people-app-input"]');
|
peopleAppInput = $('input[data-automation-id="app-people-app-input"]');
|
||||||
peoplePreselect = $('input[data-automation-id="app-people-preselect-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'));
|
groupCloudComponentTitle = element(by.cssContainingText('mat-card-title', 'Groups Cloud Component'));
|
||||||
preselectValidation = $$('mat-checkbox.app-preselect-value').first();
|
preselectValidation = $$('mat-checkbox.app-preselect-value').first();
|
||||||
preselectValidationStatus = $$('mat-checkbox.app-preselect-value label input').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() {
|
async navigateTo() {
|
||||||
await browser.get('#/cloud/people-group-cloud');
|
await browser.get('#/cloud/people-group-cloud');
|
||||||
@@ -69,18 +65,6 @@ export class PeopleGroupCloudComponentPage {
|
|||||||
await BrowserVisibility.waitUntilElementIsVisible(this.peopleCloudMultipleSelectionChecked);
|
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> {
|
async enterPeopleRoles(roles: string): Promise<void> {
|
||||||
await BrowserActions.clearSendKeys(this.peopleRoleInput, roles);
|
await BrowserActions.clearSendKeys(this.peopleRoleInput, roles);
|
||||||
}
|
}
|
||||||
@@ -113,14 +97,6 @@ export class PeopleGroupCloudComponentPage {
|
|||||||
return BrowserActions.getAttribute(this.preselectValidationStatus, 'aria-checked');
|
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> {
|
async enterPeopleAppName(appName: string): Promise<void> {
|
||||||
await BrowserActions.clearSendKeys(this.peopleAppInput, appName);
|
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', () => {
|
||||||
|
|
||||||
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 apiService = createApiService();
|
||||||
const navigationBarPage = new NavigationBarPage();
|
const identityService = new IdentityService(apiService);
|
||||||
const peopleGroupCloudComponentPage = new PeopleGroupCloudComponentPage();
|
const rolesService = new RolesService(apiService);
|
||||||
const peopleCloudComponent = new PeopleCloudComponentPage();
|
const groupIdentityService = new GroupIdentityService(apiService);
|
||||||
const groupCloudComponentPage = new GroupCloudComponentPage();
|
|
||||||
|
|
||||||
const apiService = createApiService();
|
let apsUser;
|
||||||
const identityService = new IdentityService(apiService);
|
let testUser;
|
||||||
const rolesService = new RolesService(apiService);
|
let devopsUser;
|
||||||
const groupIdentityService = new GroupIdentityService(apiService);
|
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;
|
beforeAll(async () => {
|
||||||
let testUser;
|
await apiService.loginWithProfile('identityAdmin');
|
||||||
let devopsUser;
|
|
||||||
let activitiUser;
|
|
||||||
let noRoleUser;
|
|
||||||
let groupUser;
|
|
||||||
let groupAdmin;
|
|
||||||
let groupNoRole;
|
|
||||||
let apsUserRoleId: string;
|
|
||||||
let apsAdminRoleId: string;
|
|
||||||
let users = [];
|
|
||||||
let groups = [];
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
testUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]);
|
||||||
await apiService.loginWithProfile('identityAdmin');
|
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]);
|
apsAdminRoleId = await rolesService.getRoleIdByRoleName(identityService.ROLES.ACTIVITI_ADMIN);
|
||||||
apsUser = await identityService.createIdentityUserWithRole([identityService.ROLES.ACTIVITI_USER]);
|
apsUserRoleId = await rolesService.getRoleIdByRoleName(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);
|
groupUser = await groupIdentityService.createIdentityGroup();
|
||||||
apsUserRoleId = await rolesService.getRoleIdByRoleName(identityService.ROLES.ACTIVITI_USER);
|
await groupIdentityService.assignRole(groupUser.id, apsUserRoleId, identityService.ROLES.ACTIVITI_USER);
|
||||||
|
|
||||||
groupUser = await groupIdentityService.createIdentityGroup();
|
groupAdmin = await groupIdentityService.createIdentityGroup();
|
||||||
await groupIdentityService.assignRole(groupUser.id, apsUserRoleId, identityService.ROLES.ACTIVITI_USER);
|
await groupIdentityService.assignRole(groupAdmin.id, apsAdminRoleId, identityService.ROLES.ACTIVITI_ADMIN);
|
||||||
|
|
||||||
groupAdmin = await groupIdentityService.createIdentityGroup();
|
groupMultipleRoles = await groupIdentityService.createIdentityGroup();
|
||||||
await groupIdentityService.assignRole(groupAdmin.id, apsAdminRoleId, identityService.ROLES.ACTIVITI_ADMIN);
|
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}`,
|
users = [`${apsUser.idIdentityService}`, `${activitiUser.idIdentityService}`, `${noRoleUser.idIdentityService}`,
|
||||||
`${testUser.idIdentityService}`, `${devopsUser.idIdentityService}`];
|
`${testUser.idIdentityService}`, `${devopsUser.idIdentityService}`, `${multipleRolesUser.idIdentityService}`];
|
||||||
groups = [`${groupUser.id}`, `${groupAdmin.id}`, `${groupNoRole.id}`];
|
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 () => {
|
afterAll(async () => {
|
||||||
await apiService.loginWithProfile('identityAdmin');
|
await apiService.loginWithProfile('identityAdmin');
|
||||||
for (const user of users) {
|
for (const user of users) {
|
||||||
await identityService.deleteIdentityUser(user);
|
await identityService.deleteIdentityUser(user);
|
||||||
}
|
}
|
||||||
for (const group of groups) {
|
for (const group of groups) {
|
||||||
await groupIdentityService.deleteIdentityGroup(group);
|
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 () => {
|
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.clickPeopleCloudMultipleSelection();
|
||||||
await peopleGroupCloudComponentPage.checkPeopleCloudMultipleSelectionIsSelected();
|
await peopleGroupCloudComponentPage.checkPeopleCloudMultipleSelectionIsSelected();
|
||||||
await peopleGroupCloudComponentPage.clickPreselectValidation();
|
});
|
||||||
await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('true');
|
|
||||||
|
|
||||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"id":"${apsUser.idIdentityService}"},{"id":"${testUser.idIdentityService}"},` +
|
it('No role filtering', async () => {
|
||||||
`{"id":"${noRoleUser.idIdentityService}"}]`);
|
await peopleCloudComponent.searchAssignee(noRoleUser.lastName);
|
||||||
await peopleCloudComponent.checkSelectedPeople(`${apsUser.firstName} ${apsUser.lastName}`);
|
await peopleCloudComponent.checkUserIsDisplayed(`${noRoleUser.firstName} ${noRoleUser.lastName}`);
|
||||||
await peopleCloudComponent.checkSelectedPeople(`${testUser.firstName} ${testUser.lastName}`);
|
await peopleCloudComponent.searchAssignee(apsUser.lastName);
|
||||||
await peopleCloudComponent.checkSelectedPeople(`${noRoleUser.firstName} ${noRoleUser.lastName}`);
|
await peopleCloudComponent.checkUserIsDisplayed(`${apsUser.firstName} ${apsUser.lastName}`);
|
||||||
|
await peopleCloudComponent.searchAssignee(testUser.lastName);
|
||||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(`[{"email":"${apsUser.email}"},{"email":"${testUser.email}"},{"email":"${noRoleUser.email}"}]`);
|
await peopleCloudComponent.checkUserIsDisplayed(`${testUser.firstName} ${testUser.lastName}`);
|
||||||
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('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.searchAssignee(noRoleUser.lastName);
|
||||||
await peopleCloudComponent.checkNoResultsFoundError();
|
await peopleCloudComponent.checkNoResultsFoundError();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('[C309677] Should populate the Users without any validation when the Preselect flag is set to false', async () => {
|
it('Multiple roles filtering', async () => {
|
||||||
await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection();
|
await peopleGroupCloudComponentPage.enterPeopleRoles(`["${identityService.ROLES.ACTIVITI_USER}", "${identityService.ROLES.ACTIVITI_ADMIN}"]`);
|
||||||
await peopleGroupCloudComponentPage.checkPeopleCloudMultipleSelectionIsSelected();
|
await peopleCloudComponent.searchAssignee(multipleRolesUser.lastName);
|
||||||
await expect(await peopleGroupCloudComponentPage.getPreselectValidationStatus()).toBe('false');
|
await peopleCloudComponent.checkUserIsDisplayed(`${multipleRolesUser.firstName} ${multipleRolesUser.lastName}`);
|
||||||
|
await peopleCloudComponent.searchAssignee(apsUser.lastName);
|
||||||
await peopleGroupCloudComponentPage.enterPeoplePreselect(
|
await peopleCloudComponent.checkUserIsNotDisplayed(`${apsUser.firstName} ${apsUser.lastName}`);
|
||||||
`[{"id":"TestId1","firstName":"TestFirstName1","lastName":"TestLastName1"},` +
|
await peopleCloudComponent.searchAssignee(testUser.lastName);
|
||||||
`{"id":"TestId2","firstName":"TestFirstName2","lastName":"TestLastName2"},` +
|
await peopleCloudComponent.checkUserIsNotDisplayed(`${testUser.firstName} ${testUser.lastName}`);
|
||||||
`{"id":"TestId3","firstName":"TestFirstName3","lastName":"TestLastName3"}]`);
|
await peopleCloudComponent.searchAssignee(noRoleUser.lastName);
|
||||||
await peopleCloudComponent.checkSelectedPeople('TestFirstName1 TestLastName1');
|
await peopleCloudComponent.checkNoResultsFoundError();
|
||||||
await peopleCloudComponent.checkSelectedPeople('TestFirstName2 TestLastName2');
|
|
||||||
await peopleCloudComponent.checkSelectedPeople('TestFirstName3 TestLastName3');
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 identityService = new IdentityService(apiService);
|
||||||
const groupIdentityService = new GroupIdentityService(apiService);
|
const groupIdentityService = new GroupIdentityService(apiService);
|
||||||
|
|
||||||
let apsUser, testUser;
|
let apsUser;
|
||||||
|
let testUser;
|
||||||
let noRoleUser;
|
let noRoleUser;
|
||||||
let groupNoRole;
|
let groupNoRole;
|
||||||
let users = [];
|
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 () => {
|
it('[C305041] Should filter the People Single Selection with the Application name filter', async () => {
|
||||||
await peopleGroupCloudComponentPage.checkPeopleCloudSingleSelectionIsSelected();
|
await peopleGroupCloudComponentPage.checkPeopleCloudSingleSelectionIsSelected();
|
||||||
await peopleGroupCloudComponentPage.clickPeopleFilerByApp();
|
|
||||||
await peopleGroupCloudComponentPage.enterPeopleAppName(browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name);
|
await peopleGroupCloudComponentPage.enterPeopleAppName(browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name);
|
||||||
|
|
||||||
await peopleCloudComponent.searchAssignee(testUser.firstName);
|
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 () => {
|
it('[C305041] Should filter the People Multiple Selection with the Application name filter', async () => {
|
||||||
await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection();
|
await peopleGroupCloudComponentPage.clickPeopleCloudMultipleSelection();
|
||||||
await peopleGroupCloudComponentPage.clickPeopleFilerByApp();
|
|
||||||
await peopleGroupCloudComponentPage.enterPeopleAppName(browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name);
|
await peopleGroupCloudComponentPage.enterPeopleAppName(browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name);
|
||||||
await peopleCloudComponent.searchAssignee(testUser.firstName);
|
await peopleCloudComponent.searchAssignee(testUser.firstName);
|
||||||
await peopleCloudComponent.checkUserIsDisplayed(`${testUser.firstName} ${testUser.lastName}`);
|
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 () => {
|
it('[C305041] Should filter the Groups Single Selection with the Application name filter', async () => {
|
||||||
await peopleGroupCloudComponentPage.clickGroupCloudSingleSelection();
|
await peopleGroupCloudComponentPage.clickGroupCloudSingleSelection();
|
||||||
await peopleGroupCloudComponentPage.clickGroupFilerByApp();
|
|
||||||
await peopleGroupCloudComponentPage.enterGroupAppName(browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name);
|
await peopleGroupCloudComponentPage.enterGroupAppName(browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name);
|
||||||
await groupCloudComponentPage.searchGroups(hrGroup.name);
|
await groupCloudComponentPage.searchGroups(hrGroup.name);
|
||||||
await groupCloudComponentPage.checkGroupIsDisplayed(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 () => {
|
it('[C305041] Should filter the Groups Multiple Selection with the Application name filter', async () => {
|
||||||
await peopleGroupCloudComponentPage.clickGroupCloudMultipleSelection();
|
await peopleGroupCloudComponentPage.clickGroupCloudMultipleSelection();
|
||||||
await peopleGroupCloudComponentPage.clickGroupFilerByApp();
|
|
||||||
await peopleGroupCloudComponentPage.enterGroupAppName(browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name);
|
await peopleGroupCloudComponentPage.enterGroupAppName(browser.params.resources.ACTIVITI_CLOUD_APPS.SIMPLE_APP.name);
|
||||||
await groupCloudComponentPage.searchGroups(testGroup.name);
|
await groupCloudComponentPage.searchGroups(testGroup.name);
|
||||||
await groupCloudComponentPage.checkGroupIsDisplayed(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 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 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 OAUTH_CLIENT_ID = process.env.OAUTH_CLIENDID || 'alfresco';
|
||||||
|
|
||||||
const IDENTITY_ADMIN_EMAIL = process.env.IDENTITY_ADMIN_EMAIL || "defaultadmin";
|
const IDENTITY_ADMIN_EMAIL = process.env.IDENTITY_ADMIN_EMAIL || "defaultadmin";
|
||||||
@@ -40,7 +41,7 @@ const appConfig = {
|
|||||||
"log": E2E_LOG_LEVEL,
|
"log": E2E_LOG_LEVEL,
|
||||||
"ecmHost": HOST_ECM,
|
"ecmHost": HOST_ECM,
|
||||||
"bpmHost": HOST_BPM,
|
"bpmHost": HOST_BPM,
|
||||||
"identityHost": `${HOST_SSO}/auth/admin/realms/alfresco`,
|
"identityHost": `${IDENTITY_HOST}`,
|
||||||
"provider": PROVIDER,
|
"provider": PROVIDER,
|
||||||
"authType": AUTH_TYPE,
|
"authType": AUTH_TYPE,
|
||||||
"oauth2": {
|
"oauth2": {
|
||||||
|
@@ -83,7 +83,7 @@ describe('WebscriptComponent', () => {
|
|||||||
|
|
||||||
component.ngOnChanges().then(() => {
|
component.ngOnChanges().then(() => {
|
||||||
fixture.detectChanges();
|
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();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -22,15 +22,27 @@ export class FullNamePipe implements PipeTransform {
|
|||||||
transform(user: any): string {
|
transform(user: any): string {
|
||||||
let fullName = '';
|
let fullName = '';
|
||||||
if (user) {
|
if (user) {
|
||||||
if (user.firstName && user.firstName !== 'null') {
|
if (user.displayName) {
|
||||||
fullName += user.firstName;
|
const firstAndLastName = user.displayName.split('\ (.*)');
|
||||||
}
|
if (firstAndLastName[0]) {
|
||||||
if (user.lastName && user.lastName !== 'null') {
|
fullName += firstAndLastName[0];
|
||||||
fullName += fullName.length > 0 ? ' ' : '';
|
}
|
||||||
fullName += user.lastName;
|
if (firstAndLastName[1]) {
|
||||||
}
|
fullName += fullName.length > 0 ? ' ' : '';
|
||||||
if (!fullName) {
|
fullName += firstAndLastName[1];
|
||||||
fullName += user.username ? user.username : user.email ? user.email : '';
|
}
|
||||||
|
|
||||||
|
} 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;
|
return fullName;
|
||||||
|
@@ -131,12 +131,12 @@ describe('UserAccessService', () => {
|
|||||||
expect(getAccessFromApiSpy.calls.count()).toEqual(1);
|
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';
|
const fakeIdentityHost = 'https://fake-identity-host.fake.com';
|
||||||
appConfigService.config.identityHost = fakeIdentityHost;
|
appConfigService.config.bpmHost = fakeIdentityHost;
|
||||||
await userAccessService.fetchUserAccess();
|
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 () => {
|
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 { AppConfigService } from '../app-config/app-config.service';
|
||||||
import { OAuth2Service } from './oauth2.service';
|
import { OAuth2Service } from './oauth2.service';
|
||||||
|
|
||||||
|
const IDENTITY_MICRO_SERVICE_INGRESS = 'modeling-service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
@@ -50,7 +52,7 @@ export class UserAccessService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async fetchAccessFromApi() {
|
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 })
|
await this.oAuth2Service.get({ url })
|
||||||
.toPromise()
|
.toPromise()
|
||||||
.then((response: UserAccessModel) => {
|
.then((response: UserAccessModel) => {
|
||||||
@@ -68,7 +70,7 @@ export class UserAccessService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private get identityHost(): string {
|
private get identityHost(): string {
|
||||||
return `${this.appConfigService.get('identityHost')}`;
|
return `${this.appConfigService.get('bpmHost')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private isOauth(): boolean {
|
private isOauth(): boolean {
|
||||||
|
@@ -65,13 +65,13 @@ describe('GroupCloudWidgetComponent', () => {
|
|||||||
expect(asterisk.textContent).toEqual('*');
|
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();
|
fixture.detectChanges();
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
|
|
||||||
expect(element.querySelector('.adf-invalid')).toBeFalsy();
|
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'));
|
cloudGroupInput.dispatchEvent(new Event('blur'));
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@@ -16,11 +16,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
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 { FormControl } from '@angular/forms';
|
||||||
import { filter, takeUntil } from 'rxjs/operators';
|
import { filter, takeUntil } from 'rxjs/operators';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { ComponentSelectionMode } from '../../../../types';
|
import { ComponentSelectionMode } from '../../../../types';
|
||||||
|
import { IdentityGroupModel } from '../../../../group/models/identity-group.model';
|
||||||
|
|
||||||
/* eslint-disable @angular-eslint/component-selector */
|
/* eslint-disable @angular-eslint/component-selector */
|
||||||
|
|
||||||
|
@@ -15,20 +15,20 @@
|
|||||||
* limitations under the License.
|
* 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 { TranslateModule } from '@ngx-translate/core';
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
import { PeopleCloudWidgetComponent } from './people-cloud.widget';
|
import { PeopleCloudWidgetComponent } from './people-cloud.widget';
|
||||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||||
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
|
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', () => {
|
describe('PeopleCloudWidgetComponent', () => {
|
||||||
let fixture: ComponentFixture<PeopleCloudWidgetComponent>;
|
let fixture: ComponentFixture<PeopleCloudWidgetComponent>;
|
||||||
let widget: PeopleCloudWidgetComponent;
|
let widget: PeopleCloudWidgetComponent;
|
||||||
let element: HTMLElement;
|
let element: HTMLElement;
|
||||||
let identityUserService: IdentityUserService;
|
let identityUserService: IdentityUserService;
|
||||||
const currentUser = { id: 'id', username: 'user' };
|
|
||||||
const fakeUser = { id: 'fake-id', username: 'fake' };
|
|
||||||
|
|
||||||
setupTestBed({
|
setupTestBed({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -48,20 +48,20 @@ describe('PeopleCloudWidgetComponent', () => {
|
|||||||
fixture = TestBed.createComponent(PeopleCloudWidgetComponent);
|
fixture = TestBed.createComponent(PeopleCloudWidgetComponent);
|
||||||
widget = fixture.componentInstance;
|
widget = fixture.componentInstance;
|
||||||
element = fixture.nativeElement;
|
element = fixture.nativeElement;
|
||||||
spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(fakeUser);
|
spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(mockShepherdsPie);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should preselect the current user', () => {
|
it('should preselect the current user', () => {
|
||||||
widget.field = new FormFieldModel(new FormModel(), { value: null, selectLoggedUser: true });
|
widget.field = new FormFieldModel(new FormModel(), { value: null, selectLoggedUser: true });
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(widget.preSelectUsers).toEqual([fakeUser]);
|
expect(widget.preSelectUsers).toEqual([mockShepherdsPie]);
|
||||||
expect(identityUserService.getCurrentUserInfo).toHaveBeenCalled();
|
expect(identityUserService.getCurrentUserInfo).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not preselect the current user if value exist', () => {
|
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();
|
fixture.detectChanges();
|
||||||
expect(widget.preSelectUsers).toEqual([currentUser]);
|
expect(widget.preSelectUsers).toEqual([mockYorkshirePudding]);
|
||||||
expect(identityUserService.getCurrentUserInfo).not.toHaveBeenCalled();
|
expect(identityUserService.getCurrentUserInfo).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -84,13 +84,13 @@ describe('PeopleCloudWidgetComponent', () => {
|
|||||||
expect(asterisk.textContent).toEqual('*');
|
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();
|
fixture.detectChanges();
|
||||||
await fixture.whenStable();
|
await fixture.whenStable();
|
||||||
|
|
||||||
expect(element.querySelector('.adf-invalid')).toBeFalsy();
|
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'));
|
cloudPeopleInput.dispatchEvent(new Event('blur'));
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
@@ -16,11 +16,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Component, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core';
|
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 { FormControl } from '@angular/forms';
|
||||||
import { filter, takeUntil } from 'rxjs/operators';
|
import { filter, takeUntil } from 'rxjs/operators';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { ComponentSelectionMode } from '../../../../types';
|
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 */
|
/* eslint-disable @angular-eslint/component-selector */
|
||||||
|
|
||||||
|
@@ -24,35 +24,54 @@ import { GroupCloudModule } from '../group-cloud.module';
|
|||||||
import { GroupCloudComponent } from './group-cloud.component';
|
import { GroupCloudComponent } from './group-cloud.component';
|
||||||
import {
|
import {
|
||||||
setupTestBed,
|
setupTestBed,
|
||||||
IdentityGroupService,
|
|
||||||
mockIdentityGroups,
|
|
||||||
AlfrescoApiService,
|
|
||||||
CoreTestingModule
|
CoreTestingModule
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { SimpleChange } from '@angular/core';
|
import { SimpleChange } from '@angular/core';
|
||||||
import { TranslateModule } from '@ngx-translate/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', () => {
|
describe('GroupCloudComponent', () => {
|
||||||
let component: GroupCloudComponent;
|
let component: GroupCloudComponent;
|
||||||
let fixture: ComponentFixture<GroupCloudComponent>;
|
let fixture: ComponentFixture<GroupCloudComponent>;
|
||||||
let element: HTMLElement;
|
let element: HTMLElement;
|
||||||
let identityGroupService: IdentityGroupService;
|
let identityGroupService: IdentityGroupService;
|
||||||
let alfrescoApiService: AlfrescoApiService;
|
|
||||||
let findGroupsByNameSpy: jasmine.Spy;
|
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
|
// eslint-disable-next-line prefer-arrow/prefer-arrow-functions
|
||||||
function getElement<T = HTMLElement>(selector: string): T {
|
function getElement<T = HTMLElement>(selector: string): T {
|
||||||
return fixture.nativeElement.querySelector(selector);
|
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({
|
setupTestBed({
|
||||||
imports: [
|
imports: [
|
||||||
TranslateModule.forRoot(),
|
TranslateModule.forRoot(),
|
||||||
@@ -68,16 +87,12 @@ describe('GroupCloudComponent', () => {
|
|||||||
element = fixture.nativeElement;
|
element = fixture.nativeElement;
|
||||||
|
|
||||||
identityGroupService = TestBed.inject(IdentityGroupService);
|
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';
|
component.title = 'TITLE_KEY';
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
await fixture.whenStable();
|
|
||||||
|
|
||||||
const matLabel = element.querySelector<HTMLInputElement>('#adf-group-cloud-title-id');
|
const matLabel = element.querySelector<HTMLInputElement>('#adf-group-cloud-title-id');
|
||||||
expect(matLabel.textContent).toEqual('TITLE_KEY');
|
expect(matLabel.textContent).toEqual('TITLE_KEY');
|
||||||
@@ -87,330 +102,88 @@ describe('GroupCloudComponent', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture.detectChanges();
|
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) => {
|
it('should list the groups as dropdown options if the search term has results', async () => {
|
||||||
const input = getElement<HTMLInputElement>('input');
|
await searchGroup('All');
|
||||||
input.focus();
|
|
||||||
input.value = 'Mock';
|
|
||||||
input.dispatchEvent(new Event('keyup'));
|
|
||||||
input.dispatchEvent(new Event('input'));
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
const groupList = getGroupListUI();
|
||||||
fixture.whenStable().then(() => {
|
expect(groupList.length).toEqual(2);
|
||||||
fixture.detectChanges();
|
|
||||||
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(5);
|
|
||||||
expect(findGroupsByNameSpy).toHaveBeenCalled();
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not be able to search for a group that its name matches one of the preselected groups name', (done) => {
|
it('should not be able to search for a group that its name matches one of the preselected groups name', async () => {
|
||||||
component.preSelectGroups = [{ name: mockIdentityGroups[0].name }];
|
component.preSelectGroups = [{ name: mockVegetableAubergine.name }];
|
||||||
const changes = new SimpleChange(null, [{ name: mockIdentityGroups[0].name }], false);
|
const changes = new SimpleChange(null, [{ name: mockVegetableAubergine.name }], false);
|
||||||
component.ngOnChanges({ preSelectGroups: changes });
|
component.ngOnChanges({ preSelectGroups: changes });
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const inputHTMLElement = element.querySelector<HTMLInputElement>('input');
|
await searchGroup('Aubergine');
|
||||||
inputHTMLElement.focus();
|
|
||||||
inputHTMLElement.value = 'mock-group';
|
|
||||||
inputHTMLElement.dispatchEvent(new Event('keyup'));
|
|
||||||
inputHTMLElement.dispatchEvent(new Event('input'));
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
fixture.whenStable().then(() => {
|
const groupList = getGroupListUI();
|
||||||
fixture.detectChanges();
|
expect(groupList.length).toEqual(1);
|
||||||
expect(fixture.debugElement.queryAll(By.css('[data-automation-id="adf-cloud-group-row"]')).length).toEqual(4);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide result list if input is empty', (done) => {
|
it('should hide result list if input is empty', async () => {
|
||||||
fixture.detectChanges();
|
await searchGroup('');
|
||||||
|
|
||||||
const input = getElement<HTMLInputElement>('input');
|
expect(element.querySelector('[data-automation-id="adf-cloud-group-row"]')).toBeNull();
|
||||||
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();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update selected groups when a group is selected', (done) => {
|
it('should update selected groups when a group is selected', async () => {
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
const selectEmitSpy = spyOn(component.selectGroup, 'emit');
|
const selectEmitSpy = spyOn(component.selectGroup, 'emit');
|
||||||
const changedGroupsSpy = spyOn(component.changedGroups, 'emit');
|
const changedGroupsSpy = spyOn(component.changedGroups, 'emit');
|
||||||
|
|
||||||
const group = { name: 'groupname' };
|
component.onSelect(mockMeatChicken);
|
||||||
component.onSelect(group);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
await fixture.whenStable();
|
||||||
expect(selectEmitSpy).toHaveBeenCalledWith(group);
|
|
||||||
expect(changedGroupsSpy).toHaveBeenCalledWith([group]);
|
expect(selectEmitSpy).toHaveBeenCalledWith(mockMeatChicken);
|
||||||
expect(component.selectedGroups).toEqual([group]);
|
expect(changedGroupsSpy).toHaveBeenCalledWith([mockMeatChicken]);
|
||||||
done();
|
expect(component.selectedGroups).toEqual([mockMeatChicken]);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should replace the group in single-selection mode', () => {
|
it('should replace the group in single-selection mode', () => {
|
||||||
component.mode = 'single';
|
component.mode = 'single';
|
||||||
|
|
||||||
const group1 = { name: 'group1' };
|
component.onSelect(mockVegetableAubergine);
|
||||||
const group2 = { name: 'group2' };
|
expect(component.selectedGroups).toEqual([mockVegetableAubergine]);
|
||||||
|
|
||||||
component.onSelect(group1);
|
component.onSelect(mockMeatChicken);
|
||||||
expect(component.selectedGroups).toEqual([group1]);
|
expect(component.selectedGroups).toEqual([mockMeatChicken]);
|
||||||
|
|
||||||
component.onSelect(group2);
|
|
||||||
expect(component.selectedGroups).toEqual([group2]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow multiple groups in multi-selection mode', () => {
|
it('should allow multiple groups in multi-selection mode', () => {
|
||||||
component.mode = 'multiple';
|
component.mode = 'multiple';
|
||||||
|
|
||||||
const group1 = { name: 'group1' };
|
component.onSelect(mockVegetableAubergine);
|
||||||
const group2 = { name: 'group2' };
|
component.onSelect(mockMeatChicken);
|
||||||
|
|
||||||
component.onSelect(group1);
|
expect(component.selectedGroups).toEqual([mockVegetableAubergine, mockMeatChicken]);
|
||||||
component.onSelect(group2);
|
|
||||||
|
|
||||||
expect(component.selectedGroups).toEqual([group1, group2]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow only unique groups in multi-selection mode', () => {
|
it('should allow only unique groups in multi-selection mode', () => {
|
||||||
component.mode = 'multiple';
|
component.mode = 'multiple';
|
||||||
|
|
||||||
const group1 = { name: 'group1' };
|
component.onSelect(mockVegetableAubergine);
|
||||||
const group2 = { name: 'group2' };
|
component.onSelect(mockMeatChicken);
|
||||||
|
component.onSelect(mockMeatChicken);
|
||||||
|
component.onSelect(mockVegetableAubergine);
|
||||||
|
|
||||||
component.onSelect(group1);
|
expect(component.selectedGroups).toEqual([mockVegetableAubergine, mockMeatChicken]);
|
||||||
component.onSelect(group2);
|
|
||||||
component.onSelect(group1);
|
|
||||||
component.onSelect(group2);
|
|
||||||
|
|
||||||
expect(component.selectedGroups).toEqual([group1, group2]);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
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([]));
|
findGroupsByNameSpy.and.returnValue(of([]));
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
const input = getElement<HTMLInputElement>('input');
|
await searchGroupsAndBlur('INCORRECTVALUE');
|
||||||
input.focus();
|
|
||||||
input.value = 'ZZZ';
|
|
||||||
input.dispatchEvent(new Event('keyup'));
|
|
||||||
input.dispatchEvent(new Event('input'));
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
const errorMessage = element.querySelector('[data-automation-id="invalid-groups-typing-error"]');
|
||||||
fixture.whenStable().then(() => {
|
expect(errorMessage).not.toBeNull();
|
||||||
input.blur();
|
expect(errorMessage.textContent).toContain('ADF_CLOUD_GROUPS.ERROR.NOT_FOUND');
|
||||||
fixture.detectChanges();
|
const errorIcon = element.querySelector('.adf-error-icon').textContent;
|
||||||
const errorMessage = element.querySelector('[data-automation-id="invalid-groups-typing-error"]');
|
expect(errorIcon).toEqual('error_outline');
|
||||||
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();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -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', () => {
|
describe('Single Mode with pre-selected groups', () => {
|
||||||
const changes = new SimpleChange(null, mockIdentityGroups, false);
|
const changes = new SimpleChange(null, mockFoodGroups, false);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
component.mode = 'single';
|
component.mode = 'single';
|
||||||
component.preSelectGroups = mockIdentityGroups;
|
component.preSelectGroups = mockFoodGroups;
|
||||||
component.ngOnChanges({ preSelectGroups: changes });
|
component.ngOnChanges({ preSelectGroups: changes });
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -513,16 +222,16 @@ describe('GroupCloudComponent', () => {
|
|||||||
it('should show only one mat chip with the first preSelectedGroup', () => {
|
it('should show only one mat chip with the first preSelectedGroup', () => {
|
||||||
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
|
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
|
||||||
expect(chips.length).toEqual(1);
|
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', () => {
|
describe('Multiple Mode with pre-selected groups', () => {
|
||||||
const change = new SimpleChange(null, mockIdentityGroups, false);
|
const change = new SimpleChange(null, mockFoodGroups, false);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
component.mode = 'multiple';
|
component.mode = 'multiple';
|
||||||
component.preSelectGroups = mockIdentityGroups;
|
component.preSelectGroups = mockFoodGroups;
|
||||||
component.ngOnChanges({ preSelectGroups: change });
|
component.ngOnChanges({ preSelectGroups: change });
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
@@ -533,196 +242,145 @@ describe('GroupCloudComponent', () => {
|
|||||||
component.ngOnChanges({ preSelectGroups: change });
|
component.ngOnChanges({ preSelectGroups: change });
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
|
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 removeGroupEmitterSpy = spyOn(component.removeGroup, 'emit');
|
||||||
const changedGroupsEmitterSpy = spyOn(component.changedGroups, 'emit');
|
const changedGroupsEmitterSpy = spyOn(component.changedGroups, 'emit');
|
||||||
const groupToRemove = mockIdentityGroups[0];
|
|
||||||
component.mode = 'multiple';
|
component.mode = 'multiple';
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
const removeIcon = fixture.debugElement.query(By.css('mat-chip mat-icon'));
|
const removeIcon = fixture.debugElement.query(By.css('mat-chip mat-icon'));
|
||||||
removeIcon.nativeElement.click();
|
removeIcon.nativeElement.click();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
fixture.whenStable().then(() => {
|
await fixture.whenStable();
|
||||||
expect(removeGroupEmitterSpy).toHaveBeenCalledWith(groupToRemove);
|
expect(removeGroupEmitterSpy).toHaveBeenCalledWith(mockVegetableAubergine);
|
||||||
expect(changedGroupsEmitterSpy).toHaveBeenCalledWith([mockIdentityGroups[1], mockIdentityGroups[2], mockIdentityGroups[3], mockIdentityGroups[4]]);
|
expect(changedGroupsEmitterSpy).toHaveBeenCalledWith([mockMeatChicken]);
|
||||||
expect(component.selectedGroups.indexOf({
|
expect(component.selectedGroups.indexOf({
|
||||||
id: groupToRemove.id,
|
id: mockMeatChicken.id,
|
||||||
name: groupToRemove.name,
|
name: mockMeatChicken.name
|
||||||
path: groupToRemove.path
|
})).toEqual(-1);
|
||||||
})).toEqual(-1);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Multiple Mode with read-only', () => {
|
describe('Multiple Mode with read-only', () => {
|
||||||
|
|
||||||
it('Should not show remove icon for pre-selected groups if readonly property set to true', (done) => {
|
it('Should not show remove icon for pre-selected groups if readonly property set to true', async () => {
|
||||||
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);
|
|
||||||
component.mode = 'multiple';
|
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.detectChanges();
|
||||||
fixture.whenStable().then(() => {
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
const chipList = fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip');
|
await fixture.whenStable();
|
||||||
const removeIcon = getElement('[data-automation-id="adf-cloud-group-chip-remove-icon-Mock Group 1"]');
|
|
||||||
|
|
||||||
expect(chipList.length).toBe(2);
|
const chipList = fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip');
|
||||||
expect(component.preSelectGroups[0].readonly).toBeTruthy();
|
|
||||||
expect(component.preSelectGroups[1].readonly).toBeTruthy();
|
expect(chipList.length).toBe(2);
|
||||||
expect(removeIcon).toBeNull();
|
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) => {
|
it('Should be able to remove preselected groups if readonly property set to false', async () => {
|
||||||
fixture.detectChanges();
|
component.mode = 'multiple';
|
||||||
|
component.preSelectGroups = mockFoodGroups;
|
||||||
component.preSelectGroups = [
|
|
||||||
{ id: mockIdentityGroups[0].id, name: mockIdentityGroups[0].name, readonly: false },
|
|
||||||
{ id: mockIdentityGroups[1].id, name: mockIdentityGroups[1].name, readonly: false }
|
|
||||||
];
|
|
||||||
|
|
||||||
const change = new SimpleChange(null, component.preSelectGroups, false);
|
const change = new SimpleChange(null, component.preSelectGroups, false);
|
||||||
component.mode = 'multiple';
|
|
||||||
component.ngOnChanges({ preSelectGroups: change });
|
component.ngOnChanges({ preSelectGroups: change });
|
||||||
|
|
||||||
const removeGroupSpy = spyOn(component.removeGroup, 'emit');
|
const removeGroupSpy = spyOn(component.removeGroup, 'emit');
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
fixture.whenStable().then(() => {
|
fixture.whenStable();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
const chips = fixture.debugElement.queryAll(By.css('mat-chip'));
|
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"]');
|
expect(chipList.length).toBe(2);
|
||||||
|
|
||||||
expect(chips.length).toBe(2);
|
const removeIcon = getElement(`[data-automation-id="adf-cloud-group-chip-remove-icon-${mockMeatChicken.name}"]`);
|
||||||
expect(component.preSelectGroups[0].readonly).toBe(false, 'Removable');
|
removeIcon.click();
|
||||||
expect(component.preSelectGroups[1].readonly).toBe(false, 'Removable');
|
fixture.detectChanges();
|
||||||
|
|
||||||
removeIcon.click();
|
expect(removeGroupSpy).toHaveBeenCalled();
|
||||||
fixture.detectChanges();
|
expect(fixture.nativeElement.querySelectorAll('mat-chip-list mat-chip').length).toBe(1);
|
||||||
|
|
||||||
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)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should removeDuplicatedGroups return only unique groups', () => {
|
it('should removeDuplicatedGroups return only unique groups', () => {
|
||||||
const duplicatedGroups = [{ name: mockIdentityGroups[0].name }, { name: mockIdentityGroups[0].name }];
|
const duplicatedGroups = [ mockMeatChicken, mockMeatChicken];
|
||||||
expect(component.removeDuplicatedGroups(duplicatedGroups)).toEqual([{ name: mockIdentityGroups[0].name }]);
|
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 { Meta, moduleMetadata, Story } from '@storybook/angular';
|
||||||
import { IdentityGroupService, mockIdentityGroups, IdentityGroupServiceMock } from '@alfresco/adf-core';
|
|
||||||
import { GroupCloudModule } from '../group-cloud.module';
|
import { GroupCloudModule } from '../group-cloud.module';
|
||||||
import { GroupCloudComponent } from './group-cloud.component';
|
import { GroupCloudComponent } from './group-cloud.component';
|
||||||
import { ProcessServicesCloudStoryModule } from '../../testing/process-services-cloud-story.module';
|
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 {
|
export default {
|
||||||
component: GroupCloudComponent,
|
component: GroupCloudComponent,
|
||||||
@@ -37,15 +43,6 @@ export default {
|
|||||||
mode: {
|
mode: {
|
||||||
options: ['single', 'multiple'],
|
options: ['single', 'multiple'],
|
||||||
control: 'radio'
|
control: 'radio'
|
||||||
},
|
|
||||||
roles: {
|
|
||||||
options: ['empty', 'user', 'admin'],
|
|
||||||
control: 'radio',
|
|
||||||
mapping: {
|
|
||||||
empty: [],
|
|
||||||
user: ['MOCK-USER-ROLE'],
|
|
||||||
admin: ['MOCK-ADMIN-ROLE']
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} as Meta;
|
} as Meta;
|
||||||
@@ -60,7 +57,6 @@ primary.args = {
|
|||||||
mode: 'single',
|
mode: 'single',
|
||||||
preSelectGroups: [],
|
preSelectGroups: [],
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
roles: [],
|
|
||||||
title: 'Groups',
|
title: 'Groups',
|
||||||
validate: false
|
validate: false
|
||||||
};
|
};
|
||||||
@@ -70,7 +66,7 @@ validPreselectedGroups.args = {
|
|||||||
...primary.args,
|
...primary.args,
|
||||||
validate: true,
|
validate: true,
|
||||||
mode: 'multiple',
|
mode: 'multiple',
|
||||||
preSelectGroups: mockIdentityGroups
|
preSelectGroups: mockFoodGroups
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mandatoryPreselectedGroups = template.bind({});
|
export const mandatoryPreselectedGroups = template.bind({});
|
||||||
@@ -78,9 +74,7 @@ mandatoryPreselectedGroups.args = {
|
|||||||
...primary.args,
|
...primary.args,
|
||||||
validate: true,
|
validate: true,
|
||||||
mode: 'multiple',
|
mode: 'multiple',
|
||||||
preSelectGroups: [{ id: 'mock-group-id-1', name: 'Mock Group 1', path: '/mock', subGroups: [], readonly: true },
|
preSelectGroups: [mockVegetableAubergine, { ...mockMeatChicken, 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 }]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const invalidPreselectedGroups = template.bind({});
|
export const invalidPreselectedGroups = template.bind({});
|
||||||
@@ -88,13 +82,7 @@ invalidPreselectedGroups.args = {
|
|||||||
...primary.args,
|
...primary.args,
|
||||||
validate: true,
|
validate: true,
|
||||||
mode: 'multiple',
|
mode: 'multiple',
|
||||||
preSelectGroups: [{ id: 'invalid-group', name: 'invalid groups' }]
|
preSelectGroups: [{ id: 'invalid-group', name: 'Invalid Group' }]
|
||||||
};
|
|
||||||
|
|
||||||
export const adminRoleGroups = template.bind({});
|
|
||||||
adminRoleGroups.args = {
|
|
||||||
...primary.args,
|
|
||||||
roles: 'admin'
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const invalidOrEmptyAppName = template.bind({});
|
export const invalidOrEmptyAppName = template.bind({});
|
||||||
|
@@ -27,14 +27,17 @@ import {
|
|||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
SimpleChange
|
Inject
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from '@angular/forms';
|
||||||
import { trigger, state, style, transition, animate } from '@angular/animations';
|
import { trigger, state, style, transition, animate } from '@angular/animations';
|
||||||
import { Observable, of, BehaviorSubject, Subject } from 'rxjs';
|
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||||
import { distinctUntilChanged, switchMap, mergeMap, filter, tap, map, takeUntil, debounceTime } from 'rxjs/operators';
|
import { distinctUntilChanged, switchMap, mergeMap, filter, tap, takeUntil, debounceTime } from 'rxjs/operators';
|
||||||
import { IdentityGroupModel, IdentityGroupService, LogService } from '@alfresco/adf-core';
|
import { LogService } from '@alfresco/adf-core';
|
||||||
import { ComponentSelectionMode } from '../../types';
|
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({
|
@Component({
|
||||||
selector: 'adf-cloud-group',
|
selector: 'adf-cloud-group',
|
||||||
@@ -125,7 +128,6 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
|
|
||||||
searchGroups$ = new BehaviorSubject<IdentityGroupModel[]>(this.searchGroups);
|
searchGroups$ = new BehaviorSubject<IdentityGroupModel[]>(this.searchGroups);
|
||||||
subscriptAnimationState: string = 'enter';
|
subscriptAnimationState: string = 'enter';
|
||||||
clientId: string;
|
|
||||||
isFocused: boolean;
|
isFocused: boolean;
|
||||||
touched: boolean = false;
|
touched: boolean = false;
|
||||||
|
|
||||||
@@ -135,12 +137,14 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
validationLoading = false;
|
validationLoading = false;
|
||||||
searchLoading = false;
|
searchLoading = false;
|
||||||
|
|
||||||
|
typingUniqueValueNotEmpty$: Observable<any>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private identityGroupService: IdentityGroupService,
|
@Inject(IDENTITY_GROUP_SERVICE_TOKEN)
|
||||||
|
private identityGroupService: IdentityGroupServiceInterface,
|
||||||
private logService: LogService) {}
|
private logService: LogService) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadClientId();
|
|
||||||
this.initSearch();
|
this.initSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,41 +161,52 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.invalidGroups = [];
|
this.invalidGroups = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changes.appName && this.isAppNameChanged(changes.appName)) {
|
|
||||||
this.loadClientId();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private isAppNameChanged(change: SimpleChange): boolean {
|
private initSearch(): void {
|
||||||
return change
|
this.initializeStream();
|
||||||
&& change.previousValue !== change.currentValue
|
this.typingUniqueValueNotEmpty$.pipe(
|
||||||
&& this.appName
|
switchMap((name: string) =>
|
||||||
&& this.appName.length > 0;
|
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> {
|
private initializeStream() {
|
||||||
this.clientId = await this.identityGroupService.getClientIdByApplicationName(this.appName).toPromise();
|
const typingValueFromControl$ = this.searchGroupsControl.valueChanges;
|
||||||
|
|
||||||
if (this.clientId) {
|
const typingValueTypeSting$ = typingValueFromControl$.pipe(
|
||||||
this.searchGroupsControl.enable();
|
filter(value => {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initSearch(): void {
|
|
||||||
this.searchGroupsControl.valueChanges.pipe(
|
|
||||||
filter((value) => {
|
|
||||||
this.searchLoading = true;
|
this.searchLoading = true;
|
||||||
return typeof value === 'string';
|
return typeof value === 'string';
|
||||||
}),
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const typingValueHandleErrorMessage$ = typingValueTypeSting$.pipe(
|
||||||
tap((value: string) => {
|
tap((value: string) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
this.setTypingError();
|
this.setTypingError();
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const typingValueDebouncedUnique$ = typingValueHandleErrorMessage$.pipe(
|
||||||
debounceTime(500),
|
debounceTime(500),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged()
|
||||||
tap((value) => {
|
);
|
||||||
|
|
||||||
|
this.typingUniqueValueNotEmpty$ = typingValueDebouncedUnique$.pipe(
|
||||||
|
tap((value: string) => {
|
||||||
if (value.trim()) {
|
if (value.trim()) {
|
||||||
this.searchedValue = value;
|
this.searchedValue = value;
|
||||||
} else {
|
} else {
|
||||||
@@ -199,42 +214,8 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.searchGroupsControl.markAsUntouched();
|
this.searchGroupsControl.markAsUntouched();
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
tap(() => this.resetSearchGroups()),
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private isGroupAlreadySelected(group: IdentityGroupModel): boolean {
|
private isGroupAlreadySelected(group: IdentityGroupModel): boolean {
|
||||||
@@ -246,8 +227,8 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchGroup(name: string): Promise<IdentityGroupModel> {
|
private async searchGroup(name: string): Promise<IdentityGroupModel> {
|
||||||
return (await this.identityGroupService.findGroupsByName({ name }).toPromise())[0];
|
return (await this.identityGroupService.search(name).toPromise())[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPreselectedGroups(): IdentityGroupModel[] {
|
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 = [];
|
this.invalidGroups = [];
|
||||||
|
|
||||||
for (const group of this.getPreselectedGroups()) {
|
for (const group of this.getPreselectedGroups()) {
|
||||||
@@ -276,7 +257,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.checkPreselectValidationErrors();
|
this.checkPreselectValidationErrors();
|
||||||
}
|
}
|
||||||
|
|
||||||
checkPreselectValidationErrors(): void {
|
private checkPreselectValidationErrors(): void {
|
||||||
this.invalidGroups = this.removeDuplicatedGroups(this.invalidGroups);
|
this.invalidGroups = this.removeDuplicatedGroups(this.invalidGroups);
|
||||||
|
|
||||||
if (this.invalidGroups.length > 0) {
|
if (this.invalidGroups.length > 0) {
|
||||||
@@ -289,7 +270,7 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
generateInvalidGroupsMessage(): void {
|
private generateInvalidGroupsMessage(): void {
|
||||||
this.validateGroupsMessage = '';
|
this.validateGroupsMessage = '';
|
||||||
|
|
||||||
this.invalidGroups.forEach((invalidGroup: IdentityGroupModel, index) => {
|
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 {
|
onSelect(group: IdentityGroupModel): void {
|
||||||
if (group) {
|
if (group) {
|
||||||
this.selectGroup.emit(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) {
|
private groupChipsCtrlValue(value: string) {
|
||||||
this.groupChipsCtrl.setValue(value);
|
this.groupChipsCtrl.setValue(value);
|
||||||
this.groupChipsCtrl.markAsDirty();
|
this.groupChipsCtrl.markAsDirty();
|
||||||
@@ -392,43 +379,18 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.searchGroups$.next(this.searchGroups);
|
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 {
|
private isSingleSelectionReadonly(): boolean {
|
||||||
return this.isSingleMode() && this.selectedGroups.length === 1 && this.selectedGroups[0].readonly === true;
|
return this.isSingleMode() && this.selectedGroups.length === 1 && this.selectedGroups[0].readonly === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasPreselectError(): boolean {
|
private isSingleMode(): boolean {
|
||||||
return this.invalidGroups && this.invalidGroups.length > 0;
|
return this.mode === 'single';
|
||||||
}
|
}
|
||||||
|
|
||||||
isReadonly(): boolean {
|
private isMultipleMode(): boolean {
|
||||||
return this.readOnly || this.isSingleSelectionReadonly();
|
|
||||||
}
|
|
||||||
|
|
||||||
isMultipleMode(): boolean {
|
|
||||||
return this.mode === 'multiple';
|
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 {
|
private hasPreSelectGroups(): boolean {
|
||||||
return this.preSelectGroups && this.preSelectGroups.length > 0;
|
return this.preSelectGroups && this.preSelectGroups.length > 0;
|
||||||
}
|
}
|
||||||
@@ -457,10 +419,6 @@ export class GroupCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
&& changes.preSelectGroups.currentValue.length === 0;
|
&& changes.preSelectGroups.currentValue.length === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private hasRoles(): boolean {
|
|
||||||
return this.roles && this.roles.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private setTypingError(): void {
|
private setTypingError(): void {
|
||||||
this.searchGroupsControl.setErrors({
|
this.searchGroupsControl.setErrors({
|
||||||
searchTypingError: true,
|
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 {
|
hasError(): boolean {
|
||||||
return !!this.searchGroupsControl.errors;
|
return !!this.searchGroupsControl.errors;
|
||||||
}
|
}
|
||||||
|
@@ -24,6 +24,8 @@ import { CoreModule } from '@alfresco/adf-core';
|
|||||||
import { MaterialModule } from '../material.module';
|
import { MaterialModule } from '../material.module';
|
||||||
import { GroupCloudComponent } from './components/group-cloud.component';
|
import { GroupCloudComponent } from './components/group-cloud.component';
|
||||||
import { InitialGroupNamePipe } from './pipe/group-initial.pipe';
|
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({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -35,6 +37,9 @@ import { InitialGroupNamePipe } from './pipe/group-initial.pipe';
|
|||||||
CoreModule
|
CoreModule
|
||||||
],
|
],
|
||||||
declarations: [GroupCloudComponent, InitialGroupNamePipe],
|
declarations: [GroupCloudComponent, InitialGroupNamePipe],
|
||||||
|
providers: [
|
||||||
|
{ provide: IDENTITY_GROUP_SERVICE_TOKEN, useExisting: IdentityGroupService }
|
||||||
|
],
|
||||||
exports: [GroupCloudComponent, InitialGroupNamePipe]
|
exports: [GroupCloudComponent, InitialGroupNamePipe]
|
||||||
})
|
})
|
||||||
export class GroupCloudModule { }
|
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.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { IdentityGroupModel } from '../models/identity-group.model';
|
||||||
import { InitialGroupNamePipe } from './group-initial.pipe';
|
import { InitialGroupNamePipe } from './group-initial.pipe';
|
||||||
import { IdentityGroupModel } from '@alfresco/adf-core';
|
|
||||||
|
|
||||||
describe('InitialGroupNamePipe', () => {
|
describe('InitialGroupNamePipe', () => {
|
||||||
|
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Pipe, PipeTransform } from '@angular/core';
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
import { IdentityGroupModel } from '@alfresco/adf-core';
|
import { IdentityGroupModel } from '../models/identity-group.model';
|
||||||
|
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'groupNameInitial'
|
name: 'groupNameInitial'
|
||||||
|
@@ -17,4 +17,7 @@
|
|||||||
|
|
||||||
export * from './components/group-cloud.component';
|
export * from './components/group-cloud.component';
|
||||||
export * from './pipe/group-initial.pipe';
|
export * from './pipe/group-initial.pipe';
|
||||||
|
export * from './models/identity-group.model';
|
||||||
export * from './group-cloud.module';
|
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 { 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';
|
import { ProcessInstanceVariable } from './process-instance-variable.model';
|
||||||
|
|
||||||
export class TaskCloudNodePaging {
|
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 { Meta, moduleMetadata, Story } from '@storybook/angular';
|
||||||
import { IdentityUserService, IdentityUserServiceMock, mockIdentityUsers } from '@alfresco/adf-core';
|
|
||||||
import { PeopleCloudComponent } from './people-cloud.component';
|
import { PeopleCloudComponent } from './people-cloud.component';
|
||||||
import { PeopleCloudModule } from '../people-cloud.module';
|
import { PeopleCloudModule } from '../people-cloud.module';
|
||||||
import { ProcessServicesCloudStoryModule } from '../../testing/process-services-cloud-story.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 {
|
export default {
|
||||||
component: PeopleCloudComponent,
|
component: PeopleCloudComponent,
|
||||||
@@ -28,7 +35,7 @@ export default {
|
|||||||
moduleMetadata({
|
moduleMetadata({
|
||||||
imports: [ProcessServicesCloudStoryModule, PeopleCloudModule],
|
imports: [ProcessServicesCloudStoryModule, PeopleCloudModule],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: IdentityUserService, useClass: IdentityUserServiceMock }
|
{ provide: IdentityUserService, useClass: IdentityUserServiceMock}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
@@ -37,15 +44,6 @@ export default {
|
|||||||
mode: {
|
mode: {
|
||||||
options: ['single', 'multiple'],
|
options: ['single', 'multiple'],
|
||||||
control: 'radio'
|
control: 'radio'
|
||||||
},
|
|
||||||
roles: {
|
|
||||||
options: ['empty', 'user', 'admin'],
|
|
||||||
control: 'radio',
|
|
||||||
mapping: {
|
|
||||||
empty: [],
|
|
||||||
user: ['MOCK-USER-ROLE'],
|
|
||||||
admin: ['MOCK-ADMIN-ROLE']
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} as Meta;
|
} as Meta;
|
||||||
@@ -61,7 +59,6 @@ primary.args = {
|
|||||||
mode: 'single',
|
mode: 'single',
|
||||||
preSelectUsers: [],
|
preSelectUsers: [],
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
roles: [],
|
|
||||||
title: 'Users',
|
title: 'Users',
|
||||||
validate: false
|
validate: false
|
||||||
};
|
};
|
||||||
@@ -71,7 +68,7 @@ validPreselectedUsers.args = {
|
|||||||
...primary.args,
|
...primary.args,
|
||||||
validate: true,
|
validate: true,
|
||||||
mode: 'multiple',
|
mode: 'multiple',
|
||||||
preSelectUsers: mockIdentityUsers
|
preSelectUsers: mockFoodUsers
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mandatoryPreselectedUsers = template.bind({});
|
export const mandatoryPreselectedUsers = template.bind({});
|
||||||
@@ -79,8 +76,7 @@ mandatoryPreselectedUsers.args = {
|
|||||||
...primary.args,
|
...primary.args,
|
||||||
validate: true,
|
validate: true,
|
||||||
mode: 'multiple',
|
mode: 'multiple',
|
||||||
preSelectUsers: [{ id: 'mock-user-id-1', username: 'userName1', firstName: 'first-name-1', lastName: 'last-name-1', email: 'abc@xyz.com', readonly: true },
|
preSelectUsers: [{ ...mockKielbasaSausage, readonly: true }, mockShepherdsPie]
|
||||||
{ id: 'mock-user-id-2', username: 'userName2', firstName: 'first-name-2', lastName: 'last-name-2', email: 'abcd@xyz.com' }]
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const invalidPreselectedUsers = template.bind({});
|
export const invalidPreselectedUsers = template.bind({});
|
||||||
@@ -88,28 +84,22 @@ invalidPreselectedUsers.args = {
|
|||||||
...primary.args,
|
...primary.args,
|
||||||
validate: true,
|
validate: true,
|
||||||
mode: 'multiple',
|
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({});
|
export const excludedUsers = template.bind({});
|
||||||
excludedUsers.args = {
|
excludedUsers.args = {
|
||||||
...primary.args,
|
...primary.args,
|
||||||
excludedUsers: [
|
excludedUsers: [
|
||||||
{ id: 'mock-user-id-2' },
|
mockKielbasaSausage,
|
||||||
{ id: 'mock-user-id-3' }
|
mockYorkshirePudding
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
export const adminRoleUser = template.bind({});
|
|
||||||
adminRoleUser.args = {
|
|
||||||
...primary.args,
|
|
||||||
roles: 'admin'
|
|
||||||
};
|
|
||||||
|
|
||||||
export const noUsers = template.bind({});
|
export const noUsers = template.bind({});
|
||||||
noUsers.args = {
|
noUsers.args = {
|
||||||
...primary.args,
|
...primary.args,
|
||||||
excludedUsers: mockIdentityUsers
|
excludedUsers: mockFoodUsers
|
||||||
};
|
};
|
||||||
|
|
||||||
export const invalidOrEmptyAppName = template.bind({});
|
export const invalidOrEmptyAppName = template.bind({});
|
||||||
|
@@ -26,18 +26,21 @@ import {
|
|||||||
SimpleChanges,
|
SimpleChanges,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
ViewChild, ElementRef, SimpleChange
|
ViewChild,
|
||||||
|
ElementRef,
|
||||||
|
Inject
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { Observable, of, BehaviorSubject, Subject } from 'rxjs';
|
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||||
import { switchMap, debounceTime, distinctUntilChanged, mergeMap, tap, filter, map, takeUntil } from 'rxjs/operators';
|
import { switchMap, debounceTime, distinctUntilChanged, mergeMap, tap, filter, takeUntil } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
FullNamePipe,
|
FullNamePipe,
|
||||||
IdentityUserModel,
|
|
||||||
IdentityUserService,
|
|
||||||
LogService
|
LogService
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { trigger, state, style, transition, animate } from '@angular/animations';
|
import { trigger, state, style, transition, animate } from '@angular/animations';
|
||||||
import { ComponentSelectionMode } from '../../types';
|
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({
|
@Component({
|
||||||
selector: 'adf-cloud-people',
|
selector: 'adf-cloud-people',
|
||||||
@@ -139,15 +142,14 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
@ViewChild('userInput')
|
@ViewChild('userInput')
|
||||||
private userInput: ElementRef<HTMLInputElement>;
|
private userInput: ElementRef<HTMLInputElement>;
|
||||||
|
|
||||||
private _searchUsers: IdentityUserModel[] = [];
|
private searchUsers: IdentityUserModel[] = [];
|
||||||
private onDestroy$ = new Subject<boolean>();
|
private onDestroy$ = new Subject<boolean>();
|
||||||
|
|
||||||
selectedUsers: IdentityUserModel[] = [];
|
selectedUsers: IdentityUserModel[] = [];
|
||||||
invalidUsers: IdentityUserModel[] = [];
|
invalidUsers: IdentityUserModel[] = [];
|
||||||
|
|
||||||
searchUsers$ = new BehaviorSubject<IdentityUserModel[]>(this._searchUsers);
|
searchUsers$ = new BehaviorSubject<IdentityUserModel[]>(this.searchUsers);
|
||||||
subscriptAnimationState: string = 'enter';
|
subscriptAnimationState: string = 'enter';
|
||||||
clientId: string;
|
|
||||||
isFocused: boolean;
|
isFocused: boolean;
|
||||||
touched: boolean = false;
|
touched: boolean = false;
|
||||||
|
|
||||||
@@ -157,20 +159,19 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
validationLoading = false;
|
validationLoading = false;
|
||||||
searchLoading = false;
|
searchLoading = false;
|
||||||
|
|
||||||
|
typingUniqueValueNotEmpty$: Observable<string>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private identityUserService: IdentityUserService,
|
@Inject(IDENTITY_USER_SERVICE_TOKEN)
|
||||||
|
private identityUserService: IdentityUserServiceInterface,
|
||||||
private logService: LogService) {}
|
private logService: LogService) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loadClientId();
|
|
||||||
this.initSearch();
|
this.initSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
if (this.valueChanged(changes.preSelectUsers)
|
if (this.hasPreselectedUsersChanged(changes) || this.hasModeChanged(changes) || this.isValidationChanged(changes)) {
|
||||||
|| this.valueChanged(changes.mode)
|
|
||||||
|| this.valueChanged(changes.validate)
|
|
||||||
) {
|
|
||||||
if (this.hasPreSelectUsers()) {
|
if (this.hasPreSelectUsers()) {
|
||||||
this.loadPreSelectUsers();
|
this.loadPreSelectUsers();
|
||||||
} else if (this.hasPreselectedUsersCleared(changes)) {
|
} else if (this.hasPreselectedUsersCleared(changes)) {
|
||||||
@@ -182,32 +183,51 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.invalidUsers = [];
|
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 {
|
private initSearch(): void {
|
||||||
this.searchUserCtrl.valueChanges.pipe(
|
this.initializeStream();
|
||||||
filter((value) => {
|
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;
|
this.searchLoading = true;
|
||||||
return typeof value === 'string';
|
return typeof value === 'string';
|
||||||
}),
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const typingValueHandleErrorMessage$ = typingValueTypeSting$.pipe(
|
||||||
tap((value: string) => {
|
tap((value: string) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
this.setTypingError();
|
this.setTypingError();
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const typingValueDebouncedUnique$ = typingValueHandleErrorMessage$.pipe(
|
||||||
debounceTime(500),
|
debounceTime(500),
|
||||||
distinctUntilChanged(),
|
distinctUntilChanged()
|
||||||
|
);
|
||||||
|
|
||||||
|
this.typingUniqueValueNotEmpty$ = typingValueDebouncedUnique$.pipe(
|
||||||
tap((value: string) => {
|
tap((value: string) => {
|
||||||
if (value.trim()) {
|
if (value.trim()) {
|
||||||
this.searchedValue = value;
|
this.searchedValue = value;
|
||||||
@@ -216,87 +236,17 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.searchUserCtrl.markAsUntouched();
|
this.searchUserCtrl.markAsUntouched();
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
tap(() => {
|
tap(() => this.resetSearchUsers())
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
private isValidationEnabled(): boolean {
|
||||||
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 {
|
|
||||||
return this.validate === true;
|
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 {
|
private isUserAlreadySelected(searchUser: IdentityUserModel): boolean {
|
||||||
if (this.selectedUsers && this.selectedUsers.length > 0) {
|
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;
|
return !!result;
|
||||||
}
|
}
|
||||||
@@ -305,7 +255,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
|
|
||||||
private isExcludedUser(searchUser: IdentityUserModel): boolean {
|
private isExcludedUser(searchUser: IdentityUserModel): boolean {
|
||||||
if (this.excludedUsers?.length > 0) {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -334,28 +284,14 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async validatePreselectUsers(): Promise<any> {
|
private async validatePreselectUsers(): Promise<any> {
|
||||||
this.invalidUsers = [];
|
this.invalidUsers = [];
|
||||||
const validUsers: IdentityUserModel[] = [];
|
|
||||||
|
|
||||||
for (const user of this.getPreselectedUsers()) {
|
for (const user of this.getPreselectedUsers()) {
|
||||||
try {
|
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)) {
|
if (!this.equalsUsers(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 {
|
|
||||||
this.invalidUsers.push(user);
|
this.invalidUsers.push(user);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -365,10 +301,9 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.checkPreselectValidationErrors();
|
this.checkPreselectValidationErrors();
|
||||||
this.selectedUsers = validUsers.concat(this.invalidUsers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
compare(preselectedUser: IdentityUserModel, identityUser: IdentityUserModel): boolean {
|
equalsUsers(preselectedUser: IdentityUserModel, identityUser: IdentityUserModel): boolean {
|
||||||
if (preselectedUser && identityUser) {
|
if (preselectedUser && identityUser) {
|
||||||
const uniquePropertyIdentifiers = ['id', 'username', 'email'];
|
const uniquePropertyIdentifiers = ['id', 'username', 'email'];
|
||||||
for (const property of Object.keys(preselectedUser)) {
|
for (const property of Object.keys(preselectedUser)) {
|
||||||
@@ -380,33 +315,6 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
return false;
|
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[] {
|
removeDuplicatedUsers(users: IdentityUserModel[]): IdentityUserModel[] {
|
||||||
return users.filter((user, index, self) =>
|
return users.filter((user, index, self) =>
|
||||||
index === self.findIndex(auxUser =>
|
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 {
|
onSelect(user: IdentityUserModel): void {
|
||||||
if (user) {
|
if (user) {
|
||||||
this.selectUser.emit(user);
|
this.selectUser.emit(user);
|
||||||
@@ -442,7 +337,7 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
|
|
||||||
this.userInput.nativeElement.value = '';
|
this.userInput.nativeElement.value = '';
|
||||||
this.searchUserCtrl.setValue('');
|
this.searchUserCtrl.setValue('');
|
||||||
this.userChipsCtrlValue(this.selectedUsers[0].username);
|
this.userChipsControlValue(this.selectedUsers[0].username);
|
||||||
|
|
||||||
this.changedUsers.emit(this.selectedUsers);
|
this.changedUsers.emit(this.selectedUsers);
|
||||||
this.resetSearchUsers();
|
this.resetSearchUsers();
|
||||||
@@ -454,10 +349,10 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
this.removeUserFromSelected(userToRemove);
|
this.removeUserFromSelected(userToRemove);
|
||||||
this.changedUsers.emit(this.selectedUsers);
|
this.changedUsers.emit(this.selectedUsers);
|
||||||
if (this.selectedUsers.length === 0) {
|
if (this.selectedUsers.length === 0) {
|
||||||
this.userChipsCtrlValue('');
|
this.userChipsControlValue('');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
this.userChipsCtrlValue(this.selectedUsers[0].username);
|
this.userChipsControlValue(this.selectedUsers[0].username);
|
||||||
}
|
}
|
||||||
this.searchUserCtrl.markAsDirty();
|
this.searchUserCtrl.markAsDirty();
|
||||||
this.searchUserCtrl.markAsTouched();
|
this.searchUserCtrl.markAsTouched();
|
||||||
@@ -468,10 +363,17 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private userChipsCtrlValue(value: string) {
|
private checkPreselectValidationErrors(): void {
|
||||||
this.userChipsCtrl.setValue(value);
|
this.invalidUsers = this.removeDuplicatedUsers(this.invalidUsers);
|
||||||
this.userChipsCtrl.markAsDirty();
|
|
||||||
this.userChipsCtrl.markAsTouched();
|
if (this.invalidUsers.length > 0) {
|
||||||
|
this.generateInvalidUsersMessage();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.warning.emit({
|
||||||
|
message: 'INVALID_PRESELECTED_USERS',
|
||||||
|
users: this.invalidUsers
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private removeUserFromSelected({ id, username, email }: IdentityUserModel): void {
|
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.validateUsersMessage = '';
|
||||||
|
|
||||||
this.invalidUsers.forEach((invalidUser, index) => {
|
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 {
|
hasPreselectError(): boolean {
|
||||||
return this.invalidUsers
|
return this.invalidUsers
|
||||||
&& this.invalidUsers.length > 0;
|
&& this.invalidUsers.length > 0;
|
||||||
@@ -522,11 +417,11 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
return FullNamePipe.prototype.transform(user);
|
return FullNamePipe.prototype.transform(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
isMultipleMode(): boolean {
|
private isMultipleMode(): boolean {
|
||||||
return this.mode === 'multiple';
|
return this.mode === 'multiple';
|
||||||
}
|
}
|
||||||
|
|
||||||
isSingleMode(): boolean {
|
private isSingleMode(): boolean {
|
||||||
return this.mode === 'single';
|
return this.mode === 'single';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,9 +436,22 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
&& this.preSelectUsers.length > 0;
|
&& this.preSelectUsers.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private valueChanged(change: SimpleChange): boolean {
|
private hasModeChanged(changes: SimpleChanges): boolean {
|
||||||
return change
|
return changes
|
||||||
&& change.currentValue !== change.previousValue;
|
&& 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 {
|
private hasPreselectedUsersCleared(changes: SimpleChanges): boolean {
|
||||||
@@ -554,20 +462,21 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private resetSearchUsers(): void {
|
private resetSearchUsers(): void {
|
||||||
this._searchUsers = [];
|
this.searchUsers = [];
|
||||||
this.searchUsers$.next(this._searchUsers);
|
this.searchUsers$.next(this.searchUsers);
|
||||||
}
|
}
|
||||||
|
|
||||||
private isUserPartOfAllRestrictedGroups(user: IdentityUserModel): Observable<boolean> {
|
private setTypingError(): void {
|
||||||
return this.getUserGroups(user.id).pipe(
|
this.searchUserCtrl.setErrors({
|
||||||
map(userGroups => this.groupsRestriction.every(restricted => userGroups.includes(restricted)))
|
searchTypingError: true,
|
||||||
);
|
...this.searchUserCtrl.errors
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private getUserGroups(userId: string): Observable<string[]> {
|
private userChipsControlValue(value: string) {
|
||||||
return this.identityUserService.getInvolvedGroups(userId).pipe(
|
this.userChipsCtrl.setValue(value);
|
||||||
map(groups => groups.map((group) => group.name))
|
this.userChipsCtrl.markAsDirty();
|
||||||
);
|
this.userChipsCtrl.markAsTouched();
|
||||||
}
|
}
|
||||||
|
|
||||||
getSelectedUsers(): IdentityUserModel[] {
|
getSelectedUsers(): IdentityUserModel[] {
|
||||||
@@ -617,4 +526,9 @@ export class PeopleCloudComponent implements OnInit, OnChanges, OnDestroy {
|
|||||||
getValidationMinLength(): string {
|
getValidationMinLength(): string {
|
||||||
return this.searchUserCtrl.errors.minlength.requiredLength;
|
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 { CoreModule } from '@alfresco/adf-core';
|
||||||
import { FlexLayoutModule } from '@angular/flex-layout';
|
import { FlexLayoutModule } from '@angular/flex-layout';
|
||||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
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({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -35,6 +37,9 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|||||||
declarations: [PeopleCloudComponent],
|
declarations: [PeopleCloudComponent],
|
||||||
exports: [
|
exports: [
|
||||||
PeopleCloudComponent
|
PeopleCloudComponent
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
{ provide: IDENTITY_USER_SERVICE_TOKEN, useExisting: IdentityUserService }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class PeopleCloudModule {
|
export class PeopleCloudModule {
|
||||||
|
@@ -16,5 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export * from './components/people-cloud.component';
|
export * from './components/people-cloud.component';
|
||||||
|
|
||||||
export * from './people-cloud.module';
|
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 { Component, ViewChild } from '@angular/core';
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
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 { CancelProcessDirective } from './cancel-process.directive';
|
||||||
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
|
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { ProcessInstanceCloud } from '../start-process/models/process-instance-cloud.model';
|
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 processDetailsMockRunning: ProcessInstanceCloud = { initiator: 'usermock', status: 'RUNNING' };
|
||||||
const processDetailsMockCompleted: ProcessInstanceCloud = { initiator: 'usermock', status: 'COMPLETED' };
|
const processDetailsMockCompleted: ProcessInstanceCloud = { initiator: 'usermock', status: 'COMPLETED' };
|
||||||
|
@@ -15,11 +15,11 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { Directive, HostListener, Output, EventEmitter, OnInit, OnDestroy, ElementRef } from '@angular/core';
|
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 { ProcessCloudService } from '../services/process-cloud.service';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
import { ProcessInstanceCloud } from '../start-process/models/process-instance-cloud.model';
|
import { ProcessInstanceCloud } from '../start-process/models/process-instance-cloud.model';
|
||||||
|
import { IdentityUserService } from '../../people/services/identity-user.service';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
// eslint-disable-next-line @angular-eslint/directive-selector
|
// eslint-disable-next-line @angular-eslint/directive-selector
|
||||||
|
@@ -25,11 +25,12 @@ import moment from 'moment-es6';
|
|||||||
import { Moment } from 'moment';
|
import { Moment } from 'moment';
|
||||||
import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service';
|
import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service';
|
||||||
import { ProcessFilterCloudModel, ProcessFilterProperties, ProcessFilterAction, ProcessFilterOptions, ProcessSortFilterProperty } from '../models/process-filter-cloud.model';
|
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 { ProcessFilterCloudService } from '../services/process-filter-cloud.service';
|
||||||
import { ProcessFilterDialogCloudComponent } from './process-filter-dialog-cloud.component';
|
import { ProcessFilterDialogCloudComponent } from './process-filter-dialog-cloud.component';
|
||||||
import { ProcessCloudService } from '../../services/process-cloud.service';
|
import { ProcessCloudService } from '../../services/process-cloud.service';
|
||||||
import { DateCloudFilterType, DateRangeFilter } from '../../../models/date-cloud-filter.model';
|
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 = 'save';
|
||||||
export const PROCESS_FILTER_ACTION_SAVE_AS = 'saveAs';
|
export const PROCESS_FILTER_ACTION_SAVE_AS = 'saveAs';
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { setupTestBed, IdentityUserService } from '@alfresco/adf-core';
|
import { setupTestBed } from '@alfresco/adf-core';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { ProcessFilterCloudService } from './process-filter-cloud.service';
|
import { ProcessFilterCloudService } from './process-filter-cloud.service';
|
||||||
import { PROCESS_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.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 { TranslateModule } from '@ngx-translate/core';
|
||||||
import { fakeEmptyProcessCloudFilterEntries, fakeProcessCloudFilterEntries, fakeProcessCloudFilters, fakeProcessCloudFilterWithDifferentEntries, fakeProcessFilter } from '../mock/process-filters-cloud.mock';
|
import { fakeEmptyProcessCloudFilterEntries, fakeProcessCloudFilterEntries, fakeProcessCloudFilters, fakeProcessCloudFilterWithDifferentEntries, fakeProcessFilter } from '../mock/process-filters-cloud.mock';
|
||||||
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
|
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
|
||||||
|
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||||
|
|
||||||
describe('ProcessFilterCloudService', () => {
|
describe('ProcessFilterCloudService', () => {
|
||||||
let service: ProcessFilterCloudService;
|
let service: ProcessFilterCloudService;
|
||||||
|
@@ -15,13 +15,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IdentityUserService } from '@alfresco/adf-core';
|
|
||||||
import { Injectable, Inject } from '@angular/core';
|
import { Injectable, Inject } from '@angular/core';
|
||||||
import { Observable, of, BehaviorSubject, throwError } from 'rxjs';
|
import { Observable, of, BehaviorSubject, throwError } from 'rxjs';
|
||||||
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
|
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
|
||||||
import { switchMap, map, catchError } from 'rxjs/operators';
|
import { switchMap, map, catchError } from 'rxjs/operators';
|
||||||
import { PROCESS_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
import { PROCESS_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
||||||
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
|
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
|
||||||
|
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
|
@@ -39,8 +39,7 @@ export class BaseCloudService {
|
|||||||
path: '',
|
path: '',
|
||||||
httpMethod: '',
|
httpMethod: '',
|
||||||
contentTypes: ['application/json'],
|
contentTypes: ['application/json'],
|
||||||
accepts: ['application/json'],
|
accepts: ['application/json']
|
||||||
returnType: Object
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
import { Directive, Input, HostListener, Output, EventEmitter, OnInit } from '@angular/core';
|
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';
|
import { TaskCloudService } from '../services/task-cloud.service';
|
||||||
|
|
||||||
@Directive({
|
@Directive({
|
||||||
|
@@ -18,8 +18,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { AppConfigService, CardViewArrayItem, LogService } from '@alfresco/adf-core';
|
import { AppConfigService, CardViewArrayItem, LogService } from '@alfresco/adf-core';
|
||||||
import { from, Observable, of, Subject, throwError } from 'rxjs';
|
import { from, Observable, of, Subject, throwError } from 'rxjs';
|
||||||
import { DEFAULT_TASK_PRIORITIES, TaskPriorityOption, TASK_ASSIGNED_STATE, TASK_CREATED_STATE } from '../models/task.model';
|
import { DEFAULT_TASK_PRIORITIES, TaskPriorityOption } from '../models/task.model';
|
||||||
import { TaskDetailsCloudModel } from '../start-task/public-api';
|
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 { taskDetailsContainer } from '../task-header/mocks/task-details-cloud.mock';
|
||||||
import { ProcessDefinitionCloud } from '../../models/process-definition-cloud.model';
|
import { ProcessDefinitionCloud } from '../../models/process-definition-cloud.model';
|
||||||
import { StartTaskCloudRequestModel } from '../start-task/models/start-task-cloud-request.model';
|
import { StartTaskCloudRequestModel } from '../start-task/models/start-task-cloud-request.model';
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { TestBed } from '@angular/core/testing';
|
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 { TaskCloudService } from './task-cloud.service';
|
||||||
import { taskCompleteCloudMock } from '../task-header/mocks/fake-complete-task.mock';
|
import { taskCompleteCloudMock } from '../task-header/mocks/fake-complete-task.mock';
|
||||||
import { assignedTaskDetailsCloudMock, createdTaskDetailsCloudMock, emptyOwnerTaskDetailsCloudMock } from '../task-header/mocks/task-details-cloud.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 { cloudMockUser } from '../start-task/mock/user-cloud.mock';
|
||||||
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
|
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { IdentityUserService } from '../../people/services/identity-user.service';
|
||||||
|
|
||||||
describe('Task Cloud Service', () => {
|
describe('Task Cloud Service', () => {
|
||||||
|
|
||||||
|
@@ -16,9 +16,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
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 { throwError, Observable, of, Subject } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { catchError, map } from 'rxjs/operators';
|
||||||
import {
|
import {
|
||||||
TaskDetailsCloudModel,
|
TaskDetailsCloudModel,
|
||||||
StartTaskCloudResponseModel,
|
StartTaskCloudResponseModel,
|
||||||
@@ -35,6 +35,7 @@ import {
|
|||||||
TaskPriorityOption
|
TaskPriorityOption
|
||||||
} from '../models/task.model';
|
} from '../models/task.model';
|
||||||
import { TaskCloudServiceInterface } from './task-cloud.service.interface';
|
import { TaskCloudServiceInterface } from './task-cloud.service.interface';
|
||||||
|
import { IdentityUserService } from '../../people/services/identity-user.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -242,7 +243,9 @@ export class TaskCloudService extends BaseCloudService implements TaskCloudServi
|
|||||||
getCandidateUsers(appName: string, taskId: string): Observable<string[]> {
|
getCandidateUsers(appName: string, taskId: string): Observable<string[]> {
|
||||||
if ((appName || appName === '') && taskId) {
|
if ((appName || appName === '') && taskId) {
|
||||||
const queryUrl = `${this.getBasePath(appName)}/query/v1/tasks/${taskId}/candidate-users`;
|
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 {
|
} else {
|
||||||
this.logService.error('AppName and TaskId are mandatory to get candidate user');
|
this.logService.error('AppName and TaskId are mandatory to get candidate user');
|
||||||
return of([]);
|
return of([]);
|
||||||
@@ -320,4 +323,9 @@ export class TaskCloudService extends BaseCloudService implements TaskCloudServi
|
|||||||
const currentUser = this.identityUserService.getCurrentUserInfo().username;
|
const currentUser = this.identityUserService.getCurrentUserInfo().username;
|
||||||
return assignee === currentUser;
|
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 { 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 { StartTaskCloudComponent } from './start-task-cloud.component';
|
||||||
import { of, throwError } from 'rxjs';
|
import { of, throwError } from 'rxjs';
|
||||||
import { taskDetailsMock } from '../mock/task-details.mock';
|
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 { TaskCloudService } from '../../services/task-cloud.service';
|
||||||
import { StartTaskCloudRequestModel } from '../models/start-task-cloud-request.model';
|
import { StartTaskCloudRequestModel } from '../models/start-task-cloud-request.model';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||||
|
import { IdentityUserModel } from '../../../people/models/identity-user.model';
|
||||||
|
|
||||||
describe('StartTaskCloudComponent', () => {
|
describe('StartTaskCloudComponent', () => {
|
||||||
|
|
||||||
|
@@ -24,8 +24,6 @@ import {
|
|||||||
MOMENT_DATE_FORMATS, MomentDateAdapter,
|
MOMENT_DATE_FORMATS, MomentDateAdapter,
|
||||||
LogService,
|
LogService,
|
||||||
UserPreferencesService,
|
UserPreferencesService,
|
||||||
IdentityUserService,
|
|
||||||
IdentityUserModel,
|
|
||||||
UserPreferenceValues
|
UserPreferenceValues
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { PeopleCloudComponent } from '../../../people/components/people-cloud.component';
|
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 { StartTaskCloudRequestModel } from '../models/start-task-cloud-request.model';
|
||||||
import { takeUntil } from 'rxjs/operators';
|
import { takeUntil } from 'rxjs/operators';
|
||||||
import { TaskPriorityOption } from '../../models/task.model';
|
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 MAX_NAME_LENGTH = 255;
|
||||||
const DATE_FORMAT: string = 'DD/MM/YYYY';
|
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 { debounceTime, filter, finalize, switchMap, takeUntil } from 'rxjs/operators';
|
||||||
import { Observable, Subject } from 'rxjs';
|
import { Observable, Subject } from 'rxjs';
|
||||||
import { DateAdapter } from '@angular/material/core';
|
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 { TaskFilterDialogCloudComponent } from '../task-filter-dialog/task-filter-dialog-cloud.component';
|
||||||
import { MatDialog } from '@angular/material/dialog';
|
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 */
|
/* eslint-disable @typescript-eslint/naming-convention */
|
||||||
|
|
||||||
|
@@ -19,7 +19,7 @@ import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing';
|
|||||||
import { SimpleChange } from '@angular/core';
|
import { SimpleChange } from '@angular/core';
|
||||||
import { By } from '@angular/platform-browser';
|
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 { MatDialog } from '@angular/material/dialog';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { debounceTime } from 'rxjs/operators';
|
import { debounceTime } from 'rxjs/operators';
|
||||||
@@ -41,6 +41,7 @@ import { TaskFilterCloudModel } from '../../models/filter-cloud.model';
|
|||||||
import { PeopleCloudModule } from '../../../../people/people-cloud.module';
|
import { PeopleCloudModule } from '../../../../people/people-cloud.module';
|
||||||
import { ProcessDefinitionCloud } from '../../../../models/process-definition-cloud.model';
|
import { ProcessDefinitionCloud } from '../../../../models/process-definition-cloud.model';
|
||||||
import { MatIconTestingModule } from '@angular/material/icon/testing';
|
import { MatIconTestingModule } from '@angular/material/icon/testing';
|
||||||
|
import { IdentityUserModel } from '../../../../people/models/identity-user.model';
|
||||||
|
|
||||||
describe('EditTaskFilterCloudComponent', () => {
|
describe('EditTaskFilterCloudComponent', () => {
|
||||||
let component: EditTaskFilterCloudComponent;
|
let component: EditTaskFilterCloudComponent;
|
||||||
@@ -537,7 +538,10 @@ describe('EditTaskFilterCloudComponent', () => {
|
|||||||
|
|
||||||
const mockUser: IdentityUserModel[] = [{
|
const mockUser: IdentityUserModel[] = [{
|
||||||
id: 'id',
|
id: 'id',
|
||||||
username: 'test'
|
username: 'test',
|
||||||
|
firstName: 'first-name',
|
||||||
|
lastName: 'last-name',
|
||||||
|
email: 'email@fake.com'
|
||||||
}];
|
}];
|
||||||
|
|
||||||
const startedDateTypeControl: AbstractControl = component.editTaskFilterForm.get('completedBy');
|
const startedDateTypeControl: AbstractControl = component.editTaskFilterForm.get('completedBy');
|
||||||
|
@@ -16,13 +16,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
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 { TranslateModule } from '@ngx-translate/core';
|
||||||
import { TaskAssignmentFilterCloudComponent } from './task-assignment-filter.component';
|
import { TaskAssignmentFilterCloudComponent } from './task-assignment-filter.component';
|
||||||
import { GroupCloudModule } from 'process-services-cloud/src/lib/group/public-api';
|
import { GroupCloudModule } from 'process-services-cloud/src/lib/group/public-api';
|
||||||
import { TaskFiltersCloudModule } from '../../task-filters-cloud.module';
|
import { TaskFiltersCloudModule } from '../../task-filters-cloud.module';
|
||||||
import { AssignmentType } from '../../models/filter-cloud.model';
|
import { AssignmentType } from '../../models/filter-cloud.model';
|
||||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
|
import { IdentityUserService } from '../../../../people/services/identity-user.service';
|
||||||
|
|
||||||
describe('EditTaskFilterCloudComponent', () => {
|
describe('EditTaskFilterCloudComponent', () => {
|
||||||
let component: TaskAssignmentFilterCloudComponent;
|
let component: TaskAssignmentFilterCloudComponent;
|
||||||
|
@@ -17,8 +17,10 @@
|
|||||||
|
|
||||||
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
import { Component, Input, Output, EventEmitter, OnInit } from '@angular/core';
|
||||||
import { FormControl } from '@angular/forms';
|
import { FormControl } from '@angular/forms';
|
||||||
import { IdentityGroupModel, IdentityUserModel, IdentityUserService } from '@alfresco/adf-core';
|
|
||||||
import { AssignmentType, TaskFilterProperties } from '../../models/filter-cloud.model';
|
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({
|
@Component({
|
||||||
selector: 'adf-cloud-task-assignment-filter',
|
selector: 'adf-cloud-task-assignment-filter',
|
||||||
|
@@ -22,7 +22,8 @@
|
|||||||
import { DateCloudFilterType } from '../../../models/date-cloud-filter.model';
|
import { DateCloudFilterType } from '../../../models/date-cloud-filter.model';
|
||||||
import { DateRangeFilterService } from '../../../common/date-range-filter/date-range-filter.service';
|
import { DateRangeFilterService } from '../../../common/date-range-filter/date-range-filter.service';
|
||||||
import { ComponentSelectionMode } from '../../../types';
|
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 {
|
export class TaskFilterCloudModel {
|
||||||
id: string;
|
id: string;
|
||||||
|
@@ -15,13 +15,13 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { IdentityUserService } from '@alfresco/adf-core';
|
|
||||||
import { Injectable, Inject } from '@angular/core';
|
import { Injectable, Inject } from '@angular/core';
|
||||||
import { Observable, of, BehaviorSubject } from 'rxjs';
|
import { Observable, of, BehaviorSubject } from 'rxjs';
|
||||||
import { ServiceTaskFilterCloudModel } from '../models/filter-cloud.model';
|
import { ServiceTaskFilterCloudModel } from '../models/filter-cloud.model';
|
||||||
import { switchMap, map } from 'rxjs/operators';
|
import { switchMap, map } from 'rxjs/operators';
|
||||||
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
|
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
|
||||||
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
||||||
|
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
|
@@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { IdentityUserService, setupTestBed } from '@alfresco/adf-core';
|
import { setupTestBed } from '@alfresco/adf-core';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
||||||
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.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 { NotificationCloudService } from '../../../services/notification-cloud.service';
|
||||||
import { TaskCloudEngineEvent } from './../../../models/engine-event-cloud.model';
|
import { TaskCloudEngineEvent } from './../../../models/engine-event-cloud.model';
|
||||||
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
||||||
|
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||||
|
|
||||||
describe('TaskFilterCloudService', () => {
|
describe('TaskFilterCloudService', () => {
|
||||||
let service: TaskFilterCloudService;
|
let service: TaskFilterCloudService;
|
||||||
|
@@ -15,7 +15,7 @@
|
|||||||
* limitations under the License.
|
* 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 { Injectable, Inject } from '@angular/core';
|
||||||
import { Observable, of, BehaviorSubject, throwError } from 'rxjs';
|
import { Observable, of, BehaviorSubject, throwError } from 'rxjs';
|
||||||
import { TaskFilterCloudModel } from '../models/filter-cloud.model';
|
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 { TaskCloudNodePaging } from '../../../models/task-cloud.model';
|
||||||
import { NotificationCloudService } from '../../../services/notification-cloud.service';
|
import { NotificationCloudService } from '../../../services/notification-cloud.service';
|
||||||
import { TaskCloudEngineEvent } from '../../../models/engine-event-cloud.model';
|
import { TaskCloudEngineEvent } from '../../../models/engine-event-cloud.model';
|
||||||
|
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||||
|
|
||||||
const TASK_EVENT_SUBSCRIPTION_QUERY = `
|
const TASK_EVENT_SUBSCRIPTION_QUERY = `
|
||||||
subscription {
|
subscription {
|
||||||
|
@@ -19,7 +19,7 @@ import { DebugElement, SimpleChange } from '@angular/core';
|
|||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
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 { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
||||||
import { TaskFormCloudComponent } from './task-form-cloud.component';
|
import { TaskFormCloudComponent } from './task-form-cloud.component';
|
||||||
import {
|
import {
|
||||||
@@ -32,6 +32,7 @@ import {
|
|||||||
} from '../../start-task/models/task-details-cloud.model';
|
} from '../../start-task/models/task-details-cloud.model';
|
||||||
import { TaskCloudService } from '../../services/task-cloud.service';
|
import { TaskCloudService } from '../../services/task-cloud.service';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||||
|
|
||||||
const taskDetails: TaskDetailsCloudModel = {
|
const taskDetails: TaskDetailsCloudModel = {
|
||||||
appName: 'simple-app',
|
appName: 'simple-app',
|
||||||
|
@@ -25,11 +25,7 @@ import {
|
|||||||
AppConfigServiceMock,
|
AppConfigServiceMock,
|
||||||
TranslationService,
|
TranslationService,
|
||||||
TranslationMock,
|
TranslationMock,
|
||||||
CoreModule,
|
CoreModule
|
||||||
IdentityUserService,
|
|
||||||
IdentityUserServiceMock,
|
|
||||||
IdentityGroupService,
|
|
||||||
IdentityGroupServiceMock
|
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { ProcessServicesCloudModule } from '../process-services-cloud.module';
|
import { ProcessServicesCloudModule } from '../process-services-cloud.module';
|
||||||
@@ -47,9 +43,7 @@ import { RouterTestingModule } from '@angular/router/testing';
|
|||||||
providers: [
|
providers: [
|
||||||
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
|
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
|
||||||
{ provide: AppConfigService, useClass: AppConfigServiceMock },
|
{ provide: AppConfigService, useClass: AppConfigServiceMock },
|
||||||
{ provide: TranslationService, useClass: TranslationMock },
|
{ provide: TranslationService, useClass: TranslationMock }
|
||||||
{ provide: IdentityUserService, useClass: IdentityUserServiceMock },
|
|
||||||
{ provide: IdentityGroupService, useClass: IdentityGroupServiceMock }
|
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
NoopAnimationsModule,
|
NoopAnimationsModule,
|
||||||
|
Reference in New Issue
Block a user