Skip to content

Commit

Permalink
ForeachSpec, RuleOutcome, Builders, flere rulesets (#80)
Browse files Browse the repository at this point in the history
* Diskusjonsutkast forbedring av Sequence

* Foreach spec for å erstatte DynamicRuleSvc

* Riktig summary der roten er en leaf

* Evaluation output og enkel carryon

* Forenkle output/carryon - bruk heller RuleOutcome

* Forenkle tilfeller av evaluate(t, serviceArgument

* Siste finpuss

* Konservativ ...

* Flere builders og utilities

Co-authored-by: Michal J. Sladek <mrsladek@users.noreply.github.com>
  • Loading branch information
jolarsen and mrsladek authored Feb 17, 2022
1 parent 31471ed commit f855e29
Show file tree
Hide file tree
Showing 25 changed files with 712 additions and 171 deletions.
92 changes: 81 additions & 11 deletions src/main/java/no/nav/fpsak/nare/Ruleset.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import no.nav.fpsak.nare.specification.ComputationalIfSpecification;
import no.nav.fpsak.nare.specification.ConditionalOrSpecification;
import no.nav.fpsak.nare.specification.ConditionalOrSpecification.Builder;
import no.nav.fpsak.nare.specification.ForeachSpecification;
import no.nav.fpsak.nare.specification.SequenceSpecification;
import no.nav.fpsak.nare.specification.Specification;

Expand All @@ -21,13 +21,20 @@ public Specification<V> regel(String id, String beskrivelse, Specification<V> sp
return specification.medBeskrivelse(beskrivelse).medID(id);
}

public Builder<V> hvisRegel(String id, String beskrivelse) {
public ConditionalOrSpecification.Builder<V> hvisRegel(String id, String beskrivelse) {
return ConditionalOrSpecification.<V>regel(id, beskrivelse);
}

public SequenceSpecification.Builder<V> sekvensRegel(String id, String beskrivelse) {
return SequenceSpecification.<V>regel(id, beskrivelse);
}

public ComputationalIfSpecification.Builder<V> betingetSekvensRegel(Specification<V> hvis, Specification<V> evaluer) {
return ComputationalIfSpecification.<V>regel().hvis(hvis, evaluer);
}

/**
* Realiserer flyten if <betingelse> then <then-spesifikasjon> else
* <else-spesifikasjon> for beregningsregel
* Realiserer flyten if <betingelse> then <then-spesifikasjon> else <else-spesifikasjon> for beregningsregel
*
* @param testSpec
* @param thenSpec
Expand All @@ -40,23 +47,43 @@ public Specification<V> beregningHvisRegel(Specification<V> testSpec, Specificat
}

/**
* Realiserer sekvens av to spesifikasjoner der bare den siste har betydning for
* videre flyt
*
* Realiserer flyten if <betingelse> then <then-spesifikasjon> (else true) for beregningsregel
*
* @param testSpec
* @param thenSpec
* @return
*/
public Specification<V> beregningHvisRegel(Specification<V> testSpec, Specification<V> thenSpec) {
return new ComputationalIfSpecification<>(testSpec, thenSpec);
}

/**
* Realiserer sekvens av 2 spesifikasjoner der bare den siste har betydning for videre flyt
*
* @param id
* @param beskrivelse
* @param spec1
* @param spec2
* @return
*/
public Specification<V> beregningsRegel(String id, String beskrivelse, Specification<V> spec1,
Specification<V> spec2) {
public Specification<V> beregningsRegel(String id, String beskrivelse, Specification<V> spec1, Specification<V> spec2) {
return new SequenceSpecification<>(id, beskrivelse, spec1, spec2);
}

/**
* Realiserer sekvens av N spesifikasjoner der bare den siste har betydning for
* videre flyt
* Realiserer sekvens av N spesifikasjoner der bare den siste har betydning for videre flyt
*
* @param id
* @param beskrivelse
* @param specs
* @return
*/
public Specification<V> beregningsRegel(String id, String beskrivelse, Specification<V>... specs) {
return new SequenceSpecification<>(id, beskrivelse, specs);
}

/**
* Realiserer sekvens av N spesifikasjoner der bare den siste har betydning for videre flyt
*
* @param id
* @param beskrivelse
Expand All @@ -72,6 +99,48 @@ public Specification<V> beregningsRegel(String id, String beskrivelse, List<Spec
return new SequenceSpecification<>(id, beskrivelse, specs);
}

/**
* Realiserer sekvens der en regel utføres N ganger - 1 gang for hvert element i en collection
*
* @param id
* @param beskrivelse
* @param spec regel som utføres 1 gang for hvert element i argumentlisten, med argName + arg som "scope"
* @param argName
* @param args argumentlisten, inneholder N argumenter
* @return
*/
public Specification<V> beregningsForeachRegel(String id, String beskrivelse, Specification<V> spec,
String argName, List<?> args) {
Objects.requireNonNull(spec, "spec1");
Objects.requireNonNull(args, "args1");
if (args.isEmpty()) {
throw new IllegalArgumentException("Argumentlisten kan ikke være tom");
}
return new ForeachSpecification<>(id, beskrivelse, spec, args, argName);
}

/**
* Realiserer sekvens der en regel utføres N ganger - 1 gang for hvert element i en collection - fulgt av annen regel
*
* @param id
* @param beskrivelse
* @param spec regel som utføres 1 gang for hvert element i argumentlisten, med argName + arg som "scope"
* @param argName
* @param args argumentlisten, inneholder N argumenter
* @param specThen regel som utføres 1 gang etter at spec er utført N ganger
* @return
*/
public Specification<V> beregningsForeachThenRegel(String id, String beskrivelse, Specification<V> spec,
String argName, List<?> args, Specification<V> specThen) {
Objects.requireNonNull(spec, "spec");
Objects.requireNonNull(args, "args1");
Objects.requireNonNull(specThen, "specThen");
if (args.isEmpty()) {
throw new IllegalArgumentException("Argumentlisten kan ikke være tom");
}
return new SequenceSpecification<>(id, beskrivelse, new ForeachSpecification<>(id, beskrivelse, spec, args, argName), specThen);
}

/**
* Realiserer sekvens der en regel utføres N ganger, etterfulgt av en
* spesifikasjon som har betydning for videre flyt
Expand All @@ -87,6 +156,7 @@ public Specification<V> beregningsRegel(String id, String beskrivelse, List<Spec
* @param spec2 utføres 1 gang etter at spec1 er utført N ganger
* @return
*/
@Deprecated // Bruk beregningForeach eller beregningForeachThen
public Specification<V> beregningsRegel(String id, String beskrivelse, Class<? extends DynamicRuleService<V>> spec1,
V regelmodell, String argumentBeskrivelse, List<? extends Object> args1, Specification<V> spec2) {
Objects.requireNonNull(spec1, "spec1");
Expand Down
10 changes: 2 additions & 8 deletions src/main/java/no/nav/fpsak/nare/ServiceArgument.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,13 @@

import java.util.Objects;

public class ServiceArgument {
public record ServiceArgument(String beskrivelse, Object verdi) {

public ServiceArgument(String beskrivelse, Object verdi) {
super();
public ServiceArgument {
Objects.requireNonNull(beskrivelse, "beskrivelse");
Objects.requireNonNull(verdi, "verdi");
this.beskrivelse = beskrivelse;
this.verdi = verdi;
}

private String beskrivelse;
private Object verdi;

public String getBeskrivelse() {
return beskrivelse;
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/no/nav/fpsak/nare/evaluation/Operator.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

public enum Operator {

AND, OR, SINGLE, NOT, COND_OR, SEQUENCE, COMPUTATIONAL_IF
AND, OR, SINGLE, NOT, COND_OR, SEQUENCE, COMPUTATIONAL_IF, FOREACH
}
32 changes: 32 additions & 0 deletions src/main/java/no/nav/fpsak/nare/evaluation/RuleOutcome.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package no.nav.fpsak.nare.evaluation;

/*
* Implementer gjerne en lokal DomemeRuleOutcome som kan gi ut en enum eller record, unngå getReasonCode og string-kode-magi
* - Bruk T for typet grunn til utfallet
* - Bruk R for beregnet resultat
*/
public record RuleOutcome<T, R>(T reason, String reasonCode, String reasonTextTemplate, R calculated) implements RuleReasonRef {

public RuleOutcome(R calculated) {
this(null, null, "", calculated);
}

public RuleOutcome(T reason, String reasonCode) {
this(reason, reasonCode, "", null);
}

public RuleOutcome(T reason, String reasonCode, String reasonTextTemplate) {
this(reason, reasonCode, reasonTextTemplate, null);
}

@Override
public String getReasonTextTemplate() {
return reasonTextTemplate;
}

@Override
public String getReasonCode() {
return reasonCode;
}

}
5 changes: 5 additions & 0 deletions src/main/java/no/nav/fpsak/nare/evaluation/RuleReasonRef.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

/**
* Represents a key for a valid outcome of an evaluation (leaf).
* Senere: Generification av RuleReasonRef<T> og T getReason() - vil treffe Evaluation
*/
public interface RuleReasonRef {

String getReasonTextTemplate();

/*
* Implementer heller en lokal DomemeRuleReasonRef som kan gi ut en enum eller record, unngå kode-magi
*/
@Deprecated(forRemoval = true)
String getReasonCode();

}
17 changes: 4 additions & 13 deletions src/main/java/no/nav/fpsak/nare/evaluation/RuleReasonRefImpl.java
Original file line number Diff line number Diff line change
@@ -1,19 +1,10 @@
package no.nav.fpsak.nare.evaluation;

import no.nav.fpsak.nare.specification.LeafSpecification;

/**
* Representerer en unik output fra en kjøring av Specifications. Produseres normalt av {@link LeafSpecification}.
/*
* Bruk RuleOutcome eller implementer en lokal RuleReasonRef som kan gi ut en enum eller record, unngå kode-magi
*/
public class RuleReasonRefImpl implements RuleReasonRef {

private final String reasonCode;
private final String reason;

public RuleReasonRefImpl(String reasonCode, String reason) {
this.reasonCode = reasonCode;
this.reason = reason;
}
@Deprecated(forRemoval = true)
public record RuleReasonRefImpl(String reasonCode, String reason) implements RuleReasonRef {

@Override
public String getReasonTextTemplate() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package no.nav.fpsak.nare.evaluation.node;

import no.nav.fpsak.nare.evaluation.AggregatedEvaluation;
import no.nav.fpsak.nare.evaluation.Evaluation;
import no.nav.fpsak.nare.evaluation.Operator;
import no.nav.fpsak.nare.evaluation.Resultat;

public class ForeachEvaluation extends AggregatedEvaluation {

public ForeachEvaluation(String id, String ruleDescription, Evaluation... children) {
super(Operator.FOREACH, id, ruleDescription, children);
}

@Override
public String reason() {
return "Utført " + ruleDescriptionText();
}

@Override
public Resultat result() {
// Endre hvis man tillater andre outcomes fra loop
return lastChild().result();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,25 @@

public class SequenceEvaluation extends AggregatedEvaluation {

private static final String OG = " og ";

public SequenceEvaluation(String id, String ruleDescription, Evaluation... children) {
super(Operator.SEQUENCE, id, ruleDescription, children);
}

@Override
public String reason() {
return "Utført " + firstChild().ruleIdentification() + " og " + secondChild().ruleIdentification();
var reasonText = new StringBuilder("Utført ");
for (var child : children()) {
reasonText.append(child.ruleIdentification()).append(OG);
}
var finalOg = reasonText.lastIndexOf(OG);
reasonText.delete(finalOg, finalOg + OG.length());
return reasonText.toString();
}

@Override
public Resultat result() {
return secondChild().result();
return lastChild().result();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import java.util.EnumSet;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import no.nav.fpsak.nare.evaluation.AggregatedEvaluation;
import no.nav.fpsak.nare.evaluation.Evaluation;
Expand All @@ -28,9 +27,8 @@ public boolean check(Operator op, Evaluation parent, Evaluation child) {
return false;
}

// special case, kun prosessere siste SingleEvaluation barn av SequenceEvaluation siden alle andre
// returnerer ja.
if (Operator.SEQUENCE.equals(parent.getOperator())) {
// special case, kun prosessere siste SingleEvaluation barn av SequenceEvaluation siden alle andre returnerer ja.
if (Operator.SEQUENCE.equals(parent.getOperator()) || Operator.FOREACH.equals(parent.getOperator())) {
String childRuleId = child.ruleIdentification();
String lastChildOfParentRuleId = ((AggregatedEvaluation) parent).lastChild().ruleIdentification();
return (childRuleId.equals(lastChildOfParentRuleId));
Expand All @@ -50,7 +48,9 @@ public EvaluationSummary(Evaluation rootEvaluation) {
this.rootEvaluation = rootEvaluation;
}

/** Convenience metode for å kun returnere et Leaf. Hvis det finnes flere kastes en exception. */
/**
* Convenience metode for å kun returnere et Leaf. Hvis det finnes flere kastes en exception.
*/
public Optional<Evaluation> singleLeafEvaluation(Resultat... acceptedResults) {
Collection<Evaluation> leafEvaluations = leafEvaluations(acceptedResults);

Expand All @@ -63,20 +63,6 @@ public Optional<Evaluation> singleLeafEvaluation(Resultat... acceptedResults) {
return Optional.of(leafEvaluations.iterator().next());
}
}

/** Convenience metode for å kun returnere et Leaf. Hvis det finnes flere kastes en exception. */
public Optional<String> singleLeafReason(Resultat... acceptedResults) {
Collection<String> leafReasons = leafReasons(acceptedResults);

if (leafReasons.size() > 1) {
throw new IllegalStateException("Det finnes flere enn ett Leaf resultat for angitt aksepterte resultater (" //$NON-NLS-1$
+ Arrays.toString(acceptedResults) + "): " + leafReasons); //$NON-NLS-1$
} else if (leafReasons.isEmpty()) {
return Optional.empty();
} else {
return Optional.of(leafReasons.iterator().next());
}
}

public Collection<Evaluation> leafEvaluations(Resultat... acceptedResults) {
Set<Resultat> accepted = acceptedResults.length > 0 ? EnumSet.copyOf(Arrays.asList(acceptedResults))
Expand All @@ -87,20 +73,4 @@ public Collection<Evaluation> leafEvaluations(Resultat... acceptedResults) {
return visitor.getCollected();
}

public Collection<String> leafReasons(Resultat... acceptedResults) {

Set<Resultat> accepted = acceptedResults.length > 0 ? EnumSet.copyOf(Arrays.asList(acceptedResults))
: EnumSet.allOf(Resultat.class);

NonCircularVisitorEvaluationCollector visitor = new NonCircularVisitorEvaluationCollector(new LeafNodeVisitorCondition(accepted));

rootEvaluation.visit(rootEvaluation, visitor);
return visitor.getCollected().stream()
.filter(e -> e.getOutcome() != null)
.map(this::getReasonCode).distinct().collect(Collectors.toList());
}

private String getReasonCode(Evaluation e) {
return e.getOutcome() == null ? null : e.getOutcome().getReasonCode();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import no.nav.fpsak.nare.evaluation.Evaluation;
import no.nav.fpsak.nare.evaluation.Operator;
import no.nav.fpsak.nare.evaluation.node.SingleEvaluation;
import no.nav.fpsak.nare.evaluation.summary.EvaluationSummary.EvaluationVisitorCondition;

public class NonCircularVisitorEvaluationCollector implements EvaluationVisitor {
Expand All @@ -24,9 +25,10 @@ public Set<Evaluation> getCollected() {

@Override
public boolean visiting(Operator operator, Evaluation parentEvaluation, Evaluation evaluation) {
if (Objects.equals(parentEvaluation, evaluation)) {
// er ved roten
return true;
if (Objects.equals(parentEvaluation, evaluation) &&
(!(evaluation instanceof SingleEvaluation) || visited.contains(evaluation))) {
// er ved roten. Special case med at roten er en Leaf
return true;
}
boolean added = visited.add(evaluation);
if (!added) {
Expand Down
Loading

0 comments on commit f855e29

Please sign in to comment.