From 11ec9e7df533948805ac72f790326e126d432fad Mon Sep 17 00:00:00 2001 From: Nacho Date: Mon, 30 Jul 2018 16:39:38 +0200 Subject: [PATCH] TSK-621 - Make classification categories configurable by type --- .../TaskanaEngineConfiguration.java | 35 ++++++++---- .../impl/ClassificationServiceImpl.java | 4 +- .../config/TaskanaConfigAccTest.java | 12 ++-- .../src/test/resources/taskana.properties | 3 +- .../src/main/resources/taskana.properties | 3 +- .../src/main/resources/taskana.properties | 3 +- .../rest/TaskanaEngineControllerIntTest.java | 2 +- rest/taskana-rest-spring/pom.xml | 2 +- .../taskana/rest/TaskanaEngineController.java | 7 ++- .../administration/administration.module.ts | 2 - .../classification-details.component.spec.ts | 9 +-- .../classification-details.component.ts | 8 +-- .../classification-list.component.spec.ts | 8 +-- .../list/classification-list.component.ts | 16 +++--- .../classification-categories.service.ts | 57 ++++++++++++++----- .../classification-types.service.spec.ts | 17 ------ .../classification-types.service.ts | 40 ------------- .../classifications.service.ts | 9 ++- .../custom-fields.service.spec.ts | 38 +++++++++++++ .../custom-fields/custom-fields.service.ts | 20 +++++++ .../taskana-customization-test.json | 22 +++++++ .../data-sources/taskana-customization.json | 2 +- 22 files changed, 190 insertions(+), 129 deletions(-) delete mode 100644 web/src/app/administration/services/classification-types/classification-types.service.spec.ts delete mode 100644 web/src/app/administration/services/classification-types/classification-types.service.ts create mode 100644 web/src/app/services/custom-fields/taskana-customization-test.json diff --git a/lib/taskana-core/src/main/java/pro/taskana/configuration/TaskanaEngineConfiguration.java b/lib/taskana-core/src/main/java/pro/taskana/configuration/TaskanaEngineConfiguration.java index 46776a0f5..4ee187bff 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/configuration/TaskanaEngineConfiguration.java +++ b/lib/taskana-core/src/main/java/pro/taskana/configuration/TaskanaEngineConfiguration.java @@ -95,8 +95,7 @@ public class TaskanaEngineConfiguration { // List of configured classification types protected List classificationTypes = new ArrayList(); - // List of configured classification categories - protected List classificationCategories = new ArrayList(); + protected Map> classificationCategoriesByTypeMap = new HashMap>(); public TaskanaEngineConfiguration(DataSource dataSource, boolean useManagedTransactions, String schemaName) throws SQLException { @@ -234,11 +233,20 @@ public class TaskanaEngineConfiguration { } private void initClassificationCategories(Properties props) { - String classificationCategoryNames = props.getProperty(TASKANA_CLASSIFICATION_CATEGORIES_PROPERTY); - if (classificationCategoryNames != null && !classificationCategoryNames.isEmpty()) { - StringTokenizer st = new StringTokenizer(classificationCategoryNames, ","); - while (st.hasMoreTokens()) { - classificationCategories.add(st.nextToken().trim().toUpperCase()); + if (classificationTypes != null && !classificationTypes.isEmpty()) { + String classificationCategoryNames; + StringTokenizer st; + List classificationCategoriesAux; + for (String type : classificationTypes) { + classificationCategoriesAux = new ArrayList<>(); + classificationCategoryNames = props.getProperty(TASKANA_CLASSIFICATION_CATEGORIES_PROPERTY + "." + type.toLowerCase()); + if (classificationCategoryNames != null && !classificationCategoryNames.isEmpty()) { + st = new StringTokenizer(classificationCategoryNames, ","); + while (st.hasMoreTokens()) { + classificationCategoriesAux.add(st.nextToken().trim().toUpperCase()); + } + classificationCategoriesByTypeMap.put(type, classificationCategoriesAux); + } } } LOGGER.debug("Configured classification categories : {}", domains); @@ -432,12 +440,19 @@ public class TaskanaEngineConfiguration { this.classificationTypes = classificationTypes; } - public List getClassificationCategories() { + public List getAllClassificationCategories() { + List classificationCategories = new ArrayList<>(); + for (Map.Entry> type : this.classificationCategoriesByTypeMap.entrySet()) { + classificationCategories.addAll(type.getValue()); + } return classificationCategories; } - public void setClassificationCategories(List classificationCategories) { - this.classificationCategories = classificationCategories; + public List getClassificationCategoriesByType(String type) { + return classificationCategoriesByTypeMap.get(type); + } + public void setClassificationCategoriesByType(Map> classificationCategoriesByType) { + this.classificationCategoriesByTypeMap = classificationCategoriesByType; } public Instant getTaskCleanupJobFirstRun() { diff --git a/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationServiceImpl.java b/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationServiceImpl.java index 4104f8785..fa5549fe2 100644 --- a/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationServiceImpl.java +++ b/lib/taskana-core/src/main/java/pro/taskana/impl/ClassificationServiceImpl.java @@ -290,8 +290,8 @@ public class ClassificationServiceImpl implements ClassificationService { } if (classification.getCategory() != null - && !taskanaEngine.getConfiguration().getClassificationCategories().contains(classification.getCategory())) { - throw new InvalidArgumentException("Given classification category " + classification.getCategory() + && !taskanaEngine.getConfiguration().getClassificationCategoriesByType(classification.getType()).contains(classification.getCategory())) { + throw new InvalidArgumentException("Given classification category " + classification.getCategory() + " with type " + classification.getType() + " is not valid according to the configuration."); } diff --git a/lib/taskana-core/src/test/java/acceptance/config/TaskanaConfigAccTest.java b/lib/taskana-core/src/test/java/acceptance/config/TaskanaConfigAccTest.java index 49efe6e7b..c257049ce 100644 --- a/lib/taskana-core/src/test/java/acceptance/config/TaskanaConfigAccTest.java +++ b/lib/taskana-core/src/test/java/acceptance/config/TaskanaConfigAccTest.java @@ -42,12 +42,12 @@ public class TaskanaConfigAccTest extends TaskanaEngineImpl { @Test public void testClassificationCategories() { - assertEquals(4, getConfiguration().getClassificationCategories().size()); - assertTrue(getConfiguration().getClassificationCategories().contains("EXTERNAL")); - assertTrue(getConfiguration().getClassificationCategories().contains("MANUAL")); - assertTrue(getConfiguration().getClassificationCategories().contains("AUTOMATIC")); - assertTrue(getConfiguration().getClassificationCategories().contains("PROCESS")); - assertFalse(getConfiguration().getClassificationCategories().contains("manual")); + assertEquals(4, getConfiguration().getClassificationCategoriesByType("TASK").size()); + assertTrue(getConfiguration().getClassificationCategoriesByType("TASK").contains("EXTERNAL")); + assertTrue(getConfiguration().getClassificationCategoriesByType("TASK").contains("MANUAL")); + assertTrue(getConfiguration().getClassificationCategoriesByType("TASK").contains("AUTOMATIC")); + assertTrue(getConfiguration().getClassificationCategoriesByType("TASK").contains("PROCESS")); + assertFalse(getConfiguration().getClassificationCategoriesByType("TASK").contains("manual")); } } diff --git a/lib/taskana-core/src/test/resources/taskana.properties b/lib/taskana-core/src/test/resources/taskana.properties index f14437f86..6f4bc5198 100644 --- a/lib/taskana-core/src/test/resources/taskana.properties +++ b/lib/taskana-core/src/test/resources/taskana.properties @@ -6,7 +6,8 @@ taskana.roles.monitor=john|teamlead_2 | monitor taskana.domains= Domain_A , DOMAIN_B taskana.classification.types= TASK , document -taskana.classification.categories= EXTERNAL , manual, autoMAtic ,Process +taskana.classification.categories.task= EXTERNAL, manual, autoMAtic, Process +taskana.classification.categories.document= EXTERNAL taskana.jobs.maxRetries=3 taskana.jobs.batchSize=50 diff --git a/lib/taskana-spring-example/src/main/resources/taskana.properties b/lib/taskana-spring-example/src/main/resources/taskana.properties index 6d8e72398..997700533 100644 --- a/lib/taskana-spring-example/src/main/resources/taskana.properties +++ b/lib/taskana-spring-example/src/main/resources/taskana.properties @@ -4,7 +4,8 @@ taskana.roles.businessadmin=max|Moritz|businessadmin taskana.domains= Domain_A , DOMAIN_B taskana.classification.types= TASK , document -taskana.classification.categories= EXTERNAL , manual, autoMAtic ,Process +taskana.classification.categories.task= EXTERNAL, manual, autoMAtic, Process +taskana.classification.categories.document= EXTERNAL taskana.jobs.cleanup.schedule=0 0 3 * * * taskana.jobs.cleanup.runEvery=P1D diff --git a/rest/taskana-rest-spring-example/src/main/resources/taskana.properties b/rest/taskana-rest-spring-example/src/main/resources/taskana.properties index 86241d8de..abcffeb30 100644 --- a/rest/taskana-rest-spring-example/src/main/resources/taskana.properties +++ b/rest/taskana-rest-spring-example/src/main/resources/taskana.properties @@ -4,7 +4,8 @@ taskana.roles.businessadmin=max|Moritz|businessadmin taskana.roles.monitor=john|teamlead_2 | monitor taskana.domains=DOMAIN_A,DOMAIN_B,DOMAIN_C taskana.classification.types=TASK,DOCUMENT -taskana.classification.categories= EXTERNAL , manual, autoMAtic ,Process +taskana.classification.categories.task= EXTERNAL, manual, autoMAtic, Process +taskana.classification.categories.document= EXTERNAL taskana.jobs.maxRetries=3 taskana.jobs.batchSize=50 diff --git a/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/TaskanaEngineControllerIntTest.java b/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/TaskanaEngineControllerIntTest.java index 1dbf4008f..65848b707 100644 --- a/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/TaskanaEngineControllerIntTest.java +++ b/rest/taskana-rest-spring-example/src/test/java/pro/taskana/rest/TaskanaEngineControllerIntTest.java @@ -73,7 +73,7 @@ public class TaskanaEngineControllerIntTest { headers.add("Authorization", "Basic dGVhbWxlYWRfMTp0ZWFtbGVhZF8x"); HttpEntity request = new HttpEntity(headers); ResponseEntity> response = template.exchange( - "http://127.0.0.1:" + port + "/v1/classification-categories", HttpMethod.GET, request, + "http://127.0.0.1:" + port + "/v1/classification-categories/?type=TASK", HttpMethod.GET, request, new ParameterizedTypeReference>() { }); assertTrue(response.getBody().contains("MANUAL")); diff --git a/rest/taskana-rest-spring/pom.xml b/rest/taskana-rest-spring/pom.xml index 9a974f1cd..7562635d2 100644 --- a/rest/taskana-rest-spring/pom.xml +++ b/rest/taskana-rest-spring/pom.xml @@ -214,7 +214,7 @@ commons-logging - + diff --git a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskanaEngineController.java b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskanaEngineController.java index c0ac360f7..5e1e344cf 100644 --- a/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskanaEngineController.java +++ b/rest/taskana-rest-spring/src/main/java/pro/taskana/rest/TaskanaEngineController.java @@ -34,8 +34,11 @@ public class TaskanaEngineController { } @GetMapping(path = "/v1/classification-categories", produces = {MediaType.APPLICATION_JSON_VALUE}) - public ResponseEntity> getClassificationCategories() { - return new ResponseEntity<>(taskanaEngineConfiguration.getClassificationCategories(), HttpStatus.OK); + public ResponseEntity> getClassificationCategories(String type) { + if (type != null) { + return new ResponseEntity<>(taskanaEngineConfiguration.getClassificationCategoriesByType(type), HttpStatus.OK); + } + return new ResponseEntity<>(taskanaEngineConfiguration.getAllClassificationCategories(), HttpStatus.OK); } @GetMapping(path = "/v1/classification-types", produces = {MediaType.APPLICATION_JSON_VALUE}) diff --git a/web/src/app/administration/administration.module.ts b/web/src/app/administration/administration.module.ts index a7a81f3de..4edc61f8b 100644 --- a/web/src/app/administration/administration.module.ts +++ b/web/src/app/administration/administration.module.ts @@ -30,7 +30,6 @@ import {SavingWorkbasketService} from './services/saving-workbaskets/saving-work import {ClassificationDefinitionService} from './services/classification-definition/classification-definition.service'; import {WorkbasketDefinitionService} from './services/workbasket-definition/workbasket-definition.service'; import {ClassificationsService} from './services/classifications/classifications.service'; -import {ClassificationTypesService} from './services/classification-types/classification-types.service'; import {ClassificationCategoriesService} from './services/classification-categories-service/classification-categories.service'; const MODULES = [ @@ -68,7 +67,6 @@ const DECLARATIONS = [ WorkbasketDefinitionService, SavingWorkbasketService, ClassificationsService, - ClassificationTypesService, ClassificationCategoriesService, ] }) diff --git a/web/src/app/administration/classification/details/classification-details.component.spec.ts b/web/src/app/administration/classification/details/classification-details.component.spec.ts index 6a4edec87..4ec76e5a3 100644 --- a/web/src/app/administration/classification/details/classification-details.component.spec.ts +++ b/web/src/app/administration/classification/details/classification-details.component.spec.ts @@ -24,11 +24,9 @@ import { TreeNodeModel } from 'app/models/tree-node'; import { ErrorModalService } from 'app/services/errorModal/error-modal.service'; import { AlertService } from 'app/services/alert/alert.service'; import { TreeService } from 'app/services/tree/tree.service'; -import { ClassificationTypesService } from 'app/administration/services/classification-types/classification-types.service'; import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service'; import { RemoveConfirmationService } from 'app/services/remove-confirmation/remove-confirmation.service'; - @Component({ selector: 'taskana-dummy-detail', template: 'dummydetail' @@ -45,7 +43,7 @@ describe('ClassificationDetailsComponent', () => { let fixture: ComponentFixture; const treeNodes: Array = new Array(new TreeNodeModel()); - let classificationsService, classificationTypesService, classificationCategoriesService, + let classificationsService, classificationCategoriesService, treeService, removeConfirmationService; beforeEach(done => { @@ -54,7 +52,7 @@ describe('ClassificationDetailsComponent', () => { imports: [FormsModule, HttpClientModule, RouterTestingModule.withRoutes(routes), AngularSvgIconModule], declarations: [ClassificationDetailsComponent, DummyDetailComponent], providers: [MasterAndDetailService, RequestInProgressService, ClassificationsService, HttpClient, ErrorModalService, AlertService, - TreeService, ClassificationTypesService, ClassificationCategoriesService, + TreeService, ClassificationCategoriesService, CustomFieldsService] }) }; @@ -62,12 +60,11 @@ describe('ClassificationDetailsComponent', () => { fixture = TestBed.createComponent(ClassificationDetailsComponent); component = fixture.componentInstance; classificationsService = TestBed.get(ClassificationsService); - classificationTypesService = TestBed.get(ClassificationTypesService); classificationCategoriesService = TestBed.get(ClassificationCategoriesService); classificationsService = TestBed.get(ClassificationsService); removeConfirmationService = TestBed.get(RemoveConfirmationService); spyOn(classificationsService, 'getClassifications').and.returnValue(of(treeNodes)); - spyOn(classificationTypesService, 'getClassificationTypes').and.returnValue(of([])); + spyOn(classificationCategoriesService, 'getClassificationTypes').and.returnValue(of([])); spyOn(classificationCategoriesService, 'getCategories').and.returnValue(of(['firstCategory', 'secondCategory'])); spyOn(classificationsService, 'deleteClassification').and.returnValue(of(true)); spyOn(classificationCategoriesService, 'getCategoryIcon').and.returnValue(new Pair('assets/icons/categories/external.svg')); diff --git a/web/src/app/administration/classification/details/classification-details.component.ts b/web/src/app/administration/classification/details/classification-details.component.ts index c9abcfc5a..9c32b1229 100644 --- a/web/src/app/administration/classification/details/classification-details.component.ts +++ b/web/src/app/administration/classification/details/classification-details.component.ts @@ -16,7 +16,6 @@ import { ErrorModalService } from 'app/services/errorModal/error-modal.service'; import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service'; import { AlertService } from 'app/services/alert/alert.service'; import { TreeService } from 'app/services/tree/tree.service'; -import { ClassificationTypesService } from 'app/administration/services/classification-types/classification-types.service'; import { RemoveConfirmationService } from 'app/services/remove-confirmation/remove-confirmation.service'; // tslint:disable:max-line-length @@ -78,7 +77,6 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy { private requestInProgressService: RequestInProgressService, private alertService: AlertService, private treeService: TreeService, - private classificationTypeService: ClassificationTypesService, private categoryService: ClassificationCategoriesService, private domainService: DomainService, private customFieldsService: CustomFieldsService, @@ -87,7 +85,7 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy { } ngOnInit() { - this.classificationTypeService.getClassificationTypes().subscribe((classificationTypes: Array) => { + this.categoryService.getClassificationTypes().subscribe((classificationTypes: Array) => { this.classificationTypes = classificationTypes; }) this.classificationSelectedSubscription = this.classificationsService.getSelectedClassification() @@ -112,7 +110,6 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy { this.fillClassificationInformation(this.selectedClassification ? this.selectedClassification : new ClassificationDefinition()) } - if (!this.classification || this.classification.classificationId !== id && id && id !== '') { this.selectClassification(id); } @@ -122,7 +119,6 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy { this.showDetail = showDetail; }); - this.categoriesSubscription = this.categoryService.getCategories().subscribe((categories: Array) => { this.categories = categories; if (categories.length > 0 && this.classification) { @@ -250,7 +246,7 @@ export class ClassificationDetailsComponent implements OnInit, OnDestroy { this.classification.parentKey = classificationSelected.key; this.classification.category = classificationSelected.category; this.classification.domain = this.domainService.getSelectedDomainValue(); - this.selectedClassificationSubscription = this.classificationTypeService.getSelectedClassificationType().subscribe(type => { + this.selectedClassificationSubscription = this.categoryService.getSelectedClassificationType().subscribe(type => { if (this.classification) { this.classification.type = type; } }); this.addDateToClassification(); diff --git a/web/src/app/administration/classification/master/list/classification-list.component.spec.ts b/web/src/app/administration/classification/master/list/classification-list.component.spec.ts index 29ca36b49..d6631d709 100644 --- a/web/src/app/administration/classification/master/list/classification-list.component.spec.ts +++ b/web/src/app/administration/classification/master/list/classification-list.component.spec.ts @@ -19,7 +19,6 @@ import { ClassificationsService } from 'app/administration/services/classificati import { ClassificationDefinitionService } from 'app/administration/services/classification-definition/classification-definition.service'; import { DomainService } from 'app/services/domain/domain.service'; import { ErrorModalService } from 'app/services/errorModal/error-modal.service'; -import { ClassificationTypesService } from 'app/administration/services/classification-types/classification-types.service'; import { RequestInProgressService } from 'app/services/requestInProgress/request-in-progress.service'; import { configureTests } from 'app/app.test.configuration'; import { @@ -45,7 +44,7 @@ describe('ClassificationListComponent', () => { let fixture: ComponentFixture; const treeNodes: Array = new Array(new TreeNodeModel()); const classificationTypes: Array = new Array('type1', 'type2'); - let classificationsService, classificationTypesService, classificationCategoriesService; + let classificationsService, classificationCategoriesService; beforeEach(done => { const configure = (testBed: TestBed) => { @@ -55,7 +54,7 @@ describe('ClassificationListComponent', () => { imports: [HttpClientModule, RouterTestingModule.withRoutes(routes), FormsModule, AngularSvgIconModule], providers: [ HttpClient, WorkbasketDefinitionService, AlertService, ClassificationsService, DomainService, ClassificationDefinitionService, - ErrorModalService, ClassificationTypesService, RequestInProgressService, ClassificationCategoriesService, TreeService + ErrorModalService, RequestInProgressService, ClassificationCategoriesService, TreeService ] }) }; @@ -64,10 +63,9 @@ describe('ClassificationListComponent', () => { component = fixture.componentInstance; classificationsService = testBed.get(ClassificationsService); - classificationTypesService = testBed.get(ClassificationTypesService); classificationCategoriesService = testBed.get(ClassificationCategoriesService); spyOn(classificationsService, 'getClassifications').and.returnValue(of(treeNodes)); - spyOn(classificationTypesService, 'getClassificationTypes') + spyOn(classificationCategoriesService, 'getClassificationTypes') .and.returnValue(of(classificationTypes)); spyOn(classificationCategoriesService, 'getCategories').and.returnValue(of(new Array('cat1', 'cat2'))); spyOn(classificationCategoriesService, 'getCategoryIcon').and.returnValue(new Pair('assets/icons/categories/external.svg')); diff --git a/web/src/app/administration/classification/master/list/classification-list.component.ts b/web/src/app/administration/classification/master/list/classification-list.component.ts index bf040d850..f86cc63dc 100644 --- a/web/src/app/administration/classification/master/list/classification-list.component.ts +++ b/web/src/app/administration/classification/master/list/classification-list.component.ts @@ -7,7 +7,6 @@ import { Classification } from 'app/models/classification'; import { TreeNodeModel } from 'app/models/tree-node'; import { ClassificationsService } from 'app/administration/services/classifications/classifications.service'; -import { ClassificationTypesService } from 'app/administration/services/classification-types/classification-types.service'; import { ClassificationCategoriesService } from 'app/administration/services/classification-categories-service/classification-categories.service'; @@ -43,7 +42,6 @@ export class ClassificationListComponent implements OnInit, OnDestroy { private classificationService: ClassificationsService, private router: Router, private route: ActivatedRoute, - private classificationTypeService: ClassificationTypesService, private categoryService: ClassificationCategoriesService) { } @@ -53,12 +51,13 @@ export class ClassificationListComponent implements OnInit, OnDestroy { .subscribe(value => { this.performRequest(true); }) - this.selectedClassificationSubscription = this.classificationTypeService.getSelectedClassificationType().subscribe(value => { - this.classificationTypeSelected = value; + this.selectedClassificationSubscription = this.categoryService.getSelectedClassificationType().subscribe(value => { + this.classificationTypeSelected = value; this.performRequest(); }) - this.categoriesSubscription = this.categoryService.getCategories().subscribe((categories: Array) => { + this.categoriesSubscription = + this.categoryService.getCategories(this.classificationTypeSelected).subscribe((categories: Array) => { this.categories = categories; }); } @@ -66,12 +65,13 @@ export class ClassificationListComponent implements OnInit, OnDestroy { selectClassificationType(classificationTypeSelected: string) { this.classifications = []; this.requestInProgress = true; - this.classificationTypeService.selectClassificationType(classificationTypeSelected); + this.categoryService.selectClassificationType(classificationTypeSelected); this.classificationService.getClassifications() .subscribe((classifications: Array) => { this.classifications = classifications; this.requestInProgress = false; - }); + }); + this.selectClassification(undefined); } selectClassification(id: string) { @@ -110,7 +110,7 @@ export class ClassificationListComponent implements OnInit, OnDestroy { .subscribe((classifications: Array) => { this.requestInProgress = false; this.classifications = classifications; - this.classificationTypeServiceSubscription = this.classificationTypeService.getClassificationTypes() + this.classificationTypeServiceSubscription = this.categoryService.getClassificationTypes() .subscribe((classificationsTypes: Array) => { this.classificationsTypes = classificationsTypes; }); diff --git a/web/src/app/administration/services/classification-categories-service/classification-categories.service.ts b/web/src/app/administration/services/classification-categories-service/classification-categories.service.ts index ed72e7bc2..87bd6216b 100644 --- a/web/src/app/administration/services/classification-categories-service/classification-categories.service.ts +++ b/web/src/app/administration/services/classification-categories-service/classification-categories.service.ts @@ -2,34 +2,41 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { environment } from 'environments/environment'; -import { Observable , ReplaySubject } from 'rxjs'; +import { Observable , ReplaySubject, BehaviorSubject } from 'rxjs'; import { CustomFieldsService } from 'app/services/custom-fields/custom-fields.service'; import { Pair } from 'app/models/pair'; @Injectable() export class ClassificationCategoriesService { - private url = environment.taskanaRestUrl + '/v1/classification-categories'; - private dataObs$ = new ReplaySubject>(1); + private mainUrl = environment.taskanaRestUrl; + + // categories + private urlCategories = this.mainUrl + '/v1/classification-categories'; + private param = '/?type='; + private dataObsCategories$ = new ReplaySubject>(1); private categoriesObject = new Object(); private missingIcon = 'assets/icons/categories/missing-icon.svg'; + private type = 'UNKNOW'; + + // type + private classificationTypeSelectedValue = 'TASK'; + private urlType = this.mainUrl + '/v1/classification-types'; + private classificationTypeSelected = new BehaviorSubject(this.classificationTypeSelectedValue); + private dataObsType$ = new ReplaySubject>(1); constructor( private httpClient: HttpClient, private customFieldsService: CustomFieldsService) { } - getCategories(forceRefresh = false): Observable> { - if (!this.dataObs$.observers.length || forceRefresh) { - this.httpClient.get>(this.url).subscribe( - data => { this.dataObs$.next(data); this.categoriesObject = this.getCustomCategoriesObject(data) }, - error => { - this.dataObs$.error(error); - this.dataObs$ = new ReplaySubject(1); - } + getCategories(type?: string): Observable> { + if (!this.dataObsCategories$.observers.length || type !== this.type) { + this.httpClient.get>(type ? this.urlCategories + this.param + type : this.urlCategories).subscribe( + data => { this.dataObsCategories$.next(data); this.categoriesObject = this.getCustomCategoriesObject(data); this.type = type; }, + error => { this.dataObsCategories$.error(error); this.dataObsCategories$ = new ReplaySubject(1); } ); } - - return this.dataObs$; + return this.dataObsCategories$; }; getCategoryIcon(category: string): Pair { @@ -47,7 +54,6 @@ export class ClassificationCategoriesService { this.getDefaultCategoryMap(categories), 'classifications.categories'); } - private getDefaultCategoryMap(categoryList: Array): Object { const defaultCategoryMap = new Object(); categoryList.forEach(element => { @@ -55,4 +61,27 @@ export class ClassificationCategoriesService { }); return defaultCategoryMap; } + + getClassificationTypes(forceRefresh = false): Observable> { + if (!this.dataObsType$.observers.length || forceRefresh) { + this.httpClient.get>(this.urlType).subscribe( + data => this.dataObsType$.next(data), + error => { + this.dataObsType$.error(error); + this.dataObsType$ = new ReplaySubject(1); + } + ); + } + return this.dataObsType$; + }; + + selectClassificationType(id: string) { + this.getCategories(id); + this.classificationTypeSelectedValue = id; + this.classificationTypeSelected.next(id); + } + + getSelectedClassificationType(): Observable { + return this.classificationTypeSelected.asObservable(); + } } diff --git a/web/src/app/administration/services/classification-types/classification-types.service.spec.ts b/web/src/app/administration/services/classification-types/classification-types.service.spec.ts deleted file mode 100644 index 3dec2c08e..000000000 --- a/web/src/app/administration/services/classification-types/classification-types.service.spec.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { TestBed, inject } from '@angular/core/testing'; -import { HttpClient, HttpClientModule } from '@angular/common/http'; - -import { ClassificationTypesService } from './classification-types.service'; - -describe('ClassificationTypesService', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientModule], - providers: [HttpClient, ClassificationTypesService] - }); - }); - - it('should be created', inject([ClassificationTypesService], (service: ClassificationTypesService) => { - expect(service).toBeTruthy(); - })); -}); diff --git a/web/src/app/administration/services/classification-types/classification-types.service.ts b/web/src/app/administration/services/classification-types/classification-types.service.ts deleted file mode 100644 index 47e308c2a..000000000 --- a/web/src/app/administration/services/classification-types/classification-types.service.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http'; -import { Observable, BehaviorSubject, ReplaySubject } from 'rxjs'; -import { environment } from 'environments/environment'; - -@Injectable() -export class ClassificationTypesService { - private url = environment.taskanaRestUrl + '/v1/classification-types'; - private classificationTypeSelectedValue = 'TASK'; - private classificationTypeSelected = new BehaviorSubject(this.classificationTypeSelectedValue); - private dataObs$ = new ReplaySubject>(1); - - constructor(private httpClient: HttpClient - ) { } - - getClassificationTypes(forceRefresh = false): Observable> { - - if (!this.dataObs$.observers.length || forceRefresh) { - this.httpClient.get>(this.url).subscribe( - data => this.dataObs$.next(data), - error => { - this.dataObs$.error(error); - this.dataObs$ = new ReplaySubject(1); - } - ); - } - - return this.dataObs$; - }; - - selectClassificationType(id: string) { - this.classificationTypeSelectedValue = id; - this.classificationTypeSelected.next(id); - } - - getSelectedClassificationType(): Observable { - return this.classificationTypeSelected.asObservable(); - } - -} diff --git a/web/src/app/administration/services/classifications/classifications.service.ts b/web/src/app/administration/services/classifications/classifications.service.ts index 87cf4bcf0..9ef833cfc 100644 --- a/web/src/app/administration/services/classifications/classifications.service.ts +++ b/web/src/app/administration/services/classifications/classifications.service.ts @@ -8,7 +8,7 @@ import { Classification } from 'app/models/classification'; import { ClassificationDefinition } from 'app/models/classification-definition'; import { ClassificationResource } from 'app/models/classification-resource'; -import { ClassificationTypesService } from '../classification-types/classification-types.service'; +import { ClassificationCategoriesService } from '../classification-categories-service/classification-categories.service'; import { DomainService } from 'app/services/domain/domain.service'; import { TaskanaQueryParameters } from 'app/shared/util/query-parameters'; import { Direction } from 'app/models/sorting'; @@ -22,7 +22,7 @@ export class ClassificationsService { constructor( private httpClient: HttpClient, - private classificationTypeService: ClassificationTypesService, + private classificationCategoriesService: ClassificationCategoriesService, private domainService: DomainService) { } @@ -58,7 +58,7 @@ export class ClassificationsService { return this.httpClient.get(`${environment.taskanaRestUrl}/v1/classifications/${id}`) .pipe(tap((classification: ClassificationDefinition) => { if (classification) { - this.classificationTypeService.selectClassificationType(classification.type); + this.classificationCategoriesService.selectClassificationType(classification.type); } })); } @@ -100,7 +100,7 @@ export class ClassificationsService { // #endregion private getClassificationObservable(classificationRef: Observable): Observable { - const classificationTypes = this.classificationTypeService.getSelectedClassificationType(); + const classificationTypes = this.classificationCategoriesService.getSelectedClassificationType(); return combineLatest( classificationRef, classificationTypes, @@ -132,7 +132,6 @@ export class ClassificationsService { return roots; } - private findChildren(parent: any, children: Array) { if (children[parent.classificationId]) { parent.children = children[parent.classificationId]; diff --git a/web/src/app/services/custom-fields/custom-fields.service.spec.ts b/web/src/app/services/custom-fields/custom-fields.service.spec.ts index 632d85cac..dbbaf36a5 100644 --- a/web/src/app/services/custom-fields/custom-fields.service.spec.ts +++ b/web/src/app/services/custom-fields/custom-fields.service.spec.ts @@ -12,4 +12,42 @@ describe('CustomFieldsService', () => { it('should be created', inject([CustomFieldsService], (service: CustomFieldsService) => { expect(service).toBeTruthy(); })); + + it('should take default icon path', inject([CustomFieldsService], (service: CustomFieldsService) => { + const categoriesData = {'DEFAULT': 'assets/icons/categories/default.svg'} + const returnedValue = service.getCustomObject(categoriesData, undefined); + expect(returnedValue).toBe(categoriesData); + expect(service).toBeTruthy(); + })); + + it('should take default icon path in merge', inject([CustomFieldsService], (service: CustomFieldsService) => { + const json = require('./taskana-customization-test.json'); + service.initCustomFields('EN', json); + const categoriesDefault = json.EN.classifications.categories; + const categoriesData = { + 'EXTERNAL': 'assets/icons/categories/external.svg', + 'MANUAL': 'assets/icons/categories/manual.svg', + 'AUTOMATIC': 'assets/icons/categories/automatic.svg', + 'PROCESS': 'assets/icons/categories/external.svg' + }; + const returnedValue = service.getCustomObject(categoriesData, 'classifications.categories'); + expect(returnedValue).toEqual(categoriesDefault); + expect(service).toBeTruthy(); + })); + + it('should take merge icon path', inject([CustomFieldsService], (service: CustomFieldsService) => { + const json = require('./taskana-customization-test.json'); + service.initCustomFields('EN', json); + const categoriesData = {'DEFAULT': 'assets/icons/categories/default.svg'} + const result = { + 'AUTOMATIC': 'assets/icons/categories/automatic.svg', + 'DEFAULT': 'assets/icons/categories/default.svg', + 'EXTERNAL': 'assets/icons/categories/external.svg', + 'MANUAL': 'assets/icons/categories/manual.svg', + 'PROCESS': 'assets/icons/categories/process.svg' + }; + const returnedValue = service.getCustomObject(categoriesData, 'classifications.categories'); + expect(returnedValue).toEqual(result); + expect(service).toBeTruthy(); + })); }); diff --git a/web/src/app/services/custom-fields/custom-fields.service.ts b/web/src/app/services/custom-fields/custom-fields.service.ts index dff400941..0e201dc4f 100644 --- a/web/src/app/services/custom-fields/custom-fields.service.ts +++ b/web/src/app/services/custom-fields/custom-fields.service.ts @@ -58,6 +58,26 @@ export class CustomFieldsService { return true; }); + value = this.mergeKeys(value, fallbackObject); + + return value; +} + +private mergeKeys(defaultObject: Object, newObject: Object) { + const value = new Object(); + + for (const item of Object.keys(defaultObject)) { + if (!value[item]) { + value[item] = defaultObject[item]; + } + } + + for (const item of Object.keys(newObject)) { + if (!value[item]) { + value[item] = newObject[item]; + } + } + return value; } diff --git a/web/src/app/services/custom-fields/taskana-customization-test.json b/web/src/app/services/custom-fields/taskana-customization-test.json new file mode 100644 index 000000000..166d53016 --- /dev/null +++ b/web/src/app/services/custom-fields/taskana-customization-test.json @@ -0,0 +1,22 @@ +{ + "EN": { + "classifications": { + "information": { + "custom1": { + "field": "Classification custom 1", + "visible": true + }, + "custom3": { + "field": "", + "visible": false + } + }, + "categories": { + "EXTERNAL": "assets/icons/categories/external.svg", + "MANUAL": "assets/icons/categories/manual.svg", + "AUTOMATIC": "assets/icons/categories/automatic.svg", + "PROCESS": "assets/icons/categories/process.svg" + } + } + } +} diff --git a/web/src/environments/data-sources/taskana-customization.json b/web/src/environments/data-sources/taskana-customization.json index 61d122c42..a21fcd474 100644 --- a/web/src/environments/data-sources/taskana-customization.json +++ b/web/src/environments/data-sources/taskana-customization.json @@ -59,4 +59,4 @@ } } } -} \ No newline at end of file +}