Skip to content

Commit

Permalink
Update rule calculator to do both inbound and outbound rules as well …
Browse files Browse the repository at this point in the history
…as point-to-point flows

- When decorators updated the classifications are applied

#CTCTOWALTZ-3090
finos#7032
  • Loading branch information
jessica-woodland-scott-db committed Apr 3, 2024
1 parent 680ff7f commit 05ccd34
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
import static org.finos.waltz.common.Checks.checkTrue;
import static org.finos.waltz.common.DateTimeUtilities.nowUtcTimestamp;
import static org.finos.waltz.common.DateTimeUtilities.toLocalDateTime;
import static org.finos.waltz.common.ListUtilities.asList;
import static org.finos.waltz.common.ListUtilities.newArrayList;
import static org.finos.waltz.common.MapUtilities.groupBy;
import static org.finos.waltz.common.SetUtilities.union;
Expand Down Expand Up @@ -349,7 +350,24 @@ public int clearRatingsForPointToPointFlows(FlowClassificationRule rule) {
}


public List<FlowClassificationRuleVantagePoint> findExpandedFlowClassificationRuleVantagePoints(FlowDirection direction, Set<Long> orgIds) {
public List<FlowClassificationRuleVantagePoint> findExpandedFlowClassificationRuleVantagePoints(FlowDirection direction,
Set<Long> orgVantagePointIds,
Set<Long> appVantagePointIds,
Set<Long> actorVantagePointIds) {

Condition appVantagePointCondition = FLOW_CLASSIFICATION_RULE.PARENT_KIND.eq(EntityKind.APPLICATION.name())
.and(FLOW_CLASSIFICATION_RULE.PARENT_ID.in(appVantagePointIds));

Condition actorVantagePointCondition = FLOW_CLASSIFICATION_RULE.PARENT_KIND.eq(EntityKind.ACTOR.name())
.and(FLOW_CLASSIFICATION_RULE.PARENT_ID.in(actorVantagePointIds));

Condition orgUnitVantagePointCondition = FLOW_CLASSIFICATION_RULE.PARENT_KIND.eq(EntityKind.ORG_UNIT.name())
.and(ehOrgUnit.ID.in(orgVantagePointIds));

Condition vantagePointCondition = appVantagePointCondition
.or(actorVantagePointCondition)
.or(orgUnitVantagePointCondition);

SelectSeekStep5<Record9<Long, String, Integer, Long, Integer, Long, String, String, Long>, String, Integer, Integer, Long, Long> select = dsl
.select(vantagePointId,
FLOW_CLASSIFICATION_RULE.PARENT_KIND,
Expand All @@ -361,21 +379,25 @@ public List<FlowClassificationRuleVantagePoint> findExpandedFlowClassificationRu
FLOW_CLASSIFICATION.CODE,
FLOW_CLASSIFICATION_RULE.ID)
.from(FLOW_CLASSIFICATION_RULE)
.innerJoin(ehOrgUnit)
.on(ehOrgUnit.ANCESTOR_ID.eq(FLOW_CLASSIFICATION_RULE.PARENT_ID).and(ehOrgUnit.KIND.eq(EntityKind.ORG_UNIT.name())))
.leftJoin(ehOrgUnit)
.on(ehOrgUnit.ANCESTOR_ID.eq(FLOW_CLASSIFICATION_RULE.PARENT_ID)
.and(ehOrgUnit.KIND.eq(EntityKind.ORG_UNIT.name())
.and(FLOW_CLASSIFICATION_RULE.PARENT_KIND.eq(EntityKind.ORG_UNIT.name()))))
.innerJoin(declaredDataType)
.on(declaredDataType.ID.eq(FLOW_CLASSIFICATION_RULE.DATA_TYPE_ID))
.innerJoin(ehDataType)
.on(ehDataType.ANCESTOR_ID.eq(declaredDataType.ID).and(ehDataType.KIND.eq(EntityKind.DATA_TYPE.name())))
.innerJoin(FLOW_CLASSIFICATION).on(FLOW_CLASSIFICATION_RULE.FLOW_CLASSIFICATION_ID.eq(FLOW_CLASSIFICATION.ID))
.where(FLOW_CLASSIFICATION.DIRECTION.eq(direction.name()).and(ehOrgUnit.ID.in(orgIds)))
.where(FLOW_CLASSIFICATION.DIRECTION.eq(direction.name())
.and(vantagePointCondition))
.orderBy(
FLOW_CLASSIFICATION_RULE.PARENT_KIND,
ehOrgUnit.LEVEL.desc(),
ehDataType.LEVEL.desc(),
ehOrgUnit.ID,
ehDataType.ID);

System.out.println(select);
return select
.fetch(TO_VANTAGE_MAPPER);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@

import static org.finos.waltz.common.Checks.checkNotNull;
import static org.finos.waltz.common.ListUtilities.filter;
import static org.finos.waltz.common.ListUtilities.isEmpty;
import static org.finos.waltz.common.SetUtilities.map;
import static org.finos.waltz.model.utils.IdUtilities.indexById;

Expand Down Expand Up @@ -86,28 +85,32 @@ public LogicalFlowDecoratorRatingsCalculator(ApplicationService applicationServi
}


public Collection<DataTypeDecorator> calculate(Collection<DataTypeDecorator> decorators) {
public Collection<DataTypeDecorator> calculate(Collection<DataTypeDecorator> decorators) {

List<LogicalFlow> logicalFlows = loadFlows(decorators);

List<LogicalFlow> appToAppFlows = filter(
IS_APP_TO_APP_FLOW,
loadFlows(decorators));
logicalFlows);

if (isEmpty(appToAppFlows)) return Collections.emptyList();
// if (isEmpty(appToAppFlows)) return Collections.emptyList();

List<Application> targetApps = loadTargetApplications(appToAppFlows);
List<Application> sourceApps = loadSourceApplications(appToAppFlows);

Map<Long, LogicalFlow> flowsById = indexById(appToAppFlows);
Map<Long, Application> targetAppsById = indexById(targetApps);
Map<Long, Application> sourceAppsById = indexById(sourceApps);

FlowClassificationRuleResolver outboundResolver = createResolver(FlowDirection.OUTBOUND, targetApps);
FlowClassificationRuleResolver inboundResolver = createResolver(FlowDirection.INBOUND, sourceApps);
Map<Long, LogicalFlow> flowsById = indexById(logicalFlows);

FlowClassificationRuleResolver outboundResolver = createResolver(FlowDirection.OUTBOUND, logicalFlows, targetApps);
FlowClassificationRuleResolver inboundResolver = createResolver(FlowDirection.INBOUND, logicalFlows, sourceApps);

return decorators
.stream()
.filter(d -> flowsById.containsKey(d.dataFlowId()))
.map(decorator -> {
DataTypeDecorator d = decorator;
try {
if (decorator.decoratorEntity().kind() != EntityKind.DATA_TYPE) {
return decorator;
Expand All @@ -117,11 +120,13 @@ public Collection<DataTypeDecorator> calculate(Collection<DataTypeDecorator> de
EntityReference sourceVantagePoint = lookupVantagePoint(sourceAppsById, flow.source());
Tuple2<AuthoritativenessRatingValue, Optional<Long>> sourceOutboundClassification = lookupClassification(
targetVantagePoint,
flow.target(),
flow.source(),
outboundResolver,
decorator);
Tuple2<AuthoritativenessRatingValue, Optional<Long>> targetInboundClassification = lookupClassification(
sourceVantagePoint,
flow.source(),
flow.target(),
inboundResolver,
decorator);
Expand Down Expand Up @@ -167,22 +172,43 @@ private List<LogicalFlow> loadFlows(Collection<DataTypeDecorator> decorators) {
}


private FlowClassificationRuleResolver createResolver(FlowDirection direction, Collection<Application> apps) {
private FlowClassificationRuleResolver createResolver(FlowDirection direction,
List<LogicalFlow> logicalFlows,
Collection<Application> apps) {

Set<EntityReference> vantagePointEntityLookups = map(
logicalFlows,
d -> direction.equals(FlowDirection.OUTBOUND) ? d.target() : d.source());

Set<Long> appVantagePoints = getVantagePointIdsForKind(vantagePointEntityLookups, EntityKind.APPLICATION);
Set<Long> actorVantagePoints = getVantagePointIdsForKind(vantagePointEntityLookups, EntityKind.ACTOR);

// apps are targets for the rule
Set<Long> orgIds = map(apps, OrganisationalUnitIdProvider::organisationalUnitId);

// this brings back many expanded vantage points
List<FlowClassificationRuleVantagePoint> flowClassificationRuleVantagePoints =
flowClassificationRuleDao.findExpandedFlowClassificationRuleVantagePoints(direction, orgIds);
flowClassificationRuleDao.findExpandedFlowClassificationRuleVantagePoints(direction, orgIds, appVantagePoints, actorVantagePoints);

return new FlowClassificationRuleResolver(direction, flowClassificationRuleVantagePoints);
}

private Set<Long> getVantagePointIdsForKind(Set<EntityReference> vantagePointEntityLookups, EntityKind entityKind) {
return vantagePointEntityLookups
.stream()
.filter(d -> d.kind().equals(entityKind))
.map(EntityReference::id)
.collect(Collectors.toSet());
}


private Tuple2<AuthoritativenessRatingValue, Optional<Long>> lookupClassification(EntityReference vantagePoint,
private Tuple2<AuthoritativenessRatingValue, Optional<Long>> lookupClassification(EntityReference vantagePointOrgUnit,
EntityReference vantagePointEntity,
EntityReference subject,
FlowClassificationRuleResolver resolver,
DataTypeDecorator decorator) {
return resolver.resolve(vantagePoint, subject, decorator.decoratorEntity().id());

return resolver.resolve(vantagePointOrgUnit, vantagePointEntity, subject, decorator.decoratorEntity().id());
}

//
Expand All @@ -202,13 +228,18 @@ private Tuple2<AuthoritativenessRatingValue, Optional<Long>> lookupClassificatio
// }


private EntityReference lookupVantagePoint(Map<Long, Application> appsById, EntityReference lookupApp) {
Application targetApp = appsById.get(lookupApp.id());
long targetOrgUnitId = targetApp.organisationalUnitId();
// Vantage point needs to be included in lookup of both point to point flows and org unit flows. So need ot pass in the app and then offer both the OU and App lookup
private EntityReference lookupVantagePoint(Map<Long, Application> appsById, EntityReference lookupEntity) {
Application app = appsById.get(lookupEntity.id());

return EntityReference.mkRef(
EntityKind.ORG_UNIT,
targetOrgUnitId);
if (app != null) {
long targetOrgUnitId = app.organisationalUnitId();
return EntityReference.mkRef(
EntityKind.ORG_UNIT,
targetOrgUnitId);
} else {
return null;
}
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ private Collection<DataTypeDecorator> mkDecorators(String userName,
dtId,
Optional.of(AuthoritativenessRatingValue.NO_OPINION)));
LogicalFlow flow = logicalFlowDao.getByFlowId(entityReference.id());
boolean requiresRating = flow.source().kind() == APPLICATION && flow.target().kind() == APPLICATION;
// boolean requiresRating = flow.source().kind() == APPLICATION && flow.target().kind() == APPLICATION;
boolean requiresRating = true;

return requiresRating
? ratingsCalculator.calculate(decorators)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@

import org.finos.waltz.model.EntityReference;
import org.finos.waltz.model.FlowDirection;
import org.finos.waltz.model.flow_classification_rule.FlowClassificationRule;
import org.finos.waltz.model.flow_classification_rule.FlowClassificationRuleVantagePoint;
import org.finos.waltz.model.logical_flow.LogicalFlow;
import org.finos.waltz.model.rating.AuthoritativenessRatingValue;
import org.jooq.lambda.tuple.Tuple2;

import javax.swing.text.html.parser.Entity;
import java.util.*;

import static org.finos.waltz.common.Checks.checkNotNull;
Expand All @@ -38,7 +37,7 @@

public class FlowClassificationRuleResolver {

private final Map<EntityReference, Map<Long, Map<EntityReference, Optional<FlowClassificationRuleVantagePoint>>>> byOuThenDataTypeThenSubject;
private final Map<EntityReference, Map<Long, Map<EntityReference, Optional<FlowClassificationRuleVantagePoint>>>> byVantagePointThenDataTypeThenSubject;
private final FlowDirection direction;

/**
Expand All @@ -49,13 +48,13 @@ public class FlowClassificationRuleResolver {
public FlowClassificationRuleResolver(FlowDirection direction, List<FlowClassificationRuleVantagePoint> flowClassificationVantagePoints) {
checkNotNull(flowClassificationVantagePoints, "flowClassificationVantagePoints cannot be null");

byOuThenDataTypeThenSubject =
byVantagePointThenDataTypeThenSubject =
groupAndThen(
flowClassificationVantagePoints,
FlowClassificationRuleVantagePoint::vantagePoint,
byOus -> groupAndThen(
byOus,
byOu -> byOu.dataType().id(),
byVps -> groupAndThen(
byVps,
byVp -> byVp.dataType().id(),
byDts -> groupAndThen(
byDts,
FlowClassificationRuleVantagePoint::subjectReference,
Expand All @@ -68,25 +67,31 @@ public FlowClassificationRuleResolver(FlowDirection direction, List<FlowClassifi
* Given a vantage point, a supplier and a data type this method will give back
* an authoritativeness rating.
*
* @param vantagePoint typically the ou of the consuming app
* @param subject typically the app ref of the provider
* @param vantagePointOrgUnit typically the ou of the receiving app of the rule, will be null if the vantage point is an actor
* @param vantagePointEntity typically the entity on the receiving end of the rule
* @param subject typically the ref of the rule provider
* @param dataTypeId the data type in question
* @return How this should be rated
*/
public Tuple2<AuthoritativenessRatingValue, Optional<Long>> resolve(EntityReference vantagePoint,
public Tuple2<AuthoritativenessRatingValue, Optional<Long>> resolve(EntityReference vantagePointOrgUnit,
EntityReference vantagePointEntity,
EntityReference subject,
Long dataTypeId) {

Map<Long, Map<EntityReference, Optional<FlowClassificationRuleVantagePoint>>> ouGroup = byOuThenDataTypeThenSubject.get(vantagePoint);
//Attempt lookup for rule based upon both org unit and direct rule, prioritise direct lookup as more specific rule
Map<Long, Map<EntityReference, Optional<FlowClassificationRuleVantagePoint>>> vpGroup = byVantagePointThenDataTypeThenSubject
.getOrDefault(
vantagePointEntity,
byVantagePointThenDataTypeThenSubject.get(vantagePointOrgUnit));

// if a match cannot be found for the ou and the dt then no opinion, if a match can be found for these but the subject application
// doesn't match then the rating should be discouraged

if(isEmpty(ouGroup)) {
if(isEmpty(vpGroup)) {
return tuple(AuthoritativenessRatingValue.NO_OPINION, Optional.empty());
}

Map<EntityReference, Optional<FlowClassificationRuleVantagePoint>> dataTypeGroup = ouGroup.get(dataTypeId);
Map<EntityReference, Optional<FlowClassificationRuleVantagePoint>> dataTypeGroup = vpGroup.get(dataTypeId);

if(isEmpty(dataTypeGroup)) {
return tuple(AuthoritativenessRatingValue.NO_OPINION, Optional.empty());
Expand All @@ -113,7 +118,7 @@ public Optional<FlowClassificationRuleVantagePoint> resolveAuthSource(EntityRefe
EntityReference source,
Long dataTypeId) {

Map<Long, Map<EntityReference, Optional<FlowClassificationRuleVantagePoint>>> ouGroup = byOuThenDataTypeThenSubject.get(vantagePoint);
Map<Long, Map<EntityReference, Optional<FlowClassificationRuleVantagePoint>>> ouGroup = byVantagePointThenDataTypeThenSubject.get(vantagePoint);
if(isEmpty(ouGroup)) return Optional.empty();

Map<EntityReference, Optional<FlowClassificationRuleVantagePoint>> dataTypeGroup = ouGroup.get(dataTypeId);
Expand Down

0 comments on commit 05ccd34

Please sign in to comment.