From a6e38078ab4a4a263fb734c69e90985c13b82f07 Mon Sep 17 00:00:00 2001 From: David Watkins Date: Sun, 21 Jul 2024 22:27:09 +0100 Subject: [PATCH 1/5] Measurable Category create - fixed up some integration tests - added integration tests for creation - measurableCategoryDao::store gives back the id #7023 --- .../AssessmentRatingDao.java | 14 +- .../MeasurableCategoryDao.java | 6 +- .../MeasurableCategoryServiceTest.java | 124 ++++++++++++++++++ .../AssessmentRatingService.java | 9 +- .../MeasurableCategoryService.java | 26 +++- .../test_common/helpers/ChangeLogHelper.java | 9 +- .../api/MeasurableCategoryEndpoint.java | 2 +- 7 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 waltz-integration-test/src/test/java/org/finos/waltz/integration_test/inmem/service/MeasurableCategoryServiceTest.java diff --git a/waltz-data/src/main/java/org/finos/waltz/data/assessment_rating/AssessmentRatingDao.java b/waltz-data/src/main/java/org/finos/waltz/data/assessment_rating/AssessmentRatingDao.java index 1c00f8c5fe..3dd5eb2500 100644 --- a/waltz-data/src/main/java/org/finos/waltz/data/assessment_rating/AssessmentRatingDao.java +++ b/waltz-data/src/main/java/org/finos/waltz/data/assessment_rating/AssessmentRatingDao.java @@ -266,19 +266,23 @@ public int deleteByGenericSelector(GenericSelector genericSelector) { public boolean store(SaveAssessmentRatingCommand command) { checkNotNull(command, "command cannot be null"); AssessmentRatingRecord record = COMMAND_TO_RECORD_MAPPER.apply(command); + + return isUpdate(command) + ? dsl.executeUpdate(record) == 1 + : dsl.executeInsert(record) == 1; + } + + + public boolean isUpdate(SaveAssessmentRatingCommand command) { EntityReference ref = command.entityReference(); - boolean isUpdate = dsl.fetchExists(dsl + return dsl.fetchExists(dsl .select(ar.ID) .from(ar) .where(ar.ENTITY_KIND.eq(ref.kind().name())) .and(ar.ENTITY_ID.eq(ref.id())) .and(ar.ASSESSMENT_DEFINITION_ID.eq(command.assessmentDefinitionId())) .and(ar.RATING_ID.eq(command.ratingId()))); - - return isUpdate - ? dsl.executeUpdate(record) == 1 - : dsl.executeInsert(record) == 1; } diff --git a/waltz-data/src/main/java/org/finos/waltz/data/measurable_category/MeasurableCategoryDao.java b/waltz-data/src/main/java/org/finos/waltz/data/measurable_category/MeasurableCategoryDao.java index 9b91abb2ed..df6c743ce0 100644 --- a/waltz-data/src/main/java/org/finos/waltz/data/measurable_category/MeasurableCategoryDao.java +++ b/waltz-data/src/main/java/org/finos/waltz/data/measurable_category/MeasurableCategoryDao.java @@ -126,7 +126,7 @@ public Collection findCategoriesByDirectOrgUnit(long id) { .fetch(TO_DOMAIN_MAPPER); } - public boolean save(MeasurableCategory measurableCategory, String username) { + public Long save(MeasurableCategory measurableCategory, String username) { MeasurableCategoryRecord record = dsl.newRecord(MEASURABLE_CATEGORY); @@ -146,9 +146,9 @@ public boolean save(MeasurableCategory measurableCategory, String username) { record.changed(MEASURABLE_CATEGORY.ID, false); - int update = record.store(); + record.store(); - return update == 1; + return record.getId(); } public Map findRatingCountsByCategoryId(EntityReference ref) { diff --git a/waltz-integration-test/src/test/java/org/finos/waltz/integration_test/inmem/service/MeasurableCategoryServiceTest.java b/waltz-integration-test/src/test/java/org/finos/waltz/integration_test/inmem/service/MeasurableCategoryServiceTest.java new file mode 100644 index 0000000000..4496fe6c62 --- /dev/null +++ b/waltz-integration-test/src/test/java/org/finos/waltz/integration_test/inmem/service/MeasurableCategoryServiceTest.java @@ -0,0 +1,124 @@ +/* + * Waltz - Enterprise Architecture + * Copyright (C) 2016, 2017, 2018, 2019 Waltz open source project + * See README.md for more information + * + * 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 + * + */ + +package org.finos.waltz.integration_test.inmem.service; + +import org.finos.waltz.common.SetUtilities; +import org.finos.waltz.integration_test.inmem.BaseInMemoryIntegrationTest; +import org.finos.waltz.model.EntityKind; +import org.finos.waltz.model.EntityReference; +import org.finos.waltz.model.Operation; +import org.finos.waltz.model.exceptions.NotAuthorizedException; +import org.finos.waltz.model.measurable_category.ImmutableMeasurableCategory; +import org.finos.waltz.model.measurable_category.MeasurableCategory; +import org.finos.waltz.model.user.SystemRole; +import org.finos.waltz.service.measurable_category.MeasurableCategoryService; +import org.finos.waltz.test_common.helpers.ChangeLogHelper; +import org.finos.waltz.test_common.helpers.RatingSchemeHelper; +import org.finos.waltz.test_common.helpers.UserHelper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import static org.finos.waltz.model.EntityReference.mkRef; +import static org.finos.waltz.test_common.helpers.NameHelper.mkName; +import static org.junit.Assert.*; + +public class MeasurableCategoryServiceTest extends BaseInMemoryIntegrationTest { + + @Autowired + private MeasurableCategoryService mcSvc; + + @Autowired + private UserHelper userHelper; + + @Autowired + private RatingSchemeHelper ratingSchemeHelper; + + @Autowired + private ChangeLogHelper changeLogHelper; + + + @Test + public void needToBeAnAdminOrTaxonomyEditorToSaveCategories() { + String admin = mkName("mc_admin"); + String taxonomyEditor = mkName("mc_tax"); + String unauthorised = mkName("unauthorised"); + userHelper.createUserWithSystemRoles(admin, SetUtilities.asSet(SystemRole.ADMIN)); + userHelper.createUserWithSystemRoles(taxonomyEditor, SetUtilities.asSet(SystemRole.TAXONOMY_EDITOR)); + userHelper.createUserWithRoles(unauthorised, "WIBBLE"); + + ImmutableMeasurableCategory mc1 = mkMeasurableCategory("mc1"); + assertTrue("Should work as has ADMIN role", mcSvc.save(mc1, admin) > 0L); + + ImmutableMeasurableCategory mc2 = mkMeasurableCategory("mc2"); + assertTrue("Should work as has TAXONOMY_EDITOR role", mcSvc.save(mc2, taxonomyEditor) > 0L); + + ImmutableMeasurableCategory mc3 = mkMeasurableCategory("mc3"); + assertThrows("Should not work as incorrect roles", NotAuthorizedException.class, () -> mcSvc.save(mc3, unauthorised)); + } + + + @Test + public void categoriesCanBeUpdatedByRepeatedCallsToSave() { + String user = mkName("mc_tax"); + userHelper.createUserWithSystemRoles(user, SetUtilities.asSet(SystemRole.TAXONOMY_EDITOR)); + + ImmutableMeasurableCategory v1Pre = mkMeasurableCategory("mc"); + String v1Name = v1Pre.name(); + Long mcId = mcSvc.save(v1Pre, user); + MeasurableCategory v1Post = mcSvc.getById(mcId); + assertEquals("Expected initially saved name to match", v1Name, v1Post.name()); + + EntityReference mcRef = mkRef(EntityKind.MEASURABLE_CATEGORY, mcId); + changeLogHelper.assertChangeLogContainsAtLeastOneMatchingOperation(mcRef, Operation.ADD); + + ImmutableMeasurableCategory v2Pre = ImmutableMeasurableCategory.copyOf(v1Post) + .withName(mkName("updated_mc")) + .withDescription("updated desc") + .withAllowPrimaryRatings(false) + .withIcon("fire"); + + mcSvc.save(v2Pre, user); + MeasurableCategory v2Post = mcSvc.getById(mcId); + assertEquals("Expected updated name to match", v2Pre.name(), v2Post.name()); + assertEquals("Expected updated desc to match", v2Pre.description(), v2Post.description()); + assertEquals("Expected updated primary ratings to match", v2Pre.allowPrimaryRatings(), v2Post.allowPrimaryRatings()); + assertEquals("Expected updated icon to match", v2Pre.icon(), v2Post.icon()); + + changeLogHelper.assertChangeLogContainsAtLeastOneMatchingOperation(mcRef, Operation.UPDATE); + } + + + // --- util + + private ImmutableMeasurableCategory mkMeasurableCategory(String stem) { + return ImmutableMeasurableCategory + .builder() + .name(mkName(stem)) + .externalId(mkName(stem.toUpperCase())) + .description(stem + " desc") + .icon("cog") + .allowPrimaryRatings(true) + .editable(true) + .lastUpdatedBy("mctest") + .ratingSchemeId(ratingSchemeHelper.createEmptyRatingScheme(stem + "_rs")) + .build(); + } + + +} \ No newline at end of file diff --git a/waltz-service/src/main/java/org/finos/waltz/service/assessment_rating/AssessmentRatingService.java b/waltz-service/src/main/java/org/finos/waltz/service/assessment_rating/AssessmentRatingService.java index 774e3e3f02..9f78556c3d 100644 --- a/waltz-service/src/main/java/org/finos/waltz/service/assessment_rating/AssessmentRatingService.java +++ b/waltz-service/src/main/java/org/finos/waltz/service/assessment_rating/AssessmentRatingService.java @@ -122,7 +122,9 @@ public List findByDefinitionId(long definitionId) { public boolean store(SaveAssessmentRatingCommand command, String username) throws InsufficientPrivelegeException { verifyAnyPermission(asSet(Operation.UPDATE, Operation.ADD), command.entityReference(), command.assessmentDefinitionId(), command.ratingId(), username); AssessmentDefinition assessmentDefinition = assessmentDefinitionDao.getById(command.assessmentDefinitionId()); - createChangeLogEntryForSave(command, username, assessmentDefinition); + + boolean isUpdate = assessmentRatingDao.isUpdate(command); + createChangeLogEntryForSave(command, username, assessmentDefinition, isUpdate ? Operation.UPDATE : Operation.ADD); return assessmentRatingDao.store(command); } @@ -322,7 +324,8 @@ private void verifyAnyPermission(Set possiblePerms, private void createChangeLogEntryForSave(SaveAssessmentRatingCommand command, String username, - AssessmentDefinition assessmentDefinition) { + AssessmentDefinition assessmentDefinition, + Operation operation) { Optional previousRating = assessmentRatingDao.findForEntity(command.entityReference()) .stream() .filter(r -> r.assessmentDefinitionId() == command.assessmentDefinitionId()) @@ -344,7 +347,7 @@ private void createChangeLogEntryForSave(SaveAssessmentRatingCommand command, .parentReference(mkRef(command.entityReference().kind(), command.entityReference().id())) .userId(username) .severity(Severity.INFORMATION) - .operation(Operation.UPDATE) + .operation(operation) .build(); changeLogService.write(logEntry); diff --git a/waltz-service/src/main/java/org/finos/waltz/service/measurable_category/MeasurableCategoryService.java b/waltz-service/src/main/java/org/finos/waltz/service/measurable_category/MeasurableCategoryService.java index ab1bd93d9f..152831f438 100644 --- a/waltz-service/src/main/java/org/finos/waltz/service/measurable_category/MeasurableCategoryService.java +++ b/waltz-service/src/main/java/org/finos/waltz/service/measurable_category/MeasurableCategoryService.java @@ -19,12 +19,16 @@ package org.finos.waltz.service.measurable_category; import org.finos.waltz.data.measurable_category.MeasurableCategoryDao; +import org.finos.waltz.model.EntityKind; import org.finos.waltz.model.EntityReference; +import org.finos.waltz.model.Operation; +import org.finos.waltz.model.changelog.ImmutableChangeLog; import org.finos.waltz.model.exceptions.NotAuthorizedException; import org.finos.waltz.model.measurable_category.ImmutableMeasurableCategoryView; import org.finos.waltz.model.measurable_category.MeasurableCategory; import org.finos.waltz.model.measurable_category.MeasurableCategoryView; import org.finos.waltz.model.user.SystemRole; +import org.finos.waltz.service.changelog.ChangeLogService; import org.finos.waltz.service.user.UserRoleService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -43,12 +47,15 @@ public class MeasurableCategoryService { private final MeasurableCategoryDao measurableCategoryDao; private final UserRoleService userRoleService; - + private final ChangeLogService changeLogService; @Autowired - public MeasurableCategoryService(MeasurableCategoryDao measurableCategoryDao, UserRoleService userRoleService) { + public MeasurableCategoryService(MeasurableCategoryDao measurableCategoryDao, + UserRoleService userRoleService, + ChangeLogService changeLogService) { this.measurableCategoryDao = measurableCategoryDao; this.userRoleService = userRoleService; + this.changeLogService = changeLogService; } @@ -90,9 +97,20 @@ public List findPopulatedCategoriesForRef(EntityReferenc } - public boolean save(MeasurableCategory measurableCategory, String username) { + public Long save(MeasurableCategory measurableCategory, String username) { ensureUserHasPermission(username); - return measurableCategoryDao.save(measurableCategory, username); + Long mcId = measurableCategoryDao.save(measurableCategory, username); + boolean isNew = ! measurableCategory.id().isPresent(); + + changeLogService.write(ImmutableChangeLog + .builder() + .operation(isNew ? Operation.ADD : Operation.UPDATE) + .parentReference(EntityReference.mkRef(EntityKind.MEASURABLE_CATEGORY, mcId)) + .message(isNew ? "Measureable category created" : "Measurable category updated") + .userId(username) + .build()); + + return mcId; } diff --git a/waltz-test-common/src/main/java/org/finos/waltz/test_common/helpers/ChangeLogHelper.java b/waltz-test-common/src/main/java/org/finos/waltz/test_common/helpers/ChangeLogHelper.java index d18842a07b..9c34a965a9 100644 --- a/waltz-test-common/src/main/java/org/finos/waltz/test_common/helpers/ChangeLogHelper.java +++ b/waltz-test-common/src/main/java/org/finos/waltz/test_common/helpers/ChangeLogHelper.java @@ -11,6 +11,7 @@ import java.util.Optional; import static org.finos.waltz.common.CollectionUtilities.find; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @Service @@ -28,8 +29,10 @@ public void assertChangeLogContainsAtLeastOneMatchingOperation(EntityReference r Optional.empty()); assertNotNull(changeLogEntries); - assertNotNull(find( - c -> c.operation() == operation, - changeLogEntries)); + assertNotEquals( + Optional.empty(), + find( + c -> c.operation() == operation, + changeLogEntries)); } } diff --git a/waltz-web/src/main/java/org/finos/waltz/web/endpoints/api/MeasurableCategoryEndpoint.java b/waltz-web/src/main/java/org/finos/waltz/web/endpoints/api/MeasurableCategoryEndpoint.java index 0e415ce98c..d327804e4e 100644 --- a/waltz-web/src/main/java/org/finos/waltz/web/endpoints/api/MeasurableCategoryEndpoint.java +++ b/waltz-web/src/main/java/org/finos/waltz/web/endpoints/api/MeasurableCategoryEndpoint.java @@ -68,7 +68,7 @@ public void register() { DatumRoute getByIdRoute = (request, response) -> measurableCategoryService.getById(WebUtilities.getId(request)); - DatumRoute saveRoute = (request, response) -> { + DatumRoute saveRoute = (request, response) -> { String username = getUsername(request); return measurableCategoryService .save( From 7b7756485de8e41e7f4bcb9c2c346153600dcf3c Mon Sep 17 00:00:00 2001 From: "david.watkins@db.com" Date: Thu, 25 Jul 2024 09:22:22 +0100 Subject: [PATCH 2/5] Measurable Category - creation and edit #CTCTOWALTZ-3278 #7023 --- waltz-ng/client/common/svelte/ViewLink.svelte | 8 + waltz-ng/client/system/routes.js | 10 +- .../MeasurableCategoriesAdminView.svelte | 6 +- .../MeasurableCategoryEditView.svelte | 189 ++++++++++++++++++ 4 files changed, 208 insertions(+), 5 deletions(-) create mode 100644 waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryEditView.svelte diff --git a/waltz-ng/client/common/svelte/ViewLink.svelte b/waltz-ng/client/common/svelte/ViewLink.svelte index 6c0c404479..c0fd445ffe 100644 --- a/waltz-ng/client/common/svelte/ViewLink.svelte +++ b/waltz-ng/client/common/svelte/ViewLink.svelte @@ -81,6 +81,14 @@ path: ctx => `logical-flow/${ctx.id}`, title: "Logical Flow View" }, + "main.system.measurable-category.edit": { + path: ctx => `system/measurable-category/edit/${ctx.id}`, + title: "Measurable Category" + }, + "main.system.measurable-category.list": { + path: ctx => `system/measurable-category/list`, + title: "Measurable Categories" + }, "main.measurable.view": { path: ctx => `measurable/${ctx.id}`, title: "Measurable View" diff --git a/waltz-ng/client/system/routes.js b/waltz-ng/client/system/routes.js index 88f8ea8166..a3712db188 100644 --- a/waltz-ng/client/system/routes.js +++ b/waltz-ng/client/system/routes.js @@ -120,11 +120,17 @@ const licencesState = { const measurableCategoriesState = { - url: "/measurable-categories", + url: "/measurable-category", views: { "content@": MeasurableCategoriesView } }; +// const measurableCategoryEdit = { +// url: "/measurable-category/{id:int}/edit", +// views: { "content@": MeasurableCategoryEdit } +// }; + + const eudaListState = { url: "/euda-list", views: {"content@": EudaListView} @@ -181,7 +187,7 @@ function setupRoutes($stateProvider) { .state("main.system.euda-list", eudaListState) .state("main.system.hierarchies", hierarchiesState) .state("main.system.licences", licencesState) - .state("main.system.measurable-categories", measurableCategoriesState) + .state("main.system.measurable-category.list", measurableCategoriesState) .state("main.system.nav-aids", navAidAdminState) .state("main.system.nav-aid-builder", navAidBuilderState) .state("main.system.orphans", orphansState) diff --git a/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoriesAdminView.svelte b/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoriesAdminView.svelte index 8c6f1e86f2..812974168f 100644 --- a/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoriesAdminView.svelte +++ b/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoriesAdminView.svelte @@ -206,8 +206,8 @@ style="table-layout: fixed"> - Name - External Id + Name + External Id Icon Allows Primary Ratings? Operations @@ -222,7 +222,7 @@ - + {category.externalId} diff --git a/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryEditView.svelte b/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryEditView.svelte new file mode 100644 index 0000000000..1dffd0b637 --- /dev/null +++ b/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryEditView.svelte @@ -0,0 +1,189 @@ + + + +
+
    +
  1. Home
  2. +
  3. System Admin
  4. +
  5. Measurable Categories
  6. +
  7. Measurable Category
  8. +
+
+
+ + +
+

Edit Category

+
+
+ + +
+
+ + +
+ This is the external id used to identify this category in external systems. + Be careful when changing this value as it may break integrations. +
+
+
+ + +
+ Description of the category. Markdown is supported. +
+
+
+ +
+ + +
+
+ This is the icon associated to this category. Currently these icons are based upon the FontAwesome v4 + icon set . +
+
+
+ + +
+ Position, used for ordering categories. + Lower numbers go first, name is used as a tie breaker. +
+
+ + +
+ +
+ +
+ Determines whether users can select one measurable in this category to be flagged as the primary + rating for the application. +
+
+
+ +
+ +
+ +
+ Allow the taxonomy to be edited from within Waltz using the maintenance screen. +
+
+
+
+ +
+ +
+ Which role is needed to edit the taxonomy +
+
+
+ + + + +
+
+ + + \ No newline at end of file From 2dc01aebb43167483f34c7a90f724d4409852e4f Mon Sep 17 00:00:00 2001 From: "david.watkins@db.com" Date: Thu, 25 Jul 2024 17:17:43 +0100 Subject: [PATCH 3/5] Measurable Category - creation and edit - moved to discrete pages #CTCTOWALTZ-3278 #7023 --- waltz-ng/client/common/svelte/ViewLink.svelte | 14 +- .../svelte-stores/page-navigation-store.js | 3 + .../system/measurable-category-create-view.js | 42 +++ .../system/measurable-category-edit-view.js | 42 +++ ...ew.js => measurable-category-list-view.js} | 6 +- waltz-ng/client/system/routes.js | 32 ++- .../MeasurableCategoriesAdminView.svelte | 272 ------------------ .../MeasurableCategoryEditView.svelte | 119 +++++--- .../MeasurableCategoryListView.svelte | 149 ++++++++++ waltz-ng/client/system/system-admin-list.js | 2 +- .../resources/liquibase/db.changelog-1.59.xml | 5 +- 11 files changed, 361 insertions(+), 325 deletions(-) create mode 100644 waltz-ng/client/system/measurable-category-create-view.js create mode 100644 waltz-ng/client/system/measurable-category-edit-view.js rename waltz-ng/client/system/{measurable-categories-view.js => measurable-category-list-view.js} (83%) delete mode 100644 waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoriesAdminView.svelte create mode 100644 waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryListView.svelte diff --git a/waltz-ng/client/common/svelte/ViewLink.svelte b/waltz-ng/client/common/svelte/ViewLink.svelte index 99e3ba8d8c..024eab1b45 100644 --- a/waltz-ng/client/common/svelte/ViewLink.svelte +++ b/waltz-ng/client/common/svelte/ViewLink.svelte @@ -82,17 +82,25 @@ title: "Logical Flow View" }, "main.system.measurable-category.edit": { - path: ctx => `system/measurable-category/edit/${ctx.id}`, - title: "Measurable Category" + path: ctx => `system/measurable-category/id/${ctx.id}/edit`, + title: "Measurable Category Edit" + }, + "main.system.measurable-category.create": { + path: () => `system/measurable-category/create`, + title: "Measurable Category Create" }, "main.system.measurable-category.list": { - path: ctx => `system/measurable-category/list`, + path: () => `system/measurable-category/list`, title: "Measurable Categories" }, "main.measurable.view": { path: ctx => `measurable/${ctx.id}`, title: "Measurable View" }, + "main.measurable-category.list": { + path: ctx => `measurable-category/${ctx.id}`, + title: "Measurable View" + }, "main.org-unit.view": { path: ctx => `org-units/${ctx.id}`, title: "Org Unit View" diff --git a/waltz-ng/client/svelte-stores/page-navigation-store.js b/waltz-ng/client/svelte-stores/page-navigation-store.js index 4348c79d7d..b5e90f79c9 100644 --- a/waltz-ng/client/svelte-stores/page-navigation-store.js +++ b/waltz-ng/client/svelte-stores/page-navigation-store.js @@ -1,6 +1,9 @@ import {writable} from "svelte/store"; /** + * If you push an item into this store the browser will navigate to the + * corresponding page. + * * Expects object looking like: * { * state: '' diff --git a/waltz-ng/client/system/measurable-category-create-view.js b/waltz-ng/client/system/measurable-category-create-view.js new file mode 100644 index 0000000000..ee1cd49249 --- /dev/null +++ b/waltz-ng/client/system/measurable-category-create-view.js @@ -0,0 +1,42 @@ +/* + * Waltz - Enterprise Architecture + * Copyright (C) 2016, 2017, 2018, 2019 Waltz open source project + * See README.md for more information + * + * 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 + * + */ +import MeasurableCategoryEditView from "./svelte/measurable-categories/MeasurableCategoryEditView.svelte"; +import {initialiseData} from "../common"; + + +const initialState = { + MeasurableCategoryEditView +}; + + +function controller($stateParams) { + const vm = initialiseData(this, initialState); + + vm.id = $stateParams.id; +} + + +controller.$inject = ["$stateParams"]; + + +export default { + template: ``, + controller, + controllerAs: "$ctrl", + bindToController: true, +}; \ No newline at end of file diff --git a/waltz-ng/client/system/measurable-category-edit-view.js b/waltz-ng/client/system/measurable-category-edit-view.js new file mode 100644 index 0000000000..7edc830b89 --- /dev/null +++ b/waltz-ng/client/system/measurable-category-edit-view.js @@ -0,0 +1,42 @@ +/* + * Waltz - Enterprise Architecture + * Copyright (C) 2016, 2017, 2018, 2019 Waltz open source project + * See README.md for more information + * + * 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 + * + */ +import MeasurableCategoryEditView from "./svelte/measurable-categories/MeasurableCategoryEditView.svelte"; +import {initialiseData} from "../common"; + + +const initialState = { + MeasurableCategoryEditView +}; + + +function controller($stateParams) { + const vm = initialiseData(this, initialState); + + vm.id = $stateParams.id; +} + + +controller.$inject = ["$stateParams"]; + + +export default { + template: ``, + controller, + controllerAs: "$ctrl", + bindToController: true, +}; \ No newline at end of file diff --git a/waltz-ng/client/system/measurable-categories-view.js b/waltz-ng/client/system/measurable-category-list-view.js similarity index 83% rename from waltz-ng/client/system/measurable-categories-view.js rename to waltz-ng/client/system/measurable-category-list-view.js index fd2f36f13a..196b77b9d0 100644 --- a/waltz-ng/client/system/measurable-categories-view.js +++ b/waltz-ng/client/system/measurable-category-list-view.js @@ -15,12 +15,12 @@ * See the License for the specific * */ -import MeasurableCategoriesAdminView from "./svelte/measurable-categories/MeasurableCategoriesAdminView.svelte"; +import MeasurableCategoryListView from "./svelte/measurable-categories/MeasurableCategoryListView.svelte"; import {initialiseData} from "../common"; const initialState = { - MeasurableCategoriesAdminView + MeasurableCategoryListView }; @@ -34,7 +34,7 @@ controller.$inject = []; export default { - template: ``, + template: ``, controller, controllerAs: "$ctrl", bindToController: true, diff --git a/waltz-ng/client/system/routes.js b/waltz-ng/client/system/routes.js index a3712db188..f8b550832f 100644 --- a/waltz-ng/client/system/routes.js +++ b/waltz-ng/client/system/routes.js @@ -37,7 +37,9 @@ import PermissionsView from "./permissions-view"; import NavAidBuilderView from "./nav-aid-builder-view"; import VersionInfoView from "./version-info-view"; import LicencesAdminView from "./licences-view"; -import MeasurableCategoriesView from "./measurable-categories-view"; +import MeasurableCategoryListView from "./measurable-category-list-view"; +import MeasurableCategoryEditView from "./measurable-category-edit-view"; +import MeasurableCategoryCreateView from "./measurable-category-create-view"; import DiagramBuilderView from "./diagram-builder-view"; import NavAidAdminView from "./nav-aids-view"; @@ -119,11 +121,30 @@ const licencesState = { }; -const measurableCategoriesState = { + + +const measurableCategoryState = { url: "/measurable-category", - views: { "content@": MeasurableCategoriesView } }; +const measurableCategoryListState = { + url: "/list", + views: { "content@": MeasurableCategoryListView } +}; + + +const measurableCategoryCreateState = { + url: "/create", + views: { "content@": MeasurableCategoryCreateView } +}; + + +const measurableCategoryEditState = { + url: "/id/{id:int}/edit", + views: { "content@": MeasurableCategoryEditView } +}; + + // const measurableCategoryEdit = { // url: "/measurable-category/{id:int}/edit", @@ -187,7 +208,10 @@ function setupRoutes($stateProvider) { .state("main.system.euda-list", eudaListState) .state("main.system.hierarchies", hierarchiesState) .state("main.system.licences", licencesState) - .state("main.system.measurable-category.list", measurableCategoriesState) + .state("main.system.measurable-category", measurableCategoryState) + .state("main.system.measurable-category.list", measurableCategoryListState) + .state("main.system.measurable-category.edit", measurableCategoryEditState) + .state("main.system.measurable-category.create", measurableCategoryCreateState) .state("main.system.nav-aids", navAidAdminState) .state("main.system.nav-aid-builder", navAidBuilderState) .state("main.system.orphans", orphansState) diff --git a/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoriesAdminView.svelte b/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoriesAdminView.svelte deleted file mode 100644 index 812974168f..0000000000 --- a/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoriesAdminView.svelte +++ /dev/null @@ -1,272 +0,0 @@ - - - -
-
    -
  1. Home
  2. -
  3. System Admin
  4. -
  5. Measurable Categories
  6. -
-
-
- - -
-
-
-

Measurable Categories are used to define taxonomies.

-
-
- - {#if activeMode === Modes.EDIT} -

Edit Category

-
-
- - -
-
- - -
- This is the external id used to identify this category in external systems. - Be careful when changing this value as it may break integrations. -
-
-
- - -
- Description of the category. Markdown is supported. -
-
-
- -
- - -
-
- This is the icon associated to this category. Currently these icons are based upon the FontAwesome v4 - icon set . -
-
-
- - -
- Position, used for ordering categories. - Lower numbers go first, name is used as a tie breaker. -
-
- - -
- -
- -
- Determines whether users can select one measurable in this category to be flagged as the primary - rating for the application. -
-
-
- -
- -
- -
- Allow the taxonomy to be edited from within Waltz using the maintenance screen. -
-
-
-
- -
- -
- Which role is needed to edit the taxonomy -
-
-
- - - - -
- - {:else if activeMode === Modes.LIST} -
-
- - - - - - - - - - - - - {#each _.orderBy(termSearch(categories, qry), [d => d.position, d => d.name]) as category} - - - - - - - - {/each} - - - - - - -
NameExternal IdIconAllows Primary Ratings?Operations
- - {category.name} - - - - {category.externalId} - - - - - ({category.icon}) - - - - - -
- -
-
-
- {/if} -
- - - \ No newline at end of file diff --git a/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryEditView.svelte b/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryEditView.svelte index 1dffd0b637..07edfc9a8b 100644 --- a/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryEditView.svelte +++ b/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryEditView.svelte @@ -1,43 +1,59 @@
  • Home
  • System Admin
  • -
  • Measurable Categories
  • -
  • Measurable Category
  • +
  • Measurable Categories
  • +
  • {mode === 'CREATE' ? 'Create' : category?.name}
  • -

    Edit Category

    + {#if mode === 'CREATE'} +

    Create Category

    + {:else} +

    Edit Category

    + {/if} + + {#if $workingCopy}
    - +
    - +
    @@ -80,10 +102,27 @@
    - + + +
    + The rating scheme is used to define the possible values for mappings to this taxonomy. +
    +
    +
    +
    @@ -91,16 +130,16 @@
    - +
    - +
    This is the icon associated to this category. Currently these icons are based upon the + bind:value={$workingCopy.position}>
    Position, used for ordering categories. Lower numbers go first, name is used as a tie breaker. @@ -131,7 +170,7 @@
    + bind:checked={$workingCopy.allowPrimaryRatings}>
    Determines whether users can select one measurable in this category to be flagged as the primary rating for the application. @@ -146,7 +185,7 @@
    + bind:checked={$workingCopy.editable}>
    Allow the taxonomy to be edited from within Waltz using the maintenance screen.
    @@ -158,7 +197,7 @@
    + bind:value={$workingCopy.ratingEditorRole}>
    Which role is needed to edit the taxonomy
    @@ -170,12 +209,11 @@ class="btn btn-primary"> Save - + + {/if}
    @@ -186,4 +224,9 @@ margin-bottom: 10px; color: #737373; } + + .required:after { + content:" *"; + color: red; + } \ No newline at end of file diff --git a/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryListView.svelte b/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryListView.svelte new file mode 100644 index 0000000000..be1f46a9ae --- /dev/null +++ b/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryListView.svelte @@ -0,0 +1,149 @@ + + + +
    +
      +
    1. Home
    2. +
    3. System Admin
    4. +
    5. Measurable Categories
    6. +
    +
    +
    + + +
    +
    +
    +

    Measurable Categories are used to define taxonomies.

    +
    +
    + + {#if activeMode === Modes.LIST} +
    +
    + + + + + + + + + + + + + {#each _.orderBy(termSearch(categories, qry), [d => d.position, d => d.name]) as category} + + + + + + + + {/each} + + + + + + +
    NameExternal IdIconAllows Primary Ratings?Operations
    + + {category.name} + + + + {category.externalId} + + + + + ({category.icon}) + + + + +
      + {#if hasEditPermissions} +
    • + + Edit + +
    • + {/if} +
    • + + View data + +
    • +
    +
    + + + Create a new taxonomy + +
    +
    +
    + {/if} +
    + + \ No newline at end of file diff --git a/waltz-ng/client/system/system-admin-list.js b/waltz-ng/client/system/system-admin-list.js index 06dd4c4e38..7df89e0efa 100644 --- a/waltz-ng/client/system/system-admin-list.js +++ b/waltz-ng/client/system/system-admin-list.js @@ -108,7 +108,7 @@ const referenceDataOptions= [ name: "Measurable Categories", role: "ADMIN", description: "View and edit measurable categories", - state: "main.system.measurable-categories", + state: "main.system.measurable-category.list", icon: "puzzle-piece" }, { name: "Navigation Aids", diff --git a/waltz-schema/src/main/resources/liquibase/db.changelog-1.59.xml b/waltz-schema/src/main/resources/liquibase/db.changelog-1.59.xml index 52111e08ac..0f900c8f3c 100644 --- a/waltz-schema/src/main/resources/liquibase/db.changelog-1.59.xml +++ b/waltz-schema/src/main/resources/liquibase/db.changelog-1.59.xml @@ -19,10 +19,7 @@ xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.4.xsd" logicalFilePath="db.changelog-1.59.xml"> - - - + From ecccdb505bc4ed50644eba9c7d6c328109279097 Mon Sep 17 00:00:00 2001 From: "david.watkins@db.com" Date: Mon, 19 Aug 2024 15:01:07 +0100 Subject: [PATCH 4/5] Measurable Category - mc creation now adds a root node #CTCTOWALTZ-3278 #7023 --- .../pages/edit/measurable-category-edit.js | 4 +-- .../MeasurableCategoryService.java | 26 ++++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/waltz-ng/client/measurable-category/pages/edit/measurable-category-edit.js b/waltz-ng/client/measurable-category/pages/edit/measurable-category-edit.js index 21fd9d3347..8029f95736 100644 --- a/waltz-ng/client/measurable-category/pages/edit/measurable-category-edit.js +++ b/waltz-ng/client/measurable-category/pages/edit/measurable-category-edit.js @@ -85,9 +85,7 @@ function controller($q, // -- boot vm.$onInit = () => { - serviceBroker - .loadAppData(CORE_API.MeasurableStore.findAll) - .then(r => vm.measurables = _.filter(r.data, m => m.categoryId === categoryId)); + reloadMeasurables(); serviceBroker .loadAppData(CORE_API.MeasurableCategoryStore.findAll) diff --git a/waltz-service/src/main/java/org/finos/waltz/service/measurable_category/MeasurableCategoryService.java b/waltz-service/src/main/java/org/finos/waltz/service/measurable_category/MeasurableCategoryService.java index 152831f438..0440e4715e 100644 --- a/waltz-service/src/main/java/org/finos/waltz/service/measurable_category/MeasurableCategoryService.java +++ b/waltz-service/src/main/java/org/finos/waltz/service/measurable_category/MeasurableCategoryService.java @@ -24,11 +24,14 @@ import org.finos.waltz.model.Operation; import org.finos.waltz.model.changelog.ImmutableChangeLog; import org.finos.waltz.model.exceptions.NotAuthorizedException; +import org.finos.waltz.model.measurable.ImmutableMeasurable; +import org.finos.waltz.model.measurable.Measurable; import org.finos.waltz.model.measurable_category.ImmutableMeasurableCategoryView; import org.finos.waltz.model.measurable_category.MeasurableCategory; import org.finos.waltz.model.measurable_category.MeasurableCategoryView; import org.finos.waltz.model.user.SystemRole; import org.finos.waltz.service.changelog.ChangeLogService; +import org.finos.waltz.service.measurable.MeasurableService; import org.finos.waltz.service.user.UserRoleService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -48,14 +51,17 @@ public class MeasurableCategoryService { private final MeasurableCategoryDao measurableCategoryDao; private final UserRoleService userRoleService; private final ChangeLogService changeLogService; + private final MeasurableService measurableService; @Autowired public MeasurableCategoryService(MeasurableCategoryDao measurableCategoryDao, UserRoleService userRoleService, - ChangeLogService changeLogService) { + ChangeLogService changeLogService, + MeasurableService measurableService) { this.measurableCategoryDao = measurableCategoryDao; this.userRoleService = userRoleService; this.changeLogService = changeLogService; + this.measurableService = measurableService; } @@ -99,17 +105,31 @@ public List findPopulatedCategoriesForRef(EntityReferenc public Long save(MeasurableCategory measurableCategory, String username) { ensureUserHasPermission(username); - Long mcId = measurableCategoryDao.save(measurableCategory, username); boolean isNew = ! measurableCategory.id().isPresent(); + Long mcId = measurableCategoryDao.save(measurableCategory, username); changeLogService.write(ImmutableChangeLog .builder() .operation(isNew ? Operation.ADD : Operation.UPDATE) .parentReference(EntityReference.mkRef(EntityKind.MEASURABLE_CATEGORY, mcId)) - .message(isNew ? "Measureable category created" : "Measurable category updated") + .message(isNew ? "Measurable category created, root node added" : "Measurable category updated") .userId(username) .build()); + if (isNew) { + Measurable startingMeasurable = ImmutableMeasurable + .builder() + .name("Root - " + measurableCategory.name()) + .description("Autogenerated root node, feel free to rename/remove") + .concrete(false) + .externalId(measurableCategory.externalId().map(extId -> String.format("%s-001", extId))) + .categoryId(mcId) + .lastUpdatedBy(username) + .build(); + measurableService.create( + startingMeasurable, + username); + } return mcId; } From a5f73c0338edc1bca324b283671042e601fca475 Mon Sep 17 00:00:00 2001 From: "david.watkins@db.com" Date: Thu, 22 Aug 2024 11:08:10 +0100 Subject: [PATCH 5/5] Measurable Category - Fixes based on code review - fixing permissions - removing commented-out route - clarifying input label - aligning default icon to the default used in the database schema #CTCTOWALTZ-3278 #7023 --- waltz-ng/client/system/routes.js | 7 ------- .../MeasurableCategoryEditView.svelte | 20 +++++++++---------- .../MeasurableCategoryListView.svelte | 10 +++++++++- waltz-ng/client/system/system-admin-list.js | 2 +- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/waltz-ng/client/system/routes.js b/waltz-ng/client/system/routes.js index f8b550832f..a9bcfe8dd6 100644 --- a/waltz-ng/client/system/routes.js +++ b/waltz-ng/client/system/routes.js @@ -145,13 +145,6 @@ const measurableCategoryEditState = { }; - -// const measurableCategoryEdit = { -// url: "/measurable-category/{id:int}/edit", -// views: { "content@": MeasurableCategoryEdit } -// }; - - const eudaListState = { url: "/euda-list", views: {"content@": EudaListView} diff --git a/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryEditView.svelte b/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryEditView.svelte index 07edfc9a8b..b648ba266a 100644 --- a/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryEditView.svelte +++ b/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryEditView.svelte @@ -18,7 +18,7 @@ id: null, // we want a new category position: 0, lastUpdatedBy: "defaultValue", - icon: "cog", + icon: "puzzle-piece", allowPrimaryRatings: false }; @@ -191,16 +191,16 @@
    + +
    - -
    - -
    - Which role is needed to edit the taxonomy -
    + + +
    + The role needed by users to perform mappings against this taxonomy.
    diff --git a/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryListView.svelte b/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryListView.svelte index be1f46a9ae..1e4b751bbc 100644 --- a/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryListView.svelte +++ b/waltz-ng/client/system/svelte/measurable-categories/MeasurableCategoryListView.svelte @@ -17,6 +17,11 @@ DELETE: "delete" }; + const requiredPermissions = [ // user should have at least one of these + systemRoles.ADMIN.key, + systemRoles.TAXONOMY_EDITOR.key + ]; + const permissionsCall = userStore.load(); let loadCategoriesCall; @@ -27,7 +32,10 @@ let hasEditPermissions = false; $: permissions = $permissionsCall?.data; - $: hasEditPermissions = _.includes(permissions?.roles, systemRoles.AGGREGATE_OVERLAY_DIAGRAM_EDITOR.key) || false; + + $: hasEditPermissions = _.some( + permissions?.roles, + r => _.includes(requiredPermissions, r)); function loadCategories() { loadCategoriesCall = measurableCategoryStore.findAll(true); diff --git a/waltz-ng/client/system/system-admin-list.js b/waltz-ng/client/system/system-admin-list.js index 7df89e0efa..ad726839a1 100644 --- a/waltz-ng/client/system/system-admin-list.js +++ b/waltz-ng/client/system/system-admin-list.js @@ -106,7 +106,7 @@ const referenceDataOptions= [ icon: "id-card-o" }, { name: "Measurable Categories", - role: "ADMIN", + role: "TAXONOMY_EDITOR", description: "View and edit measurable categories", state: "main.system.measurable-category.list", icon: "puzzle-piece"