Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.acme.service.DmnService;
import org.acme.service.FormDataTransformer;
import org.acme.service.LibraryApiService;
import org.acme.service.LibraryApiService.LibraryCheckEvaluation;

import java.util.*;

Expand Down Expand Up @@ -136,8 +137,15 @@ private Map<String, Object> evaluateBenefit(Benefit benefit, Map<String, Object>
int checkNum = 0;
for (CheckConfig checkConfig : benefit.getChecks()) {
EvaluationResult evaluationResult;
Map<String, Object> effectiveParameters = checkConfig.getParameters() != null
? new HashMap<>(checkConfig.getParameters())
: new HashMap<>();
List<String> defaultedParameters = List.of();
if (isLibraryCheck(checkConfig)){
evaluationResult = libraryApi.evaluateCheck(checkConfig, formData);
LibraryCheckEvaluation libraryCheckEvaluation = libraryApi.evaluateCheck(checkConfig, formData);
evaluationResult = libraryCheckEvaluation.result();
effectiveParameters = libraryCheckEvaluation.effectiveParameters();
defaultedParameters = libraryCheckEvaluation.defaultedParameters();
} else {
Map<String, Object> customFormValues = (Map<String, Object>) formData.get("custom");
if (customFormValues == null) {
Expand All @@ -159,6 +167,8 @@ private Map<String, Object> evaluateBenefit(Benefit benefit, Map<String, Object>
checkResultMap.put("module", checkConfig.getCheckModule() != null ? checkConfig.getCheckModule() : "");
checkResultMap.put("version", checkConfig.getCheckVersion() != null ? checkConfig.getCheckVersion() : "");
checkResultMap.put("parameters", checkConfig.getParameters() != null ? checkConfig.getParameters() : Map.of());
checkResultMap.put("effectiveParameters", effectiveParameters);
checkResultMap.put("defaultedParameters", defaultedParameters);
checkResults.put(uniqueCheckKey, checkResultMap);
checkNum += 1;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -27,6 +29,18 @@
@ApplicationScoped
public class LibraryApiService {
private static final String DEFAULT_LIBRARY_API_URL = "http://localhost:8083";
private static final String AS_OF_DATE_PARAMETER = "asOfDate";

public record LibraryCheckEvaluation(
EvaluationResult result,
Map<String, Object> effectiveParameters,
List<String> defaultedParameters
) {}

record EffectiveParameters(
Map<String, Object> parameters,
List<String> defaultedParameters
) {}

@Inject
private StorageService storageService;
Expand Down Expand Up @@ -100,12 +114,13 @@ public Optional<EligibilityCheck> getById(String id) {
return Optional.of(matches.getFirst());
}

public EvaluationResult evaluateCheck(CheckConfig checkConfig, Map<String, Object> inputs) throws JsonProcessingException {
public LibraryCheckEvaluation evaluateCheck(CheckConfig checkConfig, Map<String, Object> inputs) throws JsonProcessingException {

// TODO: Check that checkConfig has required attributes and handle null values
EffectiveParameters effectiveParameters = buildEffectiveParameters(checkConfig);

Map<String, Object> data = new HashMap<>();
data.put("parameters", checkConfig.getParameters());
data.put("parameters", effectiveParameters.parameters());
data.put("situation", inputs);
ObjectMapper mapper = new ObjectMapper();
String bodyJson = mapper.writeValueAsString(data);
Expand Down Expand Up @@ -139,7 +154,11 @@ public EvaluationResult evaluateCheck(CheckConfig checkConfig, Map<String, Objec
if (statusCode != 200){
Log.error("Error evaluating library check " + checkConfig.getCheckId());
Log.error("Inputs and parameters that caused error:" + bodyJson);
return EvaluationResult.UNABLE_TO_DETERMINE;
return new LibraryCheckEvaluation(
EvaluationResult.UNABLE_TO_DETERMINE,
effectiveParameters.parameters(),
effectiveParameters.defaultedParameters()
);
}
String body = response.body();
Map<String, Object> responseBody = mapper.readValue(
Expand All @@ -150,13 +169,52 @@ public EvaluationResult evaluateCheck(CheckConfig checkConfig, Map<String, Objec
// TODO: Need a safer way to validate the returned data is in the right format
Object result = responseBody.get("checkResult");
if (result == null) {
return EvaluationResult.UNABLE_TO_DETERMINE;
return new LibraryCheckEvaluation(
EvaluationResult.UNABLE_TO_DETERMINE,
effectiveParameters.parameters(),
effectiveParameters.defaultedParameters()
);
}
return EvaluationResult.fromStringIgnoreCase(result.toString());
return new LibraryCheckEvaluation(
EvaluationResult.fromStringIgnoreCase(result.toString()),
effectiveParameters.parameters(),
effectiveParameters.defaultedParameters()
);
}
catch (Exception e){
Log.error(e);
return EvaluationResult.UNABLE_TO_DETERMINE;
return new LibraryCheckEvaluation(
EvaluationResult.UNABLE_TO_DETERMINE,
effectiveParameters.parameters(),
effectiveParameters.defaultedParameters()
);
}
}

EffectiveParameters buildEffectiveParameters(CheckConfig checkConfig) {
Map<String, Object> configuredParameters = checkConfig.getParameters() != null
? checkConfig.getParameters()
: Map.of();
Map<String, Object> parameters = new HashMap<>(configuredParameters);
List<String> defaultedParameters = new ArrayList<>();

if (declaresAsOfDateParameter(checkConfig) && isMissingParameter(parameters.get(AS_OF_DATE_PARAMETER))) {
parameters.put(AS_OF_DATE_PARAMETER, LocalDate.now().toString());
defaultedParameters.add(AS_OF_DATE_PARAMETER);
}

return new EffectiveParameters(parameters, defaultedParameters);
}

private boolean declaresAsOfDateParameter(CheckConfig checkConfig) {
if (checkConfig.getParameterDefinitions() == null) {
return false;
}
return checkConfig.getParameterDefinitions().stream()
.anyMatch(parameterDefinition -> AS_OF_DATE_PARAMETER.equals(parameterDefinition.getKey()));
}

private boolean isMissingParameter(Object value) {
return value == null || (value instanceof String && ((String) value).isBlank());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package org.acme.service;

import org.acme.model.domain.CheckConfig;
import org.acme.model.domain.ParameterDefinition;
import org.junit.jupiter.api.Test;

import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class LibraryApiServiceTest {

@Test
void buildEffectiveParameters_defaultsMissingAsOfDateWhenDeclared() {
LibraryApiService service = new LibraryApiService();
CheckConfig checkConfig = checkConfigWithParameters(Map.of("minAge", 65), asOfDateParameter());

LibraryApiService.EffectiveParameters result = service.buildEffectiveParameters(checkConfig);

assertEquals(LocalDate.now().toString(), result.parameters().get("asOfDate"));
assertEquals(List.of("asOfDate"), result.defaultedParameters());
assertFalse(checkConfig.getParameters().containsKey("asOfDate"));
}

@Test
void buildEffectiveParameters_defaultsBlankAsOfDateWhenDeclared() {
LibraryApiService service = new LibraryApiService();
Map<String, Object> parameters = new HashMap<>();
parameters.put("asOfDate", " ");
parameters.put("minAge", 65);
CheckConfig checkConfig = checkConfigWithParameters(parameters, asOfDateParameter());

LibraryApiService.EffectiveParameters result = service.buildEffectiveParameters(checkConfig);

assertEquals(LocalDate.now().toString(), result.parameters().get("asOfDate"));
assertEquals(List.of("asOfDate"), result.defaultedParameters());
assertEquals(" ", checkConfig.getParameters().get("asOfDate"));
}

@Test
void buildEffectiveParameters_keepsExplicitAsOfDate() {
LibraryApiService service = new LibraryApiService();
CheckConfig checkConfig = checkConfigWithParameters(
Map.of("asOfDate", "2025-12-31", "minAge", 65),
asOfDateParameter()
);

LibraryApiService.EffectiveParameters result = service.buildEffectiveParameters(checkConfig);

assertEquals("2025-12-31", result.parameters().get("asOfDate"));
assertTrue(result.defaultedParameters().isEmpty());
}

@Test
void buildEffectiveParameters_doesNotDefaultWhenAsOfDateIsNotDeclared() {
LibraryApiService service = new LibraryApiService();
CheckConfig checkConfig = checkConfigWithParameters(Map.of("minAge", 65), minAgeParameter());

LibraryApiService.EffectiveParameters result = service.buildEffectiveParameters(checkConfig);

assertFalse(result.parameters().containsKey("asOfDate"));
assertTrue(result.defaultedParameters().isEmpty());
}

@Test
void buildEffectiveParameters_returnsMutableCopy() {
LibraryApiService service = new LibraryApiService();
Map<String, Object> parameters = Map.of("minAge", 65);
CheckConfig checkConfig = checkConfigWithParameters(parameters, minAgeParameter());

LibraryApiService.EffectiveParameters result = service.buildEffectiveParameters(checkConfig);

assertNotSame(parameters, result.parameters());
}

private CheckConfig checkConfigWithParameters(
Map<String, Object> parameters,
ParameterDefinition parameterDefinition
) {
CheckConfig checkConfig = new CheckConfig();
checkConfig.setParameters(parameters);
checkConfig.setParameterDefinitions(List.of(parameterDefinition));
return checkConfig;
}

private ParameterDefinition asOfDateParameter() {
ParameterDefinition parameterDefinition = new ParameterDefinition();
parameterDefinition.setKey("asOfDate");
parameterDefinition.setType("date");
return parameterDefinition;
}

private ParameterDefinition minAgeParameter() {
ParameterDefinition parameterDefinition = new ParameterDefinition();
parameterDefinition.setKey("minAge");
parameterDefinition.setType("number");
return parameterDefinition;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ const SelectedEligibilityCheck = ({
const displayName = () =>
checkConfig().aliasName || checkConfig().checkName;

const isBlankParameterValue = (value: unknown) => {
return value === undefined || value === null || value === "";
};

const usesAsOfDateDefault = (parameter: ParameterDefinition) => {
return (
!!checkConfig().evaluationUrl &&
parameter.key === "asOfDate" &&
parameter.type === "date" &&
isBlankParameterValue(checkConfig().parameters[parameter.key])
);
};

const unfilledRequiredParameters = () => {
return [];
};
Expand Down Expand Up @@ -88,7 +101,14 @@ const SelectedEligibilityCheck = ({
{(parameter: ParameterDefinition) => {
const getLabel = () => {
let value = checkConfig().parameters[parameter.key];
return value !== undefined ? (
if (usesAsOfDateDefault(parameter)) {
return (
<span class="text-gray-500">
Uses today's date by default
</span>
);
}
return !isBlankParameterValue(value) ? (
value.toString()
) : (
<span class="text-yellow-700">Not configured</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Accessor, For, createSignal } from "solid-js";
import { Accessor, For, Show, createSignal } from "solid-js";
import { createStore, SetStoreFunction } from "solid-js/store";

import { titleCase } from "@/utils/title_case";
Expand All @@ -7,7 +7,6 @@ import type {
CheckConfig,
ParameterDefinition,
ParameterValues,
BooleanParameter,
} from "@/types";

const ConfigureCheckModal = ({
Expand Down Expand Up @@ -66,6 +65,12 @@ const ConfigureCheckModal = ({
setTempCheck={setTempCheck}
parameter={() => parameter}
/>
<Show when={usesAsOfDateDefault(checkConfig(), parameter)}>
<div class="mt-1 text-sm text-gray-500">
Leave blank to use today's date when this screener
is evaluated.
</div>
</Show>
</div>
</div>
);
Expand All @@ -91,6 +96,17 @@ const ConfigureCheckModal = ({
);
};

const usesAsOfDateDefault = (
checkConfig: CheckConfig,
parameter: ParameterDefinition
) => {
return (
!!checkConfig.evaluationUrl &&
parameter.key === "asOfDate" &&
parameter.type === "date"
);
};

const ParameterInput = ({
tempCheck,
setTempCheck,
Expand Down Expand Up @@ -125,7 +141,7 @@ const ParameterInput = ({
return (
<ParameterBooleanInput
onParameterChange={onParameterChange}
parameter={parameter as Accessor<BooleanParameter>}
parameter={parameter}
currentValue={() => tempCheck().parameters[parameterKey()]}
/>
);
Expand Down Expand Up @@ -198,7 +214,7 @@ const ParameterBooleanInput = ({
currentValue,
}: {
onParameterChange: (value: any) => void;
parameter: Accessor<BooleanParameter>;
parameter: Accessor<ParameterDefinition>;
currentValue: Accessor<any>;
}) => {
return (
Expand Down
26 changes: 22 additions & 4 deletions builder-frontend/src/components/project/preview/Results.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,17 @@ import checkIcon from "../../../assets/images/checkIcon.svg";
import questionIcon from "../../../assets/images/questionIcon.svg";
import xIcon from "../../../assets/images/xIcon.svg";

function formatParameters(params: ParameterValues): string {
function formatParameters(
params: ParameterValues,
defaultedParameters: string[] = []
): string {
return Object.entries(params)
.map(([key, value]) => `${key}=${value}`)
.map(([key, value]) => {
const defaultedLabel = defaultedParameters.includes(key)
? " (defaulted to today's date)"
: "";
return `${key}=${value}${defaultedLabel}`;
})
.join(", ");
}

Expand Down Expand Up @@ -119,9 +127,19 @@ export default function Results({
</span>
</div>
</Show>
<Show when={check.parameters && Object.keys(check.parameters).length > 0}>
<Show
when={
(check.effectiveParameters &&
Object.keys(check.effectiveParameters).length > 0) ||
(check.parameters &&
Object.keys(check.parameters).length > 0)
}
>
<div class="text-gray-500 text-sm">
{formatParameters(check.parameters)}
{formatParameters(
check.effectiveParameters ?? check.parameters,
check.defaultedParameters
)}
</div>
</Show>
</div>
Expand Down
Loading
Loading