[ADF-4711] User Preferences in Task Filter (#4891)

* [ADF-4711] User Preferences in Task Filter
This commit is contained in:
mcchrys
2019-07-08 20:59:47 +05:30
committed by Eugenio Romano
parent 3af702f58e
commit abc376ff33
10 changed files with 776 additions and 201 deletions

View File

@@ -62,7 +62,7 @@ export class CloudFiltersDemoComponent implements OnInit {
}
onTaskFilterSelected(filter) {
this.cloudLayoutService.setCurrentTaskFilterParam({id: filter.id});
this.cloudLayoutService.setCurrentTaskFilterParam({id: filter && filter.id ? filter.id : ''});
const currentFilter = Object.assign({}, filter);
this.router.navigate([`/cloud/${this.appName}/tasks/`], { queryParams: currentFilter });
}

View File

@@ -1,6 +1,7 @@
<mat-accordion *ngIf="taskFilter">
<mat-accordion [hideToggle]="isLoading">
<mat-expansion-panel (afterExpand)="onExpand($event)" (closed)="onClose($event)">
<mat-expansion-panel-header id="adf-edit-task-filter-expansion-header">
<mat-expansion-panel-header *ngIf="taskFilter" id="adf-edit-task-filter-expansion-header">
<ng-container *ngIf="!isLoading; else loadingTemplate">
<mat-panel-title fxLayoutAlign="space-between center" id="adf-edit-task-filter-title-id">{{taskFilter.name | translate}}</mat-panel-title>
<mat-panel-description fxLayoutAlign="space-between center" id="adf-edit-task-filter-sub-title-id">
<span *ngIf="showTitle">{{ 'ADF_CLOUD_EDIT_TASK_FILTER.TITLE' | translate}}</span>
@@ -12,8 +13,15 @@
</ng-container>
</div>
</mat-panel-description>
</ng-container>
<ng-template #loadingTemplate>
<div class="adf-cloud-edit-task-filter-loading-margin">
<mat-spinner [diameter]="30"></mat-spinner>
</div>
</ng-template>
</mat-expansion-panel-header>
<form [formGroup]="editTaskFilterForm">
<ng-container *ngIf="!isLoading;">
<form [formGroup]="editTaskFilterForm" *ngIf="editTaskFilterForm">
<div fxLayout="row wrap" fxLayout.xs="column" fxLayoutGap="10px" fxLayoutAlign="start center">
<ng-container *ngFor="let taskFilterProperty of taskFilterProperties">
<mat-form-field fxFlex="23%" *ngIf="isSelectType(taskFilterProperty)" [attr.data-automation-id]="taskFilterProperty.key">
@@ -43,7 +51,7 @@
placeholder="{{taskFilterProperty.label | translate}}"
[(ngModel)]="dateFilter[taskFilterProperty.key]"
[ngModelOptions]="{standalone: true}"
[attr.data-automation-id]="'adf-cloud-edit-task-property-' + taskFilterProperty.key">
[attr.data-automation-id]="'adf-cloud-edit-task-`perty-' + taskFilterProperty.key">
<mat-datepicker-toggle matSuffix [for]="dateController" [attr.data-automation-id]="'adf-cloud-edit-task-property-date-toggle-' + taskFilterProperty.key"></mat-datepicker-toggle>
<mat-datepicker #dateController [attr.data-automation-id]="'adf-cloud-edit-task-property-date-picker-' + taskFilterProperty.key"></mat-datepicker>
<div class="adf-edit-task-filter-date-error-container">
@@ -64,5 +72,6 @@
</ng-container>
</div>
</form>
</ng-container>
</mat-expansion-panel>
</mat-accordion>

View File

@@ -34,4 +34,11 @@
color: mat-color($warn);
}
}
.adf {
&-cloud-edit-process-filter-loading-margin {
margin-left: calc((100% - 100px) / 2);
margin-right: calc((100% - 100px) / 2);
}
}
}

View File

@@ -29,6 +29,7 @@ import { fakeApplicationInstance } from '../../../app/mock/app-model.mock';
import { TaskFiltersCloudModule } from '../task-filters-cloud.module';
import { EditTaskFilterCloudComponent } from './edit-task-filter-cloud.component';
import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
import { UserPreferenceCloudService } from '../../../services/user-preference.cloud.service';
import { TaskFilterDialogCloudComponent } from './task-filter-dialog-cloud.component';
import { fakeFilter, fakeAllTaskFilter } from '../mock/task-filters-cloud.mock';
import { AbstractControl } from '@angular/forms';
@@ -45,7 +46,7 @@ describe('EditTaskFilterCloudComponent', () => {
setupTestBed({
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule],
providers: [MatDialog]
providers: [MatDialog, UserPreferenceCloudService]
});
beforeEach(() => {
@@ -59,7 +60,7 @@ describe('EditTaskFilterCloudComponent', () => {
icon: 'icon',
name: 'fake-name'
}); }});
getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(fakeFilter);
getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeFilter));
getRunningApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance));
fixture.detectChanges();
});
@@ -68,7 +69,7 @@ describe('EditTaskFilterCloudComponent', () => {
expect(component instanceof EditTaskFilterCloudComponent).toBeTruthy();
});
it('should fetch task filter by taskId', async(() => {
it('should fetch task filter by taskId', () => {
const taskFilterIDchange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ 'id': taskFilterIDchange});
fixture.detectChanges();
@@ -80,9 +81,9 @@ describe('EditTaskFilterCloudComponent', () => {
expect(component.taskFilter.order).toEqual('ASC');
expect(component.taskFilter.sort).toEqual('id');
});
}));
});
it('should display filter name as title', () => {
it('should display filter name as title', async(() => {
const taskFilterIDchange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ 'id': taskFilterIDchange});
fixture.detectChanges();
@@ -92,7 +93,37 @@ describe('EditTaskFilterCloudComponent', () => {
expect(subTitle).toBeDefined();
expect(title.innerText).toEqual('FakeInvolvedTasks');
expect(subTitle.innerText.trim()).toEqual('ADF_CLOUD_EDIT_TASK_FILTER.TITLE');
}));
it('should not display mat-spinner if isloading set to false', async(() => {
const taskFilterIDchange = new SimpleChange(null, 'mock-task-filter-id', true);
component.ngOnChanges({ 'id': taskFilterIDchange });
fixture.detectChanges();
const title = fixture.debugElement.nativeElement.querySelector('#adf-edit-task-filter-title-id');
const subTitle = fixture.debugElement.nativeElement.querySelector('#adf-edit-task-filter-sub-title-id');
const matSpinnerElement = fixture.debugElement.nativeElement.querySelector('.adf-cloud-edit-task-filter-loading-margin');
fixture.whenStable().then(() => {
expect(matSpinnerElement).toBeNull();
expect(title).toBeDefined();
expect(subTitle).toBeDefined();
expect(title.innerText).toEqual('FakeInvolvedTasks');
expect(subTitle.innerText.trim()).toEqual('ADF_CLOUD_EDIT_TASK_FILTER.TITLE');
});
}));
it('should display mat-spinner if isloading set to true', async(() => {
component.isLoading = true;
const taskFilterIDchange = new SimpleChange(null, 'mock-task-filter-id', true);
component.ngOnChanges({ 'id': taskFilterIDchange });
fixture.detectChanges();
const matSpinnerElement = fixture.debugElement.nativeElement.querySelector('.adf-cloud-edit-task-filter-loading-margin');
fixture.whenStable().then(() => {
expect(matSpinnerElement).toBeDefined();
});
}));
describe('EditTaskFilter form', () => {
@@ -196,7 +227,7 @@ describe('EditTaskFilterCloudComponent', () => {
it('should select \'All\' option in Task Status if All filter is set', async(() => {
getTaskFilterSpy.and.returnValue(fakeAllTaskFilter);
getTaskFilterSpy.and.returnValue(of(fakeAllTaskFilter));
const taskFilterIDchange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ 'id': taskFilterIDchange});
@@ -303,7 +334,11 @@ describe('EditTaskFilterCloudComponent', () => {
it('should display sort properties when sort properties are specified', async(() => {
component.sortProperties = ['id', 'name', 'processInstanceId'];
getTaskFilterSpy.and.returnValue({ sort: 'my-custom-sort', processInstanceId: 'process-instance-id', priority: '12' });
getTaskFilterSpy.and.returnValue(of({
sort: 'my-custom-sort',
processInstanceId: 'process-instance-id',
priority: '12'
}));
fixture.detectChanges();
const taskFilterIDchange = new SimpleChange(undefined, 'mock-task-filter-id', true);
component.ngOnChanges({ 'id': taskFilterIDchange});
@@ -451,7 +486,7 @@ describe('EditTaskFilterCloudComponent', () => {
it('should emit save event and save the filter on click save button', async(() => {
component.toggleFilterActions = true;
const saveFilterSpy = spyOn(service, 'updateFilter').and.returnValue(fakeFilter);
const saveFilterSpy = spyOn(service, 'updateFilter');
const saveSpy: jasmine.Spy = spyOn(component.action, 'emit');
fixture.detectChanges();
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
@@ -474,7 +509,7 @@ describe('EditTaskFilterCloudComponent', () => {
it('should emit delete event and delete the filter on click of delete button', async(() => {
component.toggleFilterActions = true;
const deleteFilterSpy = spyOn(service, 'deleteFilter').and.callThrough();
const deleteFilterSpy = spyOn(service, 'deleteFilter');
const deleteSpy: jasmine.Spy = spyOn(component.action, 'emit');
fixture.detectChanges();
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');

View File

@@ -15,31 +15,32 @@
* limitations under the License.
*/
import { Component, OnChanges, Input, Output, EventEmitter, SimpleChanges, OnInit } from '@angular/core';
import { Component, OnChanges, Input, Output, EventEmitter, SimpleChanges, OnInit, OnDestroy } from '@angular/core';
import { AbstractControl, FormGroup, FormBuilder } from '@angular/forms';
import { TaskFilterCloudModel, TaskFilterProperties, FilterOptions, TaskFilterAction } from './../models/filter-cloud.model';
import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
import { MatDialog, DateAdapter } from '@angular/material';
import { TaskFilterDialogCloudComponent } from './task-filter-dialog-cloud.component';
import { TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core';
import { debounceTime, filter } from 'rxjs/operators';
import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service';
import { ApplicationInstanceModel } from '../../../app/models/application-instance.model';
import { debounceTime, filter, takeUntil } from 'rxjs/operators';
import { Subject } from 'rxjs';
import moment from 'moment-es6';
import { Moment } from 'moment';
import { TaskFilterCloudModel, TaskFilterProperties, FilterOptions, TaskFilterAction } from './../models/filter-cloud.model';
import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
import { TaskFilterDialogCloudComponent } from './task-filter-dialog-cloud.component';
import { TranslationService, UserPreferencesService, UserPreferenceValues } from '@alfresco/adf-core';
import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service';
import { ApplicationInstanceModel } from '../../../app/models/application-instance.model';
@Component({
selector: 'adf-cloud-edit-task-filter',
templateUrl: './edit-task-filter-cloud.component.html',
styleUrls: ['./edit-task-filter-cloud.component.scss']
})
export class EditTaskFilterCloudComponent implements OnInit, OnChanges {
export class EditTaskFilterCloudComponent implements OnInit, OnChanges, OnDestroy {
public static ACTION_SAVE = 'save';
public static ACTION_SAVE_AS = 'saveAs';
public static ACTION_DELETE = 'delete';
public static APP_RUNNING_STATUS: string = 'RUNNING';
public static MIN_VALUE = 1;
public static APPLICATION_NAME: string = 'appName';
public static LAST_MODIFIED: string = 'lastModified';
public static SORT: string = 'sort';
@@ -109,6 +110,9 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges {
taskFilterActions: TaskFilterAction[] = [];
toggleFilterActions: boolean = false;
private onDestroy$ = new Subject<boolean>();
isLoading: boolean = false;
constructor(
private formBuilder: FormBuilder,
public dialog: MatDialog,
@@ -128,14 +132,13 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges {
ngOnChanges(changes: SimpleChanges) {
const id = changes['id'];
if (id && id.currentValue !== id.previousValue) {
this.taskFilterProperties = this.createAndFilterProperties();
this.taskFilterActions = this.createAndFilterActions();
this.buildForm(this.taskFilterProperties);
this.retrieveTaskFilterAndBuildForm();
}
}
retrieveTaskFilter(): TaskFilterCloudModel {
return new TaskFilterCloudModel(this.taskFilterCloudService.getTaskFilterById(this.appName, this.id));
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
buildForm(taskFilterProperties: TaskFilterProperties[]) {
@@ -177,7 +180,25 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges {
formValues.lastModifiedTo = lastModifiedToFilterValue.toDate();
}
}
createAndFilterProperties(): TaskFilterProperties[] {
/**
* Fetches task filter by application name and filter id and creates filter properties, build form
*/
retrieveTaskFilterAndBuildForm() {
this.isLoading = true;
this.taskFilterCloudService.getTaskFilterById(this.appName, this.id)
.pipe(takeUntil(this.onDestroy$)).subscribe((response) => {
this.isLoading = false;
this.taskFilter = new TaskFilterCloudModel(response);
this.taskFilterProperties = this.createAndFilterProperties();
this.taskFilterActions = this.createAndFilterActions();
this.buildForm(this.taskFilterProperties);
}, (error) => {
this.isLoading = false;
});
}
createAndFilterProperties() {
this.checkMandatoryFilterProperties();
if (this.checkForApplicationNameProperty()) {
@@ -185,7 +206,6 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges {
this.getRunningApplications();
}
this.taskFilter = this.retrieveTaskFilter();
const defaultProperties = this.createTaskFilterProperties(this.taskFilter);
let filteredProperties = defaultProperties.filter((filterProperty: TaskFilterProperties) => this.isValidProperty(this.filterProperties, filterProperty));
@@ -196,7 +216,6 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges {
if (this.hasLastModifiedProperty()) {
filteredProperties = [...filteredProperties, ...this.createLastModifiedProperty()];
}
return filteredProperties;
}
@@ -285,15 +304,16 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges {
}
/**
* Check if both filters are same
* Return true if both filters are same
* @param editedQuery, @param currentQuery
*/
compareFilters(editedQuery, currentQuery): boolean {
compareFilters(editedQuery: TaskFilterCloudModel, currentQuery: TaskFilterCloudModel): boolean {
return JSON.stringify(editedQuery).toLowerCase() === JSON.stringify(currentQuery).toLowerCase();
}
getRunningApplications() {
this.appsProcessCloudService.getDeployedApplicationsByStatus(EditTaskFilterCloudComponent.APP_RUNNING_STATUS)
.subscribe((applications: ApplicationInstanceModel[]) => {
.pipe(takeUntil(this.onDestroy$)).subscribe((applications: ApplicationInstanceModel[]) => {
if (applications && applications.length > 0) {
applications.map((application) => {
this.applicationNames.push({ label: application.name, value: application.name });
@@ -313,16 +333,20 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges {
}
save(saveAction: TaskFilterAction) {
this.taskFilterCloudService.updateFilter(this.changedTaskFilter);
this.taskFilterCloudService.updateFilter(this.changedTaskFilter)
.pipe(takeUntil(this.onDestroy$)).subscribe((res) => {
saveAction.filter = this.changedTaskFilter;
this.action.emit(saveAction);
this.formHasBeenChanged = this.compareFilters(this.changedTaskFilter, this.taskFilter);
});
}
delete(deleteAction: TaskFilterAction) {
this.taskFilterCloudService.deleteFilter(this.taskFilter);
this.taskFilterCloudService.deleteFilter(this.taskFilter)
.pipe(takeUntil(this.onDestroy$)).subscribe((res) => {
deleteAction.filter = this.taskFilter;
this.action.emit(deleteAction);
});
}
saveAs(saveAsAction: TaskFilterAction) {
@@ -343,21 +367,30 @@ export class EditTaskFilterCloudComponent implements OnInit, OnChanges {
id: filterId,
key: 'custom-' + filterKey
};
const resultFilter = Object.assign({}, this.changedTaskFilter, newFilter);
this.taskFilterCloudService.addFilter(resultFilter);
const resultFilter: TaskFilterCloudModel = Object.assign({}, this.changedTaskFilter, newFilter);
this.taskFilterCloudService.addFilter(resultFilter)
.pipe(takeUntil(this.onDestroy$)).subscribe((res) => {
saveAsAction.filter = resultFilter;
this.action.emit(saveAsAction);
});
}
});
}
getSanitizeFilterName(filterName): string {
/**
* Return filter name
* @param filterName
*/
getSanitizeFilterName(filterName: string): string {
const nameWithHyphen = this.replaceSpaceWithHyphen(filterName.trim());
return nameWithHyphen.toLowerCase();
}
replaceSpaceWithHyphen(name) {
/**
* Return name with hyphen
* @param name
*/
replaceSpaceWithHyphen(name: string): string {
const regExt = new RegExp(' ', 'g');
return name.replace(regExt, '-');
}

View File

@@ -26,6 +26,7 @@ import { By } from '@angular/platform-browser';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { TaskFiltersCloudModule } from '../task-filters-cloud.module';
import { fakeGlobalFilter } from '../mock/task-filters-cloud.mock';
import { UserPreferenceCloudService } from '../../../services/user-preference.cloud.service';
describe('TaskFiltersCloudComponent', () => {
@@ -52,7 +53,7 @@ describe('TaskFiltersCloudComponent', () => {
setupTestBed({
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule],
providers: [TaskFilterCloudService]
providers: [TaskFilterCloudService, UserPreferenceCloudService]
});
beforeEach(() => {

View File

@@ -15,17 +15,19 @@
* limitations under the License.
*/
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { Observable } from 'rxjs';
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, OnDestroy } from '@angular/core';
import { Observable, Subject } from 'rxjs';
import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
import { TaskFilterCloudModel, FilterParamsModel } from '../models/filter-cloud.model';
import { TranslationService } from '@alfresco/adf-core';
import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'adf-cloud-task-filters',
templateUrl: './task-filters-cloud.component.html',
styleUrls: ['task-filters-cloud.component.scss']
})
export class TaskFiltersCloudComponent implements OnChanges {
export class TaskFiltersCloudComponent implements OnChanges, OnDestroy {
/** Display filters available to the current user for the application with the specified name. */
@Input()
appName: string;
@@ -59,6 +61,8 @@ export class TaskFiltersCloudComponent implements OnChanges {
filters: TaskFilterCloudModel [] = [];
private onDestroy$ = new Subject<boolean>();
constructor(private taskFilterCloudService: TaskFilterCloudService, private translationService: TranslationService) {
}
@@ -72,13 +76,18 @@ export class TaskFiltersCloudComponent implements OnChanges {
}
}
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
/**
* Return the filter list filtered by appName
*/
getFilters(appName: string) {
this.filters$ = this.taskFilterCloudService.getTaskListFilters(appName);
this.filters$.subscribe(
this.filters$.pipe(takeUntil(this.onDestroy$)).subscribe(
(res: TaskFilterCloudModel[]) => {
this.resetFilter();
this.filters = Object.assign([], res);

View File

@@ -67,3 +67,167 @@ export let fakeAllTaskFilter = new TaskFilterCloudModel({
order: 'ASC',
sort: 'id'
});
export const fakeTaskCloudPreferenceList = {
list: {
entries: [
{
entry: {
key: 'task-filters-fakeAppName-mock-username',
value: JSON.stringify([
{
name: 'FAKE_TASK_1',
id: '1',
key: 'all-fake-task',
icon: 'adjust',
appName: 'fakeAppName',
sort: 'startDate',
status: 'ALL',
order: 'DESC'
},
{
name: 'FAKE_TASK_2',
id: '2',
key: 'run-fake-task',
icon: 'adjust',
appName: 'fakeAppName',
sort: 'startDate',
status: 'RUNNING',
order: 'DESC'
},
{
name: 'FAKE_TASK_3',
id: '3',
key: 'complete-fake-task',
icon: 'adjust',
appName: 'fakeAppName',
sort: 'startDate',
status: 'COMPLETED',
order: 'DESC'
}
])
}
},
{
entry: {
key: 'mock-key-2',
value: {
name: 'FAKE_TASK_2',
id: '2',
key: 'run-fake-task',
icon: 'adjust',
appName: 'fakeAppName',
sort: 'startDate',
status: 'RUNNING',
order: 'DESC'
}
}
},
{
entry: {
key: 'mock-key-3',
value: {
name: 'FAKE_TASK_3',
id: '3',
key: 'complete-fake-task',
icon: 'adjust',
appName: 'fakeAppName',
sort: 'startDate',
status: 'COMPLETED`',
order: 'DESC'
}
}
}
],
pagination: {
skipCount: 0,
maxItems: 100,
count: 3,
hasMoreItems: false,
totalItems: 3
}
}
};
export const fakeEmptyTaskCloudPreferenceList = {
list: {
entries: [],
pagination: {
skipCount: 0,
maxItems: 100,
count: 0,
hasMoreItems: false,
totalItems: 0
}
}
};
export const fakePreferenceWithNoTaskFilterPreference = {
list: {
entries: [
{
entry: {
key: 'my-mock-key-1',
value: 'my-mock-value-2'
}
},
{
entry: {
key: 'my-mock-key-2',
value: 'my-mock-key-2'
}
}
],
pagination: {
skipCount: 0,
maxItems: 100,
count: 4,
hasMoreItems: false,
totalItems: 2
}
}
};
export const fakeTaskFilter = new TaskFilterCloudModel({
name: 'FAKE_TASK_1',
id: '1',
key: 'all-fake-task',
icon: 'adjust',
appName: 'fakeAppName',
sort: 'startDate',
order: 'DESC',
status: 'ALL'
});
export const fakeTaskCloudFilters = [
{
name: 'FAKE_TASK_1',
id: '1',
key: 'all-fake-task',
icon: 'adjust',
appName: 'fakeAppName',
sort: 'startDate',
status: 'ALL',
order: 'DESC'
},
{
name: 'FAKE_TASK_2',
id: '2',
key: 'run-fake-task',
icon: 'adjust',
appName: 'fakeAppName',
sort: 'startDate',
status: 'RUNNING',
order: 'DESC'
},
{
name: 'FAKE_TASK_3',
id: '3',
key: 'complete-fake-task',
icon: 'adjust',
appName: 'fakeAppName',
sort: 'startDate',
status: 'COMPLETED',
order: 'DESC'
}
];

View File

@@ -0,0 +1,210 @@
/*!
* @license
* Copyright 2019 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async, TestBed } from '@angular/core/testing';
import { setupTestBed, CoreModule, IdentityUserService } from '@alfresco/adf-core';
import { of } from 'rxjs';
import { TaskFilterCloudService } from './task-filter-cloud.service';
import { UserPreferenceCloudService } from '../../../services/user-preference.cloud.service';
import {
fakeTaskCloudPreferenceList,
fakeTaskCloudFilters,
fakeEmptyTaskCloudPreferenceList,
fakePreferenceWithNoTaskFilterPreference,
fakeTaskFilter
} from '../mock/task-filters-cloud.mock';
describe('Task Filter Cloud Service', () => {
let service: TaskFilterCloudService;
let userPreferenceCloudService: UserPreferenceCloudService;
let identityUserService: IdentityUserService;
let getPreferencesSpy: jasmine.Spy;
let getPreferenceByKeySpy: jasmine.Spy;
let createPreferenceSpy: jasmine.Spy;
let updatePreferenceSpy: jasmine.Spy;
let getCurrentUserInfoSpy: jasmine.Spy;
const identityUserMock = { username: 'fakeusername', firstName: 'fake-identity-first-name', lastName: 'fake-identity-last-name', email: 'fakeIdentity@email.com' };
setupTestBed({
imports: [
CoreModule.forRoot()
],
providers: [TaskFilterCloudService, UserPreferenceCloudService, IdentityUserService]
});
beforeEach(async(() => {
service = TestBed.get(TaskFilterCloudService);
userPreferenceCloudService = TestBed.get(UserPreferenceCloudService);
identityUserService = TestBed.get(IdentityUserService);
createPreferenceSpy = spyOn(userPreferenceCloudService, 'createPreference').and.returnValue(of(fakeTaskCloudFilters));
updatePreferenceSpy = spyOn(userPreferenceCloudService, 'updatePreference').and.returnValue(of(fakeTaskCloudFilters));
getPreferencesSpy = spyOn(userPreferenceCloudService, 'getPreferences').and.returnValue(of(fakeTaskCloudPreferenceList));
getPreferenceByKeySpy = spyOn(userPreferenceCloudService, 'getPreferenceByKey').and.returnValue(of(fakeTaskCloudFilters));
getCurrentUserInfoSpy = spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(identityUserMock);
}));
it('should create TaskFilterCloudService instance', () => {
expect(service).toBeDefined();
});
it('should create task filter key by using appName and the username', (done) => {
service.getTaskListFilters('fakeAppName').subscribe((res: any) => {
expect(res).toBeDefined();
expect(getCurrentUserInfoSpy).toHaveBeenCalled();
done();
});
});
it('should create default task filters if there are no task filter preferences', (done) => {
getPreferencesSpy.and.returnValue(of(fakeEmptyTaskCloudPreferenceList));
service.getTaskListFilters('fakeAppName').subscribe((res: any) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.length).toBe(3);
expect(res[0].appName).toBe('fakeAppName');
expect(res[0].id).toBe('1');
expect(res[0].name).toBe('FAKE_TASK_1');
expect(res[0].status).toBe('ALL');
expect(res[1].appName).toBe('fakeAppName');
expect(res[1].id).toBe('2');
expect(res[1].name).toBe('FAKE_TASK_2');
expect(res[1].status).toBe('RUNNING');
expect(res[2].appName).toBe('fakeAppName');
expect(res[2].id).toBe('3');
expect(res[2].name).toBe('FAKE_TASK_3');
expect(res[2].status).toBe('COMPLETED');
done();
});
expect(createPreferenceSpy).toHaveBeenCalled();
});
it('should return the task filters if filters available', (done) => {
service.getTaskListFilters('fakeAppName').subscribe((res: any) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.length).toBe(3);
expect(res[0].appName).toBe('fakeAppName');
expect(res[0].id).toBe('1');
expect(res[0].name).toBe('FAKE_TASK_1');
expect(res[0].status).toBe('ALL');
expect(res[1].appName).toBe('fakeAppName');
expect(res[1].id).toBe('2');
expect(res[1].name).toBe('FAKE_TASK_2');
expect(res[1].status).toBe('RUNNING');
expect(res[2].appName).toBe('fakeAppName');
expect(res[2].id).toBe('3');
expect(res[2].name).toBe('FAKE_TASK_3');
expect(res[2].status).toBe('COMPLETED');
done();
});
expect(getPreferencesSpy).toHaveBeenCalled();
});
it('should create the task filters if the user preference does not have task filters', (done) => {
getPreferencesSpy.and.returnValue(of(fakePreferenceWithNoTaskFilterPreference));
service.getTaskListFilters('fakeAppName').subscribe((res: any) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.length).toBe(3);
expect(res[0].appName).toBe('fakeAppName');
expect(res[0].id).toBe('1');
expect(res[0].name).toBe('FAKE_TASK_1');
expect(res[0].status).toBe('ALL');
expect(res[1].appName).toBe('fakeAppName');
expect(res[1].id).toBe('2');
expect(res[1].name).toBe('FAKE_TASK_2');
expect(res[1].status).toBe('RUNNING');
expect(res[2].appName).toBe('fakeAppName');
expect(res[2].id).toBe('3');
expect(res[2].name).toBe('FAKE_TASK_3');
expect(res[2].status).toBe('COMPLETED');
done();
});
expect(getPreferencesSpy).toHaveBeenCalled();
expect(createPreferenceSpy).toHaveBeenCalled();
});
it('should return filter by task filter id', (done) => {
service.getTaskFilterById('fakeAppName', '2').subscribe((res: any) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.appName).toBe('fakeAppName');
expect(res.id).toBe('2');
expect(res.name).toBe('FAKE_TASK_2');
expect(res.status).toBe('RUNNING');
done();
});
expect(getPreferenceByKeySpy).toHaveBeenCalled();
});
it('should add task filter if filter is not exist in the filters', (done) => {
getPreferenceByKeySpy.and.returnValue(of([]));
service.getTaskFilterById('fakeAppName', '2').subscribe((res: any) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.appName).toBe('fakeAppName');
expect(res.id).toBe('2');
expect(res.name).toBe('FAKE_TASK_2');
expect(res.status).toBe('RUNNING');
done();
});
});
it('should update filter', (done) => {
createPreferenceSpy.and.returnValue(of(fakeTaskCloudFilters));
service.updateFilter(fakeTaskFilter).subscribe((res: any) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.length).toBe(3);
expect(res[0].appName).toBe('fakeAppName');
expect(res[1].appName).toBe('fakeAppName');
expect(res[2].appName).toBe('fakeAppName');
done();
});
});
it('should create task filter when trying to update in case filter is not exist in the filters', (done) => {
getPreferenceByKeySpy.and.returnValue(of([]));
service.updateFilter(fakeTaskFilter).subscribe((res: any) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.length).toBe(3);
expect(res[0].appName).toBe('fakeAppName');
expect(res[1].appName).toBe('fakeAppName');
expect(res[2].appName).toBe('fakeAppName');
done();
});
expect(createPreferenceSpy).toHaveBeenCalled();
});
it('should delete filter', (done) => {
service.deleteFilter(fakeTaskFilter).subscribe((res: any) => {
expect(res).toBeDefined();
done();
});
expect(updatePreferenceSpy).toHaveBeenCalled();
});
});

View File

@@ -15,10 +15,12 @@
* limitations under the License.
*/
import { StorageService, JwtHelperService } from '@alfresco/adf-core';
import { IdentityUserService, IdentityUserModel } from '@alfresco/adf-core';
import { Injectable } from '@angular/core';
import { Observable, BehaviorSubject } from 'rxjs';
import { Observable, of, BehaviorSubject, throwError } from 'rxjs';
import { TaskFilterCloudModel } from '../models/filter-cloud.model';
import { UserPreferenceCloudService } from '../../../services/public-api';
import { switchMap, map, catchError } from 'rxjs/operators';
@Injectable()
export class TaskFilterCloudService {
@@ -26,38 +28,85 @@ export class TaskFilterCloudService {
private filtersSubject: BehaviorSubject<TaskFilterCloudModel[]>;
filters$: Observable<TaskFilterCloudModel[]>;
constructor(private storage: StorageService, private jwtHelperService: JwtHelperService) {
constructor( private identityUserService: IdentityUserService,
private preferenceService: UserPreferenceCloudService) {
this.filtersSubject = new BehaviorSubject([]);
this.filters$ = this.filtersSubject.asObservable();
}
/**
* Creates and returns the default filters for a process app.
* Creates and returns the default task filters for an app.
* @param appName Name of the target app
* @returns Observable of default filters just created
* @returns Observable of default filters task filters just created or created filters
*/
private createDefaultFilters(appName: string) {
const myTasksFilter = this.getMyTasksFilterInstance(appName);
this.addFilter(myTasksFilter);
const completedTasksFilter = this.getCompletedTasksFilterInstance(appName);
this.addFilter(completedTasksFilter);
const key: string = this.prepareKey(appName);
this.preferenceService.getPreferences(appName).pipe(
switchMap((response: any) => {
const preferences = (response && response.list && response.list.entries) ? response.list.entries : [];
if (!this.hasPreferences(preferences)) {
return this.createTaskFilters(appName, key, this.defaultTaskFilters(appName));
} else if (!this.hasTaskFilters(preferences, key)) {
return this.createTaskFilters(appName, key, this.defaultTaskFilters(appName));
} else {
return of(this.findFiltersByKeyInPrefrences(preferences, key));
}
}),
catchError((err) => this.handleTaskError(err))
).subscribe((filters) => {
this.addFiltersToStream(filters);
});
}
/**
* Gets all task filters for a process app.
* Checks user preference are empty or not
* @param preferences User preferences of the target app
* @returns Boolean value if the preferences are not empty
*/
private hasPreferences(preferences: any): boolean {
return preferences && preferences.length > 0;
}
/**
* Checks for task filters in given user preferences
* @param preferences User preferences of the target app
* @param key Key of the task filters
* @param filters Details of create filter
* @returns Boolean value if the preference has task filters
*/
private hasTaskFilters(preferences: any, key: string): boolean {
const filters = preferences.find((filter: any) => { return filter.entry.key === key; });
return (filters && filters.entry) ? JSON.parse(filters.entry.value).length > 0 : false;
}
/**
* Calls create preference api to create task filters
* @param appName Name of the target app
* @param key Key of the task instance filters
* @param filters Details of new task filter
* @returns Observable of created task filters
*/
private createTaskFilters(appName: string, key: string, filters: TaskFilterCloudModel[]): Observable<TaskFilterCloudModel[]> {
return this.preferenceService.createPreference(appName, key, filters);
}
/**
* Calls get preference api to get task filter by preference key
* @param appName Name of the target app
* @param key Key of the task filters
* @returns Observable of task filters
*/
private getTaskFiltersByKey(appName: string, key: string): Observable<TaskFilterCloudModel[]> {
return this.preferenceService.getPreferenceByKey(appName, key);
}
/**
* Gets all task filters for a task app.
* @param appName Name of the target app
* @returns Observable of task filter details
*/
getTaskListFilters(appName?: string): Observable<TaskFilterCloudModel[]> {
const username = this.getUsername();
const key = `task-filters-${appName}-${username}`;
const filters = JSON.parse(this.storage.getItem(key) || '[]');
if (filters.length === 0) {
this.createDefaultFilters(appName);
} else {
this.addFiltersToStream(filters);
}
return this.filters$;
}
@@ -67,29 +116,47 @@ export class TaskFilterCloudService {
* @param id ID of the task
* @returns Details of the task filter
*/
getTaskFilterById(appName: string, id: string): TaskFilterCloudModel {
const username = this.getUsername();
const key = `task-filters-${appName}-${username}`;
let filters = [];
filters = JSON.parse(this.storage.getItem(key)) || [];
return filters.filter((filterTmp: TaskFilterCloudModel) => id === filterTmp.id)[0];
getTaskFilterById(appName: string, id: string): any {
const key: string = this.prepareKey(appName);
return this.getTaskFiltersByKey(appName, key).pipe(
switchMap((filters: TaskFilterCloudModel[]) => {
if (filters && filters.length === 0) {
return this.createTaskFilters(appName, key, this.defaultTaskFilters(appName));
} else {
return of(filters);
}
}),
map((filters: TaskFilterCloudModel[]) => {
return filters.filter((filter: TaskFilterCloudModel) => {
return filter.id === id;
})[0];
}),
catchError((err) => this.handleTaskError(err))
);
}
/**
* Adds a new task filter.
* @param filter The new filter to add
* @returns Details of task filter just added
* @returns Obervable of task instance filters with newly added filter
*/
addFilter(filter: TaskFilterCloudModel) {
const username = this.getUsername();
const key = `task-filters-${filter.appName}-${username}`;
const filters = JSON.parse(this.storage.getItem(key) || '[]');
filters.push(filter);
this.storage.setItem(key, JSON.stringify(filters));
addFilter(newFilter: TaskFilterCloudModel) {
const key: string = this.prepareKey(newFilter.appName);
return this.getTaskFiltersByKey(newFilter.appName, key).pipe(
switchMap((filters: TaskFilterCloudModel[]) => {
if (filters && filters.length === 0) {
return this.createTaskFilters(newFilter.appName, key, [newFilter]);
} else {
filters.push(newFilter);
return this.preferenceService.updatePreference(newFilter.appName, key, filters);
}
}),
map((filters: TaskFilterCloudModel[]) => {
this.addFiltersToStream(filters);
return filters;
}),
catchError((err) => this.handleTaskError(err))
);
}
private addFiltersToStream(filters: TaskFilterCloudModel[]) {
@@ -99,36 +166,59 @@ export class TaskFilterCloudService {
/**
* Updates a task filter.
* @param filter The filter to update
* @returns Observable of task instance filters with updated filter
*/
updateFilter(filter: TaskFilterCloudModel) {
const username = this.getUsername();
const key = `task-filters-${filter.appName}-${username}`;
if (key) {
const filters = JSON.parse(this.storage.getItem(key) || '[]');
const itemIndex = filters.findIndex((flt: TaskFilterCloudModel) => flt.id === filter.id);
filters[itemIndex] = filter;
this.storage.setItem(key, JSON.stringify(filters));
this.addFiltersToStream(filters);
updateFilter(updatedFilter: TaskFilterCloudModel): Observable<TaskFilterCloudModel[]> {
const key: string = this.prepareKey(updatedFilter.appName);
return this.getTaskFiltersByKey(updatedFilter.appName, key).pipe(
switchMap((filters: any) => {
if (filters && filters.length === 0) {
return this.createTaskFilters(updatedFilter.appName, key, [updatedFilter]);
} else {
const itemIndex = filters.findIndex((filter: TaskFilterCloudModel) => filter.id === updatedFilter.id);
filters[itemIndex] = updatedFilter;
return this.updateProcessFilters(updatedFilter.appName, key, filters);
}
}),
map((updatedFilters: TaskFilterCloudModel[]) => {
this.addFiltersToStream(updatedFilters);
return updatedFilters;
}),
catchError((err) => this.handleTaskError(err))
);
}
/**
* Deletes a task filter
* @param filter The filter to delete
* @returns Observable of task instance filters without deleted filter
*/
deleteFilter(filter: TaskFilterCloudModel) {
const username = this.getUsername();
const key = `task-filters-${filter.appName}-${username}`;
if (key) {
let filters: TaskFilterCloudModel[] = JSON.parse(this.storage.getItem(key) || '[]');
filters = filters.filter((item) => item.id !== filter.id);
this.storage.setItem(key, JSON.stringify(filters));
if (filters.length === 0) {
this.createDefaultFilters(filter.appName);
} else {
deleteFilter(deletedFilter: TaskFilterCloudModel): Observable<TaskFilterCloudModel[]> {
const key = this.prepareKey(deletedFilter.appName);
return this.getTaskFiltersByKey(deletedFilter.appName, key).pipe(
switchMap((filters: any) => {
if (filters && filters.length > 0) {
filters = filters.filter((filter: TaskFilterCloudModel) => filter.id !== deletedFilter.id);
return this.updateProcessFilters(deletedFilter.appName, key, filters);
}
}),
map((filters: TaskFilterCloudModel[]) => {
this.addFiltersToStream(filters);
return filters;
}),
catchError((err) => this.handleTaskError(err))
);
}
}
/**
* Calls update preference api to update task filter
* @param appName Name of the target app
* @param key Key of the task filters
* @param filters Details of update filter
* @returns Observable of updated task filters
*/
private updateProcessFilters(appName: string, key: string, filters: TaskFilterCloudModel[]): Observable<TaskFilterCloudModel[]> {
return this.preferenceService.updatePreference(appName, key, filters);
}
/**
@@ -136,35 +226,51 @@ export class TaskFilterCloudService {
* @returns Username string
*/
getUsername(): string {
return this.jwtHelperService.getValueFromLocalAccessToken<string>(JwtHelperService.USER_PREFERRED_USERNAME);
const user: IdentityUserModel = this.identityUserService.getCurrentUserInfo();
return user.username;
}
/**
* Creates and returns a filter for "My Tasks" task instances.
* Creates a uniq key with appName and username
* @param appName Name of the target app
* @returns The newly created filter
* @returns String of task filters preference key
*/
getMyTasksFilterInstance(appName: string): TaskFilterCloudModel {
const username = this.getUsername();
return new TaskFilterCloudModel({
private prepareKey(appName: string): string {
return `task-filters-${appName}-${this.getUsername()}`;
}
/**
* Finds and returns the task filters from preferences
* @param appName Name of the target app
* @returns Array of TaskFilterCloudModel
*/
private findFiltersByKeyInPrefrences(preferences: any, key: string): TaskFilterCloudModel[] {
const result = preferences.find((filter: any) => { return filter.entry.key === key; });
return result && result.entry ? JSON.parse(result.entry.value) : [];
}
private handleTaskError(error: any) {
return throwError(error || 'Server error');
}
/**
* Creates and returns the default filters for a task app.
* @param appName Name of the target app
* @returns Array of TaskFilterCloudModel
*/
private defaultTaskFilters(appName: string): TaskFilterCloudModel[] {
return [
new TaskFilterCloudModel({
name: 'ADF_CLOUD_TASK_FILTERS.MY_TASKS',
key: 'my-tasks',
icon: 'inbox',
appName: appName,
status: 'ASSIGNED',
assignee: username,
assignee: this.getUsername(),
sort: 'createdDate',
order: 'DESC'
});
}
/**
* Creates and returns a filter for "Completed" task instances.
* @param appName Name of the target app
* @returns The newly created filter
*/
getCompletedTasksFilterInstance(appName: string): TaskFilterCloudModel {
return new TaskFilterCloudModel({
}),
new TaskFilterCloudModel({
name: 'ADF_CLOUD_TASK_FILTERS.COMPLETED_TASKS',
key: 'completed-tasks',
icon: 'done',
@@ -173,6 +279,7 @@ export class TaskFilterCloudService {
assignee: '',
sort: 'createdDate',
order: 'DESC'
});
})
];
}
}