diff --git a/waltz-data/src/main/java/org/finos/waltz/data/end_user_app/EndUserAppDao.java b/waltz-data/src/main/java/org/finos/waltz/data/end_user_app/EndUserAppDao.java index 987c59b178..a65099a62f 100644 --- a/waltz-data/src/main/java/org/finos/waltz/data/end_user_app/EndUserAppDao.java +++ b/waltz-data/src/main/java/org/finos/waltz/data/end_user_app/EndUserAppDao.java @@ -22,6 +22,7 @@ import org.finos.waltz.model.application.LifecyclePhase; import org.finos.waltz.model.enduserapp.EndUserApplication; import org.finos.waltz.model.enduserapp.ImmutableEndUserApplication; +import org.finos.waltz.model.entity_search.EntitySearchOptions; import org.finos.waltz.model.physical_flow.CriticalityValue; import org.finos.waltz.model.tally.Tally; import org.finos.waltz.schema.tables.records.EndUserApplicationRecord; @@ -34,6 +35,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; +import java.util.Collection; import java.util.List; import static java.util.Optional.ofNullable; @@ -123,4 +125,5 @@ public List findAll() { .where(COMMON_CONDITION) .fetch(TO_DOMAIN_MAPPER); } + } diff --git a/waltz-data/src/main/java/org/finos/waltz/data/end_user_app/search/EndUserAppSearchDao.java b/waltz-data/src/main/java/org/finos/waltz/data/end_user_app/search/EndUserAppSearchDao.java new file mode 100644 index 0000000000..748f9e7d04 --- /dev/null +++ b/waltz-data/src/main/java/org/finos/waltz/data/end_user_app/search/EndUserAppSearchDao.java @@ -0,0 +1,66 @@ +package org.finos.waltz.data.end_user_app.search; + +import org.finos.waltz.data.SearchDao; +import org.finos.waltz.data.end_user_app.EndUserAppDao; +import org.finos.waltz.model.enduserapp.EndUserApplication; +import org.finos.waltz.model.entity_search.EntitySearchOptions; +import org.jooq.Condition; +import org.jooq.DSLContext; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +import java.util.List; + +import static java.util.Collections.emptyList; +import static org.finos.waltz.common.ListUtilities.concat; +import static org.finos.waltz.data.JooqUtilities.mkBasicTermSearch; +import static org.finos.waltz.data.JooqUtilities.mkStartsWithTermSearch; +import static org.finos.waltz.data.SearchUtilities.mkTerms; +import static org.finos.waltz.schema.Tables.END_USER_APPLICATION; + + +@Repository +public class EndUserAppSearchDao implements SearchDao { + + private final DSLContext dsl; + + + @Autowired + public EndUserAppSearchDao(DSLContext dsl) { + this.dsl = dsl; + } + + + /** + * Searches by name and external_id + * @param options + * @return List of matching end user applications, + * matches on name are given precedence over external_id matches + */ + @Override + public List search(EntitySearchOptions options) { + List terms = mkTerms(options.searchQuery()); + if (terms.isEmpty()) { + return emptyList(); + } + + Condition nameCondition = mkBasicTermSearch(END_USER_APPLICATION.NAME, terms); + Condition externalIdCondition = mkStartsWithTermSearch(END_USER_APPLICATION.EXTERNAL_ID, terms); + + return concat( + mkQuery(nameCondition, options), + mkQuery(externalIdCondition, options)); + } + + + private List mkQuery(Condition nameCondition, EntitySearchOptions options) { + return dsl + .select(END_USER_APPLICATION.fields()) + .from(END_USER_APPLICATION) + .where(nameCondition) + .orderBy(END_USER_APPLICATION.NAME) + .limit(options.limit()) + .fetch(EndUserAppDao.TO_DOMAIN_MAPPER); + } + +} \ No newline at end of file diff --git a/waltz-model/src/main/java/org/finos/waltz/model/enduserapp/EndUserApplication.java b/waltz-model/src/main/java/org/finos/waltz/model/enduserapp/EndUserApplication.java index 530e5ca206..4f25f16ce4 100644 --- a/waltz-model/src/main/java/org/finos/waltz/model/enduserapp/EndUserApplication.java +++ b/waltz-model/src/main/java/org/finos/waltz/model/enduserapp/EndUserApplication.java @@ -34,8 +34,8 @@ public abstract class EndUserApplication implements DescriptionProvider, ExternalIdProvider, NameProvider, - ProvenanceProvider { - + ProvenanceProvider, + WaltzEntity { public abstract Long organisationalUnitId(); public abstract String applicationKind(); public abstract LifecyclePhase lifecyclePhase(); @@ -45,11 +45,12 @@ public abstract class EndUserApplication implements @Value.Default public EntityKind kind() { return EntityKind.END_USER_APPLICATION; } - public EntityReference toEntityReference() { + public EntityReference entityReference() { return ImmutableEntityReference.builder() .kind(EntityKind.END_USER_APPLICATION) .id(id().get()) .name(name()) + .description(description()) .build(); } } \ No newline at end of file diff --git a/waltz-ng/client/common/link-utils.js b/waltz-ng/client/common/link-utils.js index 08667ccada..5e2541b1f8 100644 --- a/waltz-ng/client/common/link-utils.js +++ b/waltz-ng/client/common/link-utils.js @@ -33,6 +33,7 @@ const stateKindTuples = [ {kind: "CHANGE_INITIATIVE", state: "main.change-initiative.view"}, {kind: "CHANGE_SET", state: "main.change-set.view"}, {kind: "DATA_TYPE", state: "main.data-type.view"}, + {kind: "END_USER_APPLICATION", state: "main.end-user-application.view"}, {kind: "ENTITY_RELATIONSHIP", state: "main.entity-relationship.view"}, {kind: "ENTITY_STATISTIC", state: "main.entity-statistic.view"}, {kind: "FLOW_DIAGRAM", state: "main.flow-diagram.view"}, diff --git a/waltz-ng/client/common/svelte/EntityLink.svelte b/waltz-ng/client/common/svelte/EntityLink.svelte index 9c35ce6fb5..a6aabc69ae 100644 --- a/waltz-ng/client/common/svelte/EntityLink.svelte +++ b/waltz-ng/client/common/svelte/EntityLink.svelte @@ -2,6 +2,7 @@ import ViewLink from "./ViewLink.svelte"; import EntityLabel from "./EntityLabel.svelte"; import {kindToViewState} from "../link-utils"; + import _ from "lodash"; /** * Entity Link takes an entity ref @@ -22,11 +23,11 @@ $: { try { - state = ref - ? kindToViewState(ref.kind) - : null; + state = _.isEmpty(ref) + ? null + : kindToViewState(ref.kind); } catch(e){ - console.error(e); + console.error(e, "bad ref?", ref); } } diff --git a/waltz-ng/client/dynamic-section/dynamic-section-definitions.js b/waltz-ng/client/dynamic-section/dynamic-section-definitions.js index 020cdf8b68..f53a2d61db 100644 --- a/waltz-ng/client/dynamic-section/dynamic-section-definitions.js +++ b/waltz-ng/client/dynamic-section/dynamic-section-definitions.js @@ -899,6 +899,16 @@ const legalEntityRelationshipSections = [ changeLogSection ]; +const endUserApplicationSections = [ + assessmentRatingSection, + bookmarksSection, + entityNamedNotesSection, + involvedPeopleSection, + dataFlowSection, + entityDiagramsSection, + changeLogSection +]; + export const dynamicSectionsByKind = { "main.actor.view": actorSections, @@ -916,6 +926,7 @@ export const dynamicSectionsByKind = { "main.database.external-id": databaseSections, "main.database.view": databaseSections, "main.entity-relationship.view": entityRelationshipSections, + "main.end-user-application.view": endUserApplicationSections, "main.flow-classification-rule.view": flowClassificationRuleSections, "main.flow-diagram.view": flowDiagramSections, "main.involvement-kind.view": involvementKindSections, diff --git a/waltz-ng/client/end-user-apps/index.js b/waltz-ng/client/end-user-apps/index.js index 2ff83f3ed7..f443c211b6 100644 --- a/waltz-ng/client/end-user-apps/index.js +++ b/waltz-ng/client/end-user-apps/index.js @@ -16,13 +16,17 @@ * */ -import angular from 'angular'; -import * as endUserAppStore from './services/end-user-app-store'; -import {registerStore} from '../common/module-utils'; +import angular from "angular"; +import * as endUserAppStore from "./services/end-user-app-store"; +import {registerStore} from "../common/module-utils"; +import Routes from "./routes"; export default () => { - const module = angular.module('waltz.end.user.apps', []); + const module = angular.module("waltz.end.user.apps", []); + + module + .config(Routes); registerStore(module, endUserAppStore); diff --git a/waltz-ng/client/end-user-apps/pages/end-user-application-view.js b/waltz-ng/client/end-user-apps/pages/end-user-application-view.js new file mode 100644 index 0000000000..66fdadc5a1 --- /dev/null +++ b/waltz-ng/client/end-user-apps/pages/end-user-application-view.js @@ -0,0 +1,65 @@ +import EndUserApplicationOverview + from "../svelte/EndUserApplicationOverview.svelte"; +import {initialiseData} from "../../common"; +import {CORE_API} from "../../common/services/core-api-utils"; + +const initialState = { + EndUserApplicationOverview, + parentEntityRef: null, + endUserApplication: null +}; + + +const addToHistory = (historyStore, ref) => { + if (! ref) { return; } + historyStore.put( + ref.name, + "END_USER_APPLICATION", + "main.end-user-application.view", + { id: ref.id }); +}; + + +function controller($stateParams, historyStore, serviceBroker) { + + const vm = initialiseData(this, initialState); + + const endUserAppId = $stateParams.id; + + vm.parentEntityRef = { id: endUserAppId, kind: "END_USER_APPLICATION" }; + + serviceBroker + .loadViewData( + CORE_API.EndUserAppStore.getById, + [endUserAppId]) + .then(r => { + vm.endUserApplication = r.data; + addToHistory(historyStore, vm.endUserApplication); + }); +} + + +controller.$inject = [ + "$stateParams", + "HistoryStore", + "ServiceBroker" +]; + +const template = ` +
+ + + +
+ + + +
`; + +export default { + template, + controller, + controllerAs: "$ctrl", + bindToController: true, +}; \ No newline at end of file diff --git a/waltz-ng/client/end-user-apps/routes.js b/waltz-ng/client/end-user-apps/routes.js new file mode 100644 index 0000000000..e7a1789983 --- /dev/null +++ b/waltz-ng/client/end-user-apps/routes.js @@ -0,0 +1,41 @@ +/* + * 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 EndUserApplicationView from "./pages/end-user-application-view"; + +const baseState = {}; + + +const viewState = { + url: "end-user-application/id/{id:int}", + views: { + "content@": EndUserApplicationView + } + +}; + +function setup($stateProvider) { + $stateProvider + .state("main.end-user-application", baseState) + .state("main.end-user-application.view", viewState); +} + +setup.$inject = ["$stateProvider"]; + + +export default setup; \ No newline at end of file diff --git a/waltz-ng/client/end-user-apps/services/end-user-app-store.js b/waltz-ng/client/end-user-apps/services/end-user-app-store.js index 9a1cb8f463..aa02d5f989 100644 --- a/waltz-ng/client/end-user-apps/services/end-user-app-store.js +++ b/waltz-ng/client/end-user-apps/services/end-user-app-store.js @@ -38,12 +38,17 @@ export function store($http, baseUrl) { .get(`${BASE}`) .then(result => result.data); + const getById = (id) => $http + .get(`${BASE}/id/${id}`) + .then(result => result.data); + return { findBySelector, countByOrganisationalUnit, promoteToApplication, - findAll + findAll, + getById }; } @@ -73,6 +78,11 @@ export const EndUserAppStore_API = { serviceName, serviceFnName: 'findAll', description: 'returns all EUDAs that are not promoted' + }, + getById: { + serviceName, + serviceFnName: 'getById', + description: 'returns specific EUDA based on id' } }; diff --git a/waltz-ng/client/end-user-apps/svelte/EndUserApplicationOverview.svelte b/waltz-ng/client/end-user-apps/svelte/EndUserApplicationOverview.svelte new file mode 100644 index 0000000000..00768b7a70 --- /dev/null +++ b/waltz-ng/client/end-user-apps/svelte/EndUserApplicationOverview.svelte @@ -0,0 +1,151 @@ + + + + + Waltz: {endUserApplication?.name} - End User Application + + + +{#if endUserApplication} + + +
+
    +
  1. Home
  2. +
  3. End User Application
  4. +
  5. {endUserApplication.name}
  6. +
+
+
+ + +
+ {#if promotedApp} +
+
+
+ + This End User Application record has been converted to a full Application record. +
+ See: +
+
+
+ {/if} +
+
+
+
+ Name +
+
+ {endUserApplication.name} +
+
+
+
+ External Id +
+
+ {endUserApplication.externalId} +
+
+
+
+ Owning Org Unit +
+
+ +
+
+
+
+ Kind +
+
+ {endUserApplication.applicationKind} +
+
+
+
+ Lifecycle Phase +
+
+ {endUserApplication.lifecyclePhase} +
+
+
+
+ Risk Rating +
+
+ {endUserApplication.riskRating} +
+
+
+
+ Description +
+
+ +
+
+
+
+ +
+ Assessments +
+
+ +
+
+
+ +
+
+
+
+
+
+{/if} \ No newline at end of file diff --git a/waltz-ng/client/navbar/components/nav-search-overlay/nav-search-overlay.js b/waltz-ng/client/navbar/components/nav-search-overlay/nav-search-overlay.js index 450b6d662d..c667eb79de 100644 --- a/waltz-ng/client/navbar/components/nav-search-overlay/nav-search-overlay.js +++ b/waltz-ng/client/navbar/components/nav-search-overlay/nav-search-overlay.js @@ -47,6 +47,7 @@ const initialState = { entity.MEASURABLE.key, entity.DATA_TYPE.key, entity.PHYSICAL_SPECIFICATION.key, + entity.END_USER_APPLICATION.key, entity.LEGAL_ENTITY.key, entity.SERVER.key, entity.DATABASE.key, @@ -148,7 +149,7 @@ function controller($element, vm.searching = true; handleSearch(query, [entity.APPLICATION.key, entity.PERSON.key]) .then(() => handleSearch(query, [entity.APP_GROUP.key, entity.CHANGE_INITIATIVE.key, entity.ORG_UNIT.key])) - .then(() => handleSearch(query, [entity.ACTOR.key, entity.MEASURABLE.key, entity.LEGAL_ENTITY.key])) + .then(() => handleSearch(query, [entity.ACTOR.key, entity.MEASURABLE.key, entity.LEGAL_ENTITY.key, entity.END_USER_APPLICATION.key])) .then(() => handleSearch(query, [entity.PHYSICAL_SPECIFICATION.key, entity.DATA_TYPE.key, entity.SERVER.key, entity.DATABASE.key])) .then(() => handleSearch(query, [entity.SOFTWARE.key, entity.ROADMAP.key, entity.LOGICAL_DATA_ELEMENT.key, entity.LICENCE.key])) .catch(e => displayError("Failed to search")) diff --git a/waltz-ng/client/svelte-stores/application-store.js b/waltz-ng/client/svelte-stores/application-store.js index 3373835cba..e2f8f047ef 100644 --- a/waltz-ng/client/svelte-stores/application-store.js +++ b/waltz-ng/client/svelte-stores/application-store.js @@ -36,10 +36,16 @@ export function mkApplicationStore() { .fetchViewDatum("POST", "api/app/view/selector", selector); + const findByExternalId = (extId) => remote + .fetchViewList("GET", `api/app/external-id/${extId}`, null ); + + + return { getById, update, registerApp, + findByExternalId, findBySelector, getViewBySelector }; diff --git a/waltz-ng/client/svelte-stores/bookmark-store.js b/waltz-ng/client/svelte-stores/bookmark-store.js index b0cb99fc84..c2bd48bbbd 100644 --- a/waltz-ng/client/svelte-stores/bookmark-store.js +++ b/waltz-ng/client/svelte-stores/bookmark-store.js @@ -17,12 +17,16 @@ */ import {remote} from "./remote"; +import {toEntityRef} from "../common/entity-utils"; function stripExtraneousFields(bookmark) { - return _.pick( - bookmark, - ["id", "bookmarkKind", "url", "title", "isRestricted", "parent", "description", "lastUpdatedBy"]); + const simplifiedBookmark = _.pick(bookmark, ["id", "bookmarkKind", "url", "title", "isRestricted", "parent", "description", "lastUpdatedBy"]); + const simplifiedParent = {parent: toEntityRef(bookmark.parent)}; + return Object.assign( + {}, + simplifiedBookmark, + simplifiedParent); } diff --git a/waltz-service/src/main/java/org/finos/waltz/service/end_user_app/EndUserAppService.java b/waltz-service/src/main/java/org/finos/waltz/service/end_user_app/EndUserAppService.java index b86c6cb647..522ca248a9 100644 --- a/waltz-service/src/main/java/org/finos/waltz/service/end_user_app/EndUserAppService.java +++ b/waltz-service/src/main/java/org/finos/waltz/service/end_user_app/EndUserAppService.java @@ -23,6 +23,7 @@ import org.finos.waltz.data.changelog.ChangeLogDao; import org.finos.waltz.data.end_user_app.EndUserAppDao; import org.finos.waltz.data.end_user_app.EndUserAppIdSelectorFactory; +import org.finos.waltz.data.end_user_app.search.EndUserAppSearchDao; import org.finos.waltz.data.involvement.InvolvementDao; import org.finos.waltz.data.orgunit.OrganisationalUnitIdSelectorFactory; import org.finos.waltz.model.Criticality; @@ -38,6 +39,7 @@ import org.finos.waltz.model.changelog.ChangeLogComment; import org.finos.waltz.model.changelog.ImmutableChangeLog; import org.finos.waltz.model.enduserapp.EndUserApplication; +import org.finos.waltz.model.entity_search.EntitySearchOptions; import org.finos.waltz.model.involvement.ImmutableInvolvement; import org.finos.waltz.model.involvement.Involvement; import org.finos.waltz.model.rating.RagRating; @@ -64,6 +66,7 @@ public class EndUserAppService { private final ApplicationDao applicationDao; private final ChangeLogDao changeLogDao; private final InvolvementDao involvementDao; + private final EndUserAppSearchDao endUserAppSearchDao; private final EndUserAppIdSelectorFactory endUserAppIdSelectorFactory = new EndUserAppIdSelectorFactory(); private final OrganisationalUnitIdSelectorFactory orgUnitIdSelectorFactory= new OrganisationalUnitIdSelectorFactory(); @@ -72,15 +75,18 @@ public class EndUserAppService { public EndUserAppService(EndUserAppDao endUserAppDao, ApplicationDao applicationDao, ChangeLogDao changeLogDao, - InvolvementDao involvementDao) { + InvolvementDao involvementDao, + EndUserAppSearchDao endUserAppSearchDao) { checkNotNull(endUserAppDao, "EndUserAppDao is required"); checkNotNull(applicationDao, "ApplicationDao is required"); checkNotNull(changeLogDao, "ChangeLogDao is required"); checkNotNull(involvementDao, "InvolvementDao is required"); + checkNotNull(endUserAppSearchDao, "EndUserAppSearchDao is required"); this.endUserAppDao = endUserAppDao; this.applicationDao = applicationDao; this.changeLogDao = changeLogDao; this.involvementDao = involvementDao; + this.endUserAppSearchDao = endUserAppSearchDao; } @@ -190,4 +196,7 @@ private AppRegistrationRequest mkAppRegistrationRequestForEuda(Long id) { } } + public Collection search(EntitySearchOptions options) { + return endUserAppSearchDao.search(options); + } } diff --git a/waltz-service/src/main/java/org/finos/waltz/service/entity_search/EntitySearchService.java b/waltz-service/src/main/java/org/finos/waltz/service/entity_search/EntitySearchService.java index 974b0e5ef8..b8773c3ae9 100644 --- a/waltz-service/src/main/java/org/finos/waltz/service/entity_search/EntitySearchService.java +++ b/waltz-service/src/main/java/org/finos/waltz/service/entity_search/EntitySearchService.java @@ -24,6 +24,7 @@ import org.finos.waltz.service.change_initiative.ChangeInitiativeService; import org.finos.waltz.service.data_type.DataTypeService; import org.finos.waltz.service.database_information.DatabaseInformationService; +import org.finos.waltz.service.end_user_app.EndUserAppService; import org.finos.waltz.service.flow_diagram.FlowDiagramService; import org.finos.waltz.service.legal_entity.LegalEntityService; import org.finos.waltz.service.licence.LicenceService; @@ -65,6 +66,7 @@ public class EntitySearchService { private final ChangeInitiativeService changeInitiativeService; private final LogicalDataElementService logicalDataElementService; private final DataTypeService dataTypeService; + private final EndUserAppService endUserAppService; private final MeasurableService measurableService; private final OrganisationalUnitService organisationalUnitService; private final PersonService personService; @@ -86,6 +88,7 @@ public EntitySearchService(DBExecutorPoolInterface dbExecutorPool, ChangeInitiativeService changeInitiativeService, LogicalDataElementService logicalDataElementService, DataTypeService dataTypeService, + EndUserAppService endUserAppService, MeasurableService measurableService, OrganisationalUnitService organisationalUnitService, PersonService personService, @@ -104,6 +107,7 @@ public EntitySearchService(DBExecutorPoolInterface dbExecutorPool, checkNotNull(appGroupService, "appGroupService cannot be null"); checkNotNull(changeInitiativeService, "changeInitiativeService cannot be null"); checkNotNull(dataTypeService, "dataTypeService cannot be null"); + checkNotNull(endUserAppService, "endUserAppService cannot be null"); checkNotNull(flowDiagramService, "flowDiagramService cannot be null"); checkNotNull(logicalDataElementService, "logicalDataElementService cannot be null"); checkNotNull(measurableService, "measurableService cannot be null"); @@ -125,6 +129,7 @@ public EntitySearchService(DBExecutorPoolInterface dbExecutorPool, this.dataTypeService = dataTypeService; this.flowDiagramService = flowDiagramService; this.logicalDataElementService = logicalDataElementService; + this.endUserAppService = endUserAppService; this.measurableService = measurableService; this.organisationalUnitService = organisationalUnitService; this.personService = personService; @@ -175,6 +180,8 @@ private Callable> mkCallable(EntityKind entity return () -> dataTypeService.search(options); case DATABASE: return () -> databaseInformationService.search(options); + case END_USER_APPLICATION: + return () -> endUserAppService.search(options); case FLOW_DIAGRAM: return () -> flowDiagramService.search(options); case LEGAL_ENTITY: