New functions for Consent evaluation (#6250)
Little tools for consent evaluation.
This commit is contained in:
parent
18293eba89
commit
36cbca65a8
|
@ -19,6 +19,8 @@
|
||||||
*/
|
*/
|
||||||
package ca.uhn.fhir.rest.server.interceptor.consent;
|
package ca.uhn.fhir.rest.server.interceptor.consent;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public enum ConsentOperationStatusEnum {
|
public enum ConsentOperationStatusEnum {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,4 +41,71 @@ public enum ConsentOperationStatusEnum {
|
||||||
* counting/caching methods)
|
* counting/caching methods)
|
||||||
*/
|
*/
|
||||||
AUTHORIZED,
|
AUTHORIZED,
|
||||||
|
;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns ordinals to the verdicts by strength:
|
||||||
|
* REJECT > AUTHORIZED > PROCEED.
|
||||||
|
* @return 2/1/0 for REJECT/AUTHORIZED/PROCEED
|
||||||
|
*/
|
||||||
|
int getPrecedence() {
|
||||||
|
switch (this) {
|
||||||
|
case REJECT:
|
||||||
|
return 2;
|
||||||
|
case AUTHORIZED:
|
||||||
|
return 1;
|
||||||
|
case PROCEED:
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Evaluate verdicts in order, taking the first "decision" (i.e. first non-PROCEED) verdict.
|
||||||
|
*
|
||||||
|
* @return the first decisive verdict, or PROCEED when empty or all PROCEED.
|
||||||
|
*/
|
||||||
|
public static ConsentOperationStatusEnum serialEvaluate(Stream<ConsentOperationStatusEnum> theVoteStream) {
|
||||||
|
return theVoteStream.filter(verdict -> PROCEED != verdict).findFirst().orElse(PROCEED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluate verdicts in order, taking the first "decision" (i.e. first non-PROCEED) verdict.
|
||||||
|
*
|
||||||
|
* @param theNextVerdict the next verdict to consider
|
||||||
|
* @return the combined verdict
|
||||||
|
*/
|
||||||
|
public ConsentOperationStatusEnum serialReduce(ConsentOperationStatusEnum theNextVerdict) {
|
||||||
|
if (this != PROCEED) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return theNextVerdict;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluate all verdicts together, allowing any to veto (i.e. REJECT) the operation.
|
||||||
|
* <ul>
|
||||||
|
* <li>If any vote is REJECT, then the result is REJECT.
|
||||||
|
* <li>If no vote is REJECT, and any vote is AUTHORIZED, then the result is AUTHORIZED.
|
||||||
|
* <li>If no vote is REJECT or AUTHORIZED, the result is PROCEED.
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @return REJECT if any reject, AUTHORIZED if no REJECT and some AUTHORIZED, PROCEED if empty or all PROCEED
|
||||||
|
*/
|
||||||
|
public static ConsentOperationStatusEnum parallelEvaluate(Stream<ConsentOperationStatusEnum> theVoteStream) {
|
||||||
|
return theVoteStream.reduce(PROCEED, ConsentOperationStatusEnum::parallelReduce);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluate two verdicts together, allowing either to veto (i.e. REJECT) the operation.
|
||||||
|
*
|
||||||
|
* @return REJECT if either reject, AUTHORIZED if no REJECT and some AUTHORIZED, PROCEED otherwise
|
||||||
|
*/
|
||||||
|
public ConsentOperationStatusEnum parallelReduce(ConsentOperationStatusEnum theNextVerdict) {
|
||||||
|
if (theNextVerdict.getPrecedence() > this.getPrecedence()) {
|
||||||
|
return theNextVerdict;
|
||||||
|
} else {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
package ca.uhn.fhir.rest.server.interceptor.consent;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.CsvSource;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.rest.server.interceptor.consent.ConsentOperationStatusEnum.AUTHORIZED;
|
||||||
|
import static ca.uhn.fhir.rest.server.interceptor.consent.ConsentOperationStatusEnum.PROCEED;
|
||||||
|
import static ca.uhn.fhir.rest.server.interceptor.consent.ConsentOperationStatusEnum.REJECT;
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
class ConsentOperationStatusEnumTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* With "serial" evaluation, the first non-PROCEED verdict wins.
|
||||||
|
*/
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(textBlock = """
|
||||||
|
REJECT REJECT REJECT , REJECT
|
||||||
|
REJECT REJECT PROCEED , REJECT
|
||||||
|
REJECT REJECT AUTHORIZED, REJECT
|
||||||
|
REJECT PROCEED REJECT , REJECT
|
||||||
|
REJECT PROCEED PROCEED , REJECT
|
||||||
|
REJECT PROCEED AUTHORIZED, REJECT
|
||||||
|
REJECT AUTHORIZED REJECT , REJECT
|
||||||
|
REJECT AUTHORIZED PROCEED , REJECT
|
||||||
|
REJECT AUTHORIZED AUTHORIZED, REJECT
|
||||||
|
PROCEED REJECT REJECT , REJECT
|
||||||
|
PROCEED REJECT PROCEED , REJECT
|
||||||
|
PROCEED REJECT AUTHORIZED, REJECT
|
||||||
|
PROCEED PROCEED REJECT , REJECT
|
||||||
|
PROCEED PROCEED PROCEED , PROCEED
|
||||||
|
PROCEED PROCEED AUTHORIZED, AUTHORIZED
|
||||||
|
PROCEED AUTHORIZED REJECT , AUTHORIZED
|
||||||
|
PROCEED AUTHORIZED PROCEED , AUTHORIZED
|
||||||
|
PROCEED AUTHORIZED AUTHORIZED, AUTHORIZED
|
||||||
|
AUTHORIZED REJECT REJECT , AUTHORIZED
|
||||||
|
AUTHORIZED REJECT PROCEED , AUTHORIZED
|
||||||
|
AUTHORIZED REJECT AUTHORIZED, AUTHORIZED
|
||||||
|
AUTHORIZED PROCEED REJECT , AUTHORIZED
|
||||||
|
AUTHORIZED PROCEED PROCEED , AUTHORIZED
|
||||||
|
AUTHORIZED PROCEED AUTHORIZED, AUTHORIZED
|
||||||
|
AUTHORIZED AUTHORIZED REJECT , AUTHORIZED
|
||||||
|
AUTHORIZED AUTHORIZED PROCEED , AUTHORIZED
|
||||||
|
AUTHORIZED AUTHORIZED AUTHORIZED, AUTHORIZED
|
||||||
|
""")
|
||||||
|
void testSerialEvaluation_choosesFirstVerdict(String theInput, ConsentOperationStatusEnum theExpectedResult) {
|
||||||
|
// given
|
||||||
|
Stream<ConsentOperationStatusEnum> consentOperationStatusEnumStream = Arrays.stream(theInput.split(" +"))
|
||||||
|
.map(String::trim)
|
||||||
|
.map(ConsentOperationStatusEnum::valueOf);
|
||||||
|
|
||||||
|
// when
|
||||||
|
ConsentOperationStatusEnum result = ConsentOperationStatusEnum.serialEvaluate(consentOperationStatusEnumStream);
|
||||||
|
|
||||||
|
assertEquals(theExpectedResult, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(textBlock = """
|
||||||
|
REJECT , REJECT , REJECT
|
||||||
|
REJECT , PROCEED , REJECT
|
||||||
|
REJECT , AUTHORIZED, REJECT
|
||||||
|
AUTHORIZED, REJECT , AUTHORIZED
|
||||||
|
AUTHORIZED, PROCEED , AUTHORIZED
|
||||||
|
AUTHORIZED, AUTHORIZED, AUTHORIZED
|
||||||
|
PROCEED , REJECT , REJECT
|
||||||
|
PROCEED , PROCEED , PROCEED
|
||||||
|
PROCEED , AUTHORIZED, AUTHORIZED
|
||||||
|
""")
|
||||||
|
void testSerialReduction_choosesFirstVerdict(ConsentOperationStatusEnum theFirst, ConsentOperationStatusEnum theSecond, ConsentOperationStatusEnum theExpectedResult) {
|
||||||
|
|
||||||
|
// when
|
||||||
|
ConsentOperationStatusEnum result = theFirst.serialReduce(theSecond);
|
||||||
|
|
||||||
|
assertEquals(theExpectedResult, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* With "parallel" evaluation, the "strongest" verdict wins.
|
||||||
|
* REJECT > AUTHORIZED > PROCEED.
|
||||||
|
*/
|
||||||
|
@ParameterizedTest
|
||||||
|
@CsvSource(textBlock = """
|
||||||
|
REJECT REJECT REJECT , REJECT
|
||||||
|
REJECT REJECT PROCEED , REJECT
|
||||||
|
REJECT REJECT AUTHORIZED, REJECT
|
||||||
|
REJECT PROCEED REJECT , REJECT
|
||||||
|
REJECT PROCEED PROCEED , REJECT
|
||||||
|
REJECT PROCEED AUTHORIZED, REJECT
|
||||||
|
REJECT AUTHORIZED REJECT , REJECT
|
||||||
|
REJECT AUTHORIZED PROCEED , REJECT
|
||||||
|
REJECT AUTHORIZED AUTHORIZED, REJECT
|
||||||
|
PROCEED REJECT REJECT , REJECT
|
||||||
|
PROCEED REJECT PROCEED , REJECT
|
||||||
|
PROCEED REJECT AUTHORIZED, REJECT
|
||||||
|
PROCEED PROCEED REJECT , REJECT
|
||||||
|
PROCEED PROCEED PROCEED , PROCEED
|
||||||
|
PROCEED PROCEED AUTHORIZED, AUTHORIZED
|
||||||
|
PROCEED AUTHORIZED REJECT , REJECT
|
||||||
|
PROCEED AUTHORIZED PROCEED , AUTHORIZED
|
||||||
|
PROCEED AUTHORIZED AUTHORIZED, AUTHORIZED
|
||||||
|
AUTHORIZED REJECT REJECT , REJECT
|
||||||
|
AUTHORIZED REJECT PROCEED , REJECT
|
||||||
|
AUTHORIZED REJECT AUTHORIZED, REJECT
|
||||||
|
AUTHORIZED PROCEED REJECT , REJECT
|
||||||
|
AUTHORIZED PROCEED PROCEED , AUTHORIZED
|
||||||
|
AUTHORIZED PROCEED AUTHORIZED, AUTHORIZED
|
||||||
|
AUTHORIZED AUTHORIZED REJECT , REJECT
|
||||||
|
AUTHORIZED AUTHORIZED PROCEED , AUTHORIZED
|
||||||
|
AUTHORIZED AUTHORIZED AUTHORIZED, AUTHORIZED
|
||||||
|
""")
|
||||||
|
void testParallelReduction_strongestVerdictWins(String theInput, ConsentOperationStatusEnum theExpectedResult) {
|
||||||
|
// given
|
||||||
|
Stream<ConsentOperationStatusEnum> consentOperationStatusEnumStream = Arrays.stream(theInput.split(" +"))
|
||||||
|
.map(String::trim)
|
||||||
|
.map(ConsentOperationStatusEnum::valueOf);
|
||||||
|
|
||||||
|
// when
|
||||||
|
ConsentOperationStatusEnum result = ConsentOperationStatusEnum.parallelEvaluate(consentOperationStatusEnumStream);
|
||||||
|
|
||||||
|
assertEquals(theExpectedResult, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void testStrengthOrder() {
|
||||||
|
assertTrue(REJECT.getPrecedence() > AUTHORIZED.getPrecedence());
|
||||||
|
assertTrue(AUTHORIZED.getPrecedence() > PROCEED.getPrecedence());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue