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;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public enum ConsentOperationStatusEnum {
|
||||
|
||||
/**
|
||||
|
@ -39,4 +41,71 @@ public enum ConsentOperationStatusEnum {
|
|||
* counting/caching methods)
|
||||
*/
|
||||
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