diff --git a/waltz-data/src/main/java/org/finos/waltz/data/data_type/DataTypeDao.java b/waltz-data/src/main/java/org/finos/waltz/data/data_type/DataTypeDao.java index cccd7c77a8..28ac96530f 100644 --- a/waltz-data/src/main/java/org/finos/waltz/data/data_type/DataTypeDao.java +++ b/waltz-data/src/main/java/org/finos/waltz/data/data_type/DataTypeDao.java @@ -23,6 +23,7 @@ import org.finos.waltz.model.EntityLifecycleStatus; import org.finos.waltz.model.EntityReference; import org.finos.waltz.model.datatype.DataType; +import org.finos.waltz.model.datatype.DataTypeMigrationResult; import org.finos.waltz.model.datatype.ImmutableDataType; import org.finos.waltz.schema.tables.records.DataTypeRecord; import org.jooq.Condition; @@ -76,6 +77,12 @@ public DataTypeDao(DSLContext dsl) { this.dsl = dsl; } + public DataTypeMigrationResult migrate(Long fromId, Long toId, boolean deleteOldDataType) { + return dsl.transactionResult(ctx -> { + DSLContext tx = ctx.dsl(); + return DataTypeUtilities.migrate(tx, fromId, toId, deleteOldDataType); + }); + } public List findAll() { return dsl diff --git a/waltz-jobs/src/main/java/org/finos/waltz/jobs/tools/DataTypeUtilities.java b/waltz-data/src/main/java/org/finos/waltz/data/data_type/DataTypeUtilities.java similarity index 84% rename from waltz-jobs/src/main/java/org/finos/waltz/jobs/tools/DataTypeUtilities.java rename to waltz-data/src/main/java/org/finos/waltz/data/data_type/DataTypeUtilities.java index ff72fdd702..1c7fc2a055 100644 --- a/waltz-jobs/src/main/java/org/finos/waltz/jobs/tools/DataTypeUtilities.java +++ b/waltz-data/src/main/java/org/finos/waltz/data/data_type/DataTypeUtilities.java @@ -1,9 +1,12 @@ -package org.finos.waltz.jobs.tools; +package org.finos.waltz.data.data_type; +import org.finos.waltz.common.Checks; import org.finos.waltz.common.SetUtilities; import org.finos.waltz.model.EntityKind; import org.finos.waltz.model.EntityLifecycleStatus; import org.finos.waltz.model.EntityReference; +import org.finos.waltz.model.datatype.DataTypeMigrationResult; +import org.finos.waltz.model.datatype.ImmutableDataTypeMigrationResult; import org.finos.waltz.schema.Tables; import org.finos.waltz.schema.tables.DataType; import org.finos.waltz.schema.tables.DataTypeUsage; @@ -14,7 +17,6 @@ import org.finos.waltz.schema.tables.PhysicalSpecDataType; import org.finos.waltz.schema.tables.PhysicalSpecification; import org.finos.waltz.schema.tables.records.LogicalFlowRecord; -import org.finos.waltz.service.DIConfiguration; import org.jooq.Condition; import org.jooq.DSLContext; import org.jooq.impl.DSL; @@ -22,9 +24,7 @@ import org.jooq.lambda.tuple.Tuple2; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.AnnotationConfigApplicationContext; -import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Map; @@ -34,11 +34,12 @@ import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toSet; import static org.finos.waltz.model.EntityReference.mkRef; +import static org.finos.waltz.schema.Tables.DATA_TYPE; public class DataTypeUtilities { private static final Logger LOG = LoggerFactory.getLogger(DataTypeUtilities.class); - private static final DataType dataType = Tables.DATA_TYPE; + private static final DataType dataType = DATA_TYPE; private static final LogicalFlow logicalFlow = Tables.LOGICAL_FLOW; private static final LogicalFlowDecorator logicalFlowDecorator = Tables.LOGICAL_FLOW_DECORATOR; private static final FlowClassificationRule flowClassificationRule = Tables.FLOW_CLASSIFICATION_RULE; @@ -106,6 +107,15 @@ private static long removeDataType(DSLContext dsl, } + private static boolean removeDataType(DSLContext dsl, + long fromId) { + return dsl + .delete(dataType) + .where(dataType.ID.eq(fromId)) + .execute() > 0; + } + + public static void migrate(DSLContext dsl, String fromCode, String toCode, @@ -132,20 +142,41 @@ public static void migrate(DSLContext dsl, // 4) update flow_classification_rules set data_type_id = toId where data_type_id = fromId // x) delete from dataType where code = 'fromCode' - migrateDataTypeUsage(dsl, fromId, toId); - migrateFlowClassificationRules(dsl, fromId, toId); - migrateLogicalFlowDecorator(dsl, fromId, toId); - migratePhysicalSpecDataType(dsl, fromId, toId); + migrate(dsl, fromId, toId, deleteOldDataType); + + } + + private static void verifyDataTypeHasNoChildren(DSLContext dsl, Long fromId) { + int childCount = dsl.fetchCount(DATA_TYPE, DATA_TYPE.PARENT_ID.eq(fromId)); + Checks.checkTrue(childCount == 0, "Data Type, %d has %d children", fromId, childCount); + } + public static DataTypeMigrationResult migrate(DSLContext dsl, Long fromId, Long toId, boolean deleteOldDataType) { if (deleteOldDataType) { - removeDataType(dsl, fromCode); + verifyDataTypeHasNoChildren(dsl, fromId); } + int dtuCount = migrateDataTypeUsage(dsl, fromId, toId); + int crCount = migrateFlowClassificationRules(dsl, fromId, toId); + int lfCount = migrateLogicalFlowDecorator(dsl, fromId, toId); + int psCount = migratePhysicalSpecDataType(dsl, fromId, toId); + boolean dataTypeRemoved = deleteOldDataType + ? removeDataType(dsl, fromId) + : false; + + return ImmutableDataTypeMigrationResult + .builder() + .usageCount(dtuCount) + .classificationRuleCount(crCount) + .logicalFlowDataTypeCount(lfCount) + .physicalSpecDataTypeCount(psCount) + .dataTypeRemoved(dataTypeRemoved) + .build(); } - private static void migratePhysicalSpecDataType(DSLContext dsl, - Long fromId, - Long toId) { + private static int migratePhysicalSpecDataType(DSLContext dsl, + Long fromId, + Long toId) { PhysicalSpecDataType physicSpec = physicalSpecDataType.as("physicSpec"); Condition notAlreadyExists = DSL .notExists(DSL @@ -167,12 +198,13 @@ private static void migratePhysicalSpecDataType(DSLContext dsl, .execute(); LOG.info("Migrate Phys Spec Data Type Usage: {} -> {}, updated: {}, removed: {}", fromId, toId, updateCount, rmCount); + return updateCount + rmCount; } - private static void migrateLogicalFlowDecorator(DSLContext dsl, - Long fromId, - Long toId) { + private static int migrateLogicalFlowDecorator(DSLContext dsl, + Long fromId, + Long toId) { LogicalFlowDecorator decorator = logicalFlowDecorator.as("decorator"); Condition notAlreadyExists = DSL.notExists(DSL @@ -197,12 +229,14 @@ private static void migrateLogicalFlowDecorator(DSLContext dsl, .execute(); LOG.info("Migrate Logical Flow Decorator: {} -> {}, updated: {}, removed: {}", fromId, toId, updateCount, rmCount); + + return updateCount + rmCount; } - private static void migrateFlowClassificationRules(DSLContext dsl, - Long fromId, - Long toId) { + private static int migrateFlowClassificationRules(DSLContext dsl, + Long fromId, + Long toId) { FlowClassificationRule authSrc = flowClassificationRule.as("authSrc"); @@ -228,12 +262,14 @@ private static void migrateFlowClassificationRules(DSLContext dsl, .execute(); LOG.info("Migrate Flow Classification Rules: {} -> {}, updated: {}, removed: {}", fromId, toId, updateCount, rmCount); + return updateCount + rmCount; + } - private static void migrateDataTypeUsage(DSLContext dsl, - Long fromId, - Long toId) { + private static int migrateDataTypeUsage(DSLContext dsl, + Long fromId, + Long toId) { DataTypeUsage dtu = dataTypeUsage.as("dtu"); Condition condition = DSL.notExists(DSL @@ -257,6 +293,8 @@ private static void migrateDataTypeUsage(DSLContext dsl, .execute(); LOG.info("Migrate DataType Usage: {} -> {}, updated: {}, removed: {}", fromId, toId, updateCount, rmCount); + + return updateCount + rmCount; } public static List findLogicalFlowIdsForDataType(DSLContext dsl, Long datatype, Set logicalFlowIds) { @@ -366,26 +404,4 @@ public static long markDataTypeAsConcrete(DSLContext dsl, .where(dataType.CODE.in(dataTypeCodes)) .execute(); } - - - public static void main(String[] args) throws IOException { - - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(DIConfiguration.class); - DSLContext dsl = ctx.getBean(DSLContext.class); - - String party = "DataTypeCode1"; - String allocation = "DataTypeCode2"; - String instrumentIdentifier = "DataTypeCode3"; - String prospect = "DataTypeCode4"; - String dealEvent = "DataTypeCode5"; - String instrumentStatic = "DataTypeCode6"; - - dsl.transaction(context -> { - DSLContext tx = context.dsl(); - migrate(tx, instrumentIdentifier, instrumentStatic, false); - migrate(tx, prospect, party, false); - migrate(tx, allocation, dealEvent, false); - //throw new RuntimeException("BoooM!"); - }); - } } diff --git a/waltz-model/src/main/java/org/finos/waltz/model/datatype/DataTypeMigrationResult.java b/waltz-model/src/main/java/org/finos/waltz/model/datatype/DataTypeMigrationResult.java new file mode 100644 index 0000000000..923cebbd6e --- /dev/null +++ b/waltz-model/src/main/java/org/finos/waltz/model/datatype/DataTypeMigrationResult.java @@ -0,0 +1,14 @@ +package org.finos.waltz.model.datatype; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.immutables.value.Value; + +@Value.Immutable +@JsonSerialize(as=ImmutableDataTypeMigrationResult.class) +public interface DataTypeMigrationResult { + long usageCount(); + long classificationRuleCount(); + long logicalFlowDataTypeCount(); + long physicalSpecDataTypeCount(); + boolean dataTypeRemoved(); +} diff --git a/waltz-ng/client/data-types/components/maintain/DataTypeMaintain.svelte b/waltz-ng/client/data-types/components/maintain/DataTypeMaintain.svelte new file mode 100644 index 0000000000..64ac6610e3 --- /dev/null +++ b/waltz-ng/client/data-types/components/maintain/DataTypeMaintain.svelte @@ -0,0 +1,241 @@ + + + + +
+
    +
  1. + Home +
  2. +
  3. + Data Types +
  4. +
  5. + Maintain +
  6. +
+
+
+ +
+
+
+ Select a data type you wish to modify. +
+
+
+ From + +
+
+ {#if sourceDataType} + +
+ Actions +
+
+ Selected Data Type: +
+ Migrate +
+
+ This action migrates data type usage from the source data type to a destination data type. + You may optionally remove the source data type when the migration is completed. +
+ {#if mode === Modes.MIGRATE_PICKER} +
Select a data type to merge into, this must be concrete and non-deprecated
+ + {/if} + {#if mode === Modes.MIGRATE_OPTIONS} +
If you choose to remove the source data type, be aware this cannot be undone
+
+ To + +
+ +
+ + +
+ + + {/if} + {#if mode === Modes.MIGRATE_CONFIRM} +
+ Are you sure you wish to migrate from to ? + {#if removeSource} +
+ Once migration is complete the source data type will be removed + {/if} +
+ + +
+ {/if} + {#if mode === Modes.WAITING} +

Please wait...

+ {/if} + {#if mode === Modes.MIGRATE_RESULT} + + + + + + + + + + + + + + + + + + + + + + +
Logical Flow Data Types Changed{migrateResult.logicalFlowDataTypeCount}
Physical Specification Data Types Changed{migrateResult.physicalSpecDataTypeCount}
Classification Rules Changed{migrateResult.classificationRuleCount}
Data Type Usages Changed{migrateResult.usageCount}
Source Data Type Removed?{migrateResult.dataTypeRemoved}
+ + {/if} +
+
+
+
+ {:else} + +
+ Pending changes +
+
+ Waiting for Selection... +
+
+ {/if} +
+
+
+
diff --git a/waltz-ng/client/data-types/index.js b/waltz-ng/client/data-types/index.js index dbcca0c218..a75ad87078 100644 --- a/waltz-ng/client/data-types/index.js +++ b/waltz-ng/client/data-types/index.js @@ -31,6 +31,7 @@ import RelatedDataTypesSection from './components/related-data-types-section/rel import DataTypesDecoratorSection from './components/data-type-decorator-section/data-type-decorator-section'; import Routes from './routes'; import DataTypeViewData from './services/data-type-view-data'; +import DataTypeMaintain from './pages/maintain/data-type-maintain'; export default () => { const module = angular.module('waltz.data.types', []); @@ -47,7 +48,8 @@ export default () => { DataTypeUsageSelector, DataTypeUsageTree, RelatedDataTypesSection, - DataTypesDecoratorSection + DataTypesDecoratorSection, + DataTypeMaintain ]); module diff --git a/waltz-ng/client/data-types/pages/home/data-type-home.html b/waltz-ng/client/data-types/pages/home/data-type-home.html index cc57366e88..9b8afba771 100644 --- a/waltz-ng/client/data-types/pages/home/data-type-home.html +++ b/waltz-ng/client/data-types/pages/home/data-type-home.html @@ -18,7 +18,7 @@
- @@ -29,7 +29,7 @@ Home -
  • Data
  • +
  • Data Types
  • @@ -76,6 +76,13 @@ +
    +
    diff --git a/waltz-ng/client/data-types/pages/maintain/data-type-maintain.html b/waltz-ng/client/data-types/pages/maintain/data-type-maintain.html new file mode 100644 index 0000000000..aa1bb86255 --- /dev/null +++ b/waltz-ng/client/data-types/pages/maintain/data-type-maintain.html @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/waltz-ng/client/data-types/pages/maintain/data-type-maintain.js b/waltz-ng/client/data-types/pages/maintain/data-type-maintain.js new file mode 100644 index 0000000000..11fab18a53 --- /dev/null +++ b/waltz-ng/client/data-types/pages/maintain/data-type-maintain.js @@ -0,0 +1,51 @@ +/* + * 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 angular from "angular"; +import template from "./data-type-maintain.html"; +import {initialiseData} from "../../../common"; +import {CORE_API} from "../../../common/services/core-api-utils"; +import DataTypeMaintain from "../../components/maintain/DataTypeMaintain.svelte" + + +const bindings = {} + +const initialState = { + DataTypeMaintain +}; + + +function controller() { + + const vm = initialiseData(this, initialState); +} + + +controller.$inject = []; + + +const component = { + template, + controller, + bindings +} + +export default { + component, + id: "waltzDataTypeMaintain", +}; \ No newline at end of file diff --git a/waltz-ng/client/data-types/routes.js b/waltz-ng/client/data-types/routes.js index 2c026cb5a2..86eb55c757 100644 --- a/waltz-ng/client/data-types/routes.js +++ b/waltz-ng/client/data-types/routes.js @@ -20,6 +20,7 @@ import {loadDataTypes, dataTypeByCodeResolver, dataTypeByIdResolver} from "./res import HomePage from './pages/home/data-type-home'; import ViewPage from './pages/view/data-type-view'; +import MaintainPage from './pages/maintain/data-type-maintain'; const baseState = { @@ -54,6 +55,12 @@ const viewState = { resolve: {dataType: dataTypeByIdResolver } }; +const maintainState = { + url: 'data-types/maintain', + views: {'content@': MaintainPage.id } +}; + + function setup($stateProvider) { $stateProvider @@ -61,7 +68,8 @@ function setup($stateProvider) { .state('main.data-type.list', listState) .state('main.data-type.code', viewByCodeState) .state('main.data-type.external-id', viewByExternalIdState) - .state('main.data-type.view', viewState); + .state('main.data-type.view', viewState) + .state('main.data-type.maintain', maintainState); } setup.$inject = ['$stateProvider']; diff --git a/waltz-ng/client/svelte-stores/data-type-store.js b/waltz-ng/client/svelte-stores/data-type-store.js index f7d247633b..03386f3c69 100644 --- a/waltz-ng/client/svelte-stores/data-type-store.js +++ b/waltz-ng/client/svelte-stores/data-type-store.js @@ -52,13 +52,21 @@ export function mkDataTypeStore() { `api/data-types/suggested/entity/${ref.kind}/${ref.id}`, null, {force}); - } + }; + + const migrate = (sourceDataTypeId, targetDataTypeId, removeSource = false) => { + return remote.execute( + "POST", + `api/data-types/migrate/${sourceDataTypeId}/to/${targetDataTypeId}?removeSource=${removeSource}`, + null); + }; return { findAll, getById, findByParentId, - findSuggestedByRef + findSuggestedByRef, + migrate }; } diff --git a/waltz-service/src/main/java/org/finos/waltz/service/data_type/DataTypeService.java b/waltz-service/src/main/java/org/finos/waltz/service/data_type/DataTypeService.java index 26798bf2a8..48780e8803 100644 --- a/waltz-service/src/main/java/org/finos/waltz/service/data_type/DataTypeService.java +++ b/waltz-service/src/main/java/org/finos/waltz/service/data_type/DataTypeService.java @@ -27,6 +27,7 @@ import org.finos.waltz.model.EntityReference; import org.finos.waltz.model.IdSelectionOptions; import org.finos.waltz.model.datatype.DataType; +import org.finos.waltz.model.datatype.DataTypeMigrationResult; import org.finos.waltz.model.entity_search.EntitySearchOptions; import org.jooq.Record1; import org.jooq.Select; @@ -68,12 +69,16 @@ public DataTypeService(DataTypeDao dataTypeDao, this.logicalFlowDao = logicalFlowDao; } + public DataTypeMigrationResult migrate(Long fromId, Long toId, boolean deleteOldDataType) { + return dataTypeDao.migrate(fromId, toId, deleteOldDataType); + } public List findAll() { return dataTypeDao.findAll(); } + public DataType getDataTypeById(long dataTypeId) { return dataTypeDao.getById(dataTypeId); } diff --git a/waltz-web/src/main/java/org/finos/waltz/web/endpoints/api/DataTypesEndpoint.java b/waltz-web/src/main/java/org/finos/waltz/web/endpoints/api/DataTypesEndpoint.java index 31c8e2fccc..8ffe0544b4 100644 --- a/waltz-web/src/main/java/org/finos/waltz/web/endpoints/api/DataTypesEndpoint.java +++ b/waltz-web/src/main/java/org/finos/waltz/web/endpoints/api/DataTypesEndpoint.java @@ -18,7 +18,10 @@ package org.finos.waltz.web.endpoints.api; +import org.finos.waltz.model.datatype.DataTypeMigrationResult; +import org.finos.waltz.model.user.SystemRole; import org.finos.waltz.service.data_type.DataTypeService; +import org.finos.waltz.service.user.UserRoleService; import org.finos.waltz.web.DatumRoute; import org.finos.waltz.web.ListRoute; import org.finos.waltz.web.WebUtilities; @@ -28,11 +31,9 @@ import org.finos.waltz.model.entity_search.EntitySearchOptions; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import spark.Request; -import static org.finos.waltz.web.WebUtilities.getEntityReference; -import static org.finos.waltz.web.WebUtilities.getId; -import static org.finos.waltz.web.WebUtilities.mkPath; -import static org.finos.waltz.web.WebUtilities.readBody; +import static org.finos.waltz.web.WebUtilities.*; import static org.finos.waltz.web.endpoints.EndpointUtilities.*; import static java.lang.Long.parseLong; import static org.finos.waltz.common.Checks.checkNotNull; @@ -43,12 +44,15 @@ public class DataTypesEndpoint implements Endpoint { private static final String BASE_URL = WebUtilities.mkPath("api", "data-types"); - private final DataTypeService service; + private final DataTypeService dataTypeService; + private final UserRoleService userRoleService; @Autowired - public DataTypesEndpoint(DataTypeService service) { - checkNotNull(service, "service must not be null"); - this.service = service; + public DataTypesEndpoint(DataTypeService dataTypeService, UserRoleService userRoleService) { + checkNotNull(dataTypeService, "dataTypeService must not be null"); + checkNotNull(userRoleService, "userRoleService must not be null"); + this.userRoleService = userRoleService; + this.dataTypeService = dataTypeService; } @@ -59,30 +63,45 @@ public void register() { String getDataTypeByCodePath = mkPath(BASE_URL, "code", ":code"); String findSuggestedByEntityRefPath = mkPath(BASE_URL, "suggested", "entity", ":kind", ":id"); String findByParentIdPath = mkPath(BASE_URL, "parent-id", ":id"); + String migratePath = mkPath(BASE_URL, "migrate", ":sourceDataTypeId", "to", ":targetDataTypeId"); ListRoute searchRoute = (request, response) -> - service.search(EntitySearchOptions + dataTypeService.search(EntitySearchOptions .mkForEntity(EntityKind.DATA_TYPE, readBody(request, String.class))); DatumRoute getDataTypeByIdRoute = (request, response) -> - service.getDataTypeById(parseLong(request.params("id"))); + dataTypeService.getDataTypeById(parseLong(request.params("id"))); DatumRoute getDataTypeByCodeRoute = (request, response) -> - service.getDataTypeByCode(request.params("code")); + dataTypeService.getDataTypeByCode(request.params("code")); ListRoute findSuggestedByEntityRefRoute = (req, res) -> - service.findSuggestedByEntityRef(getEntityReference(req)); + dataTypeService.findSuggestedByEntityRef(getEntityReference(req)); ListRoute findByParentIdRoute = (req, res) -> - service.findByParentId(getId(req)); + dataTypeService.findByParentId(getId(req)); + DatumRoute migrateRoute = (request, response) -> { + ensureUserHasMigrateAdminRights(request); + long sourceDataTypeId = getLong(request, "sourceDataTypeId"); + long targetDataTypeId = getLong(request, "targetDataTypeId"); + boolean removeSource = Boolean.parseBoolean(request.queryParams("removeSource")); + return dataTypeService.migrate(sourceDataTypeId, targetDataTypeId, removeSource); + }; - getForList(BASE_URL, (request, response) -> service.findAll()); + getForList(BASE_URL, (request, response) -> dataTypeService.findAll()); postForList(searchPath, searchRoute); getForDatum(getDataTypeByIdPath, getDataTypeByIdRoute); getForDatum(getDataTypeByCodePath, getDataTypeByCodeRoute); getForList(findSuggestedByEntityRefPath, findSuggestedByEntityRefRoute); getForList(findByParentIdPath, findByParentIdRoute); + postForDatum(migratePath, migrateRoute); + } + + // -- HELPERS --- + + private void ensureUserHasMigrateAdminRights(Request request) { + requireRole(userRoleService, request, SystemRole.ADMIN); }