use of org.opencds.cqf.cql.engine.exception.CqlException in project cqf-ruler by DBCG.
the class CdsHooksServlet method doPost.
@Override
@SuppressWarnings("deprecation")
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
logger.info(request.getRequestURI());
try {
// validate that we are dealing with JSON
if (request.getContentType() == null || !request.getContentType().startsWith("application/json")) {
throw new ServletException(String.format("Invalid content type %s. Please use application/json.", request.getContentType()));
}
String baseUrl = this.myAppProperties.getServer_address();
String service = request.getPathInfo().replace("/", "");
JsonParser parser = new JsonParser();
JsonObject requestJson = parser.parse(request.getReader()).getAsJsonObject();
logger.info(requestJson.toString());
Request cdsHooksRequest = new Request(service, requestJson, JsonHelper.getObjectRequired(getService(service), "prefetch"));
Hook hook = HookFactory.createHook(cdsHooksRequest);
String hookName = hook.getRequest().getHook();
logger.info("cds-hooks hook: {}", hookName);
logger.info("cds-hooks hook instance: {}", hook.getRequest().getHookInstance());
logger.info("cds-hooks maxCodesPerQuery: {}", this.getProviderConfiguration().getMaxCodesPerQuery());
logger.info("cds-hooks expandValueSets: {}", this.getProviderConfiguration().getExpandValueSets());
logger.info("cds-hooks searchStyle: {}", this.getProviderConfiguration().getSearchStyle());
logger.info("cds-hooks prefetch maxUriLength: {}", this.getProviderConfiguration().getMaxUriLength());
logger.info("cds-hooks local server address: {}", baseUrl);
logger.info("cds-hooks fhir server address: {}", hook.getRequest().getFhirServerUrl());
logger.info("cds-hooks cql_logging_enabled: {}", this.getProviderConfiguration().getCqlLoggingEnabled());
PlanDefinition planDefinition = read(Ids.newId(PlanDefinition.class, hook.getRequest().getServiceName()));
AtomicBoolean planDefinitionHookMatchesRequestHook = new AtomicBoolean(false);
planDefinition.getAction().forEach(action -> {
action.getTriggerDefinition().forEach(triggerDefn -> {
if (hookName.equals(triggerDefn.getEventName())) {
planDefinitionHookMatchesRequestHook.set(true);
return;
}
});
if (planDefinitionHookMatchesRequestHook.get()) {
return;
}
});
if (!planDefinitionHookMatchesRequestHook.get()) {
throw new ServletException("ERROR: Request hook does not match the service called.");
}
// No tenant information available, so create local system request
RequestDetails requestDetails = new SystemRequestDetails();
LibraryLoader libraryLoader = libraryLoaderFactory.create(Lists.newArrayList(jpaLibraryContentProviderFactory.create(requestDetails)));
Reference reference = planDefinition.getLibrary().get(0);
Library library = read(reference.getReferenceElement());
org.cqframework.cql.elm.execution.Library elm = libraryLoader.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()));
Context context = new Context(elm);
context.setDebugMap(this.getDebugMap());
// provider case
// No tenant information available for cds-hooks
TerminologyProvider serverTerminologyProvider = myJpaTerminologyProviderFactory.create(requestDetails);
// TODO make sure tooling handles remote
context.registerDataProvider("http://hl7.org/fhir", fhirRetrieveProviderFactory.create(requestDetails, serverTerminologyProvider));
context.registerTerminologyProvider(serverTerminologyProvider);
context.registerLibraryLoader(libraryLoader);
context.setContextValue("Patient", hook.getRequest().getContext().getPatientId().replace("Patient/", ""));
context.setExpressionCaching(true);
EvaluationContext<PlanDefinition> evaluationContext = new Stu3EvaluationContext(hook, FhirContext.forCached(FhirVersionEnum.DSTU3).newRestfulGenericClient(baseUrl), context, elm, planDefinition, this.getProviderConfiguration(), this.modelResolver);
this.setAccessControlHeaders(response);
response.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType());
Stu3HookEvaluator evaluator = new Stu3HookEvaluator(this.modelResolver);
String jsonResponse = toJsonResponse(evaluator.evaluate(evaluationContext));
logger.info(jsonResponse);
response.getWriter().println(jsonResponse);
} catch (BaseServerResponseException e) {
this.setAccessControlHeaders(response);
// This will be overwritten with the correct status code downstream if needed.
response.setStatus(500);
response.getWriter().println("ERROR: Exception connecting to remote server.");
this.printMessageAndCause(e, response);
this.handleServerResponseException(e, response);
this.printStackTrack(e, response);
logger.error(e.toString());
} catch (DataProviderException e) {
this.setAccessControlHeaders(response);
// This will be overwritten with the correct status code downstream if needed.
response.setStatus(500);
response.getWriter().println("ERROR: Exception in DataProvider.");
this.printMessageAndCause(e, response);
if (e.getCause() != null && (e.getCause() instanceof BaseServerResponseException)) {
this.handleServerResponseException((BaseServerResponseException) e.getCause(), response);
}
this.printStackTrack(e, response);
logger.error(e.toString());
} catch (CqlException e) {
this.setAccessControlHeaders(response);
// This will be overwritten with the correct status code downstream if needed.
response.setStatus(500);
response.getWriter().println("ERROR: Exception in CQL Execution.");
this.printMessageAndCause(e, response);
if (e.getCause() != null && (e.getCause() instanceof BaseServerResponseException)) {
this.handleServerResponseException((BaseServerResponseException) e.getCause(), response);
}
this.printStackTrack(e, response);
logger.error(e.toString());
} catch (Exception e) {
logger.error(e.toString());
throw new ServletException("ERROR: Exception in cds-hooks processing.", e);
}
}
use of org.opencds.cqf.cql.engine.exception.CqlException in project cqf-ruler by DBCG.
the class CdsHooksServlet method doPost.
@Override
@SuppressWarnings("deprecation")
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
logger.info(request.getRequestURI());
try {
// validate that we are dealing with JSON
if (request.getContentType() == null || !request.getContentType().startsWith("application/json")) {
throw new ServletException(String.format("Invalid content type %s. Please use application/json.", request.getContentType()));
}
String baseUrl = this.myAppProperties.getServer_address();
String service = request.getPathInfo().replace("/", "");
JsonParser parser = new JsonParser();
Request cdsHooksRequest = new Request(service, parser.parse(request.getReader()).getAsJsonObject(), JsonHelper.getObjectRequired(getService(service), "prefetch"));
logger.info(cdsHooksRequest.getRequestJson().toString());
Hook hook = HookFactory.createHook(cdsHooksRequest);
String hookName = hook.getRequest().getHook();
logger.info("cds-hooks hook: {}", hookName);
logger.info("cds-hooks hook instance: {}", hook.getRequest().getHookInstance());
logger.info("cds-hooks maxCodesPerQuery: {}", this.getProviderConfiguration().getMaxCodesPerQuery());
logger.info("cds-hooks expandValueSets: {}", this.getProviderConfiguration().getExpandValueSets());
logger.info("cds-hooks searchStyle: {}", this.getProviderConfiguration().getSearchStyle());
logger.info("cds-hooks prefetch maxUriLength: {}", this.getProviderConfiguration().getMaxUriLength());
logger.info("cds-hooks local server address: {}", baseUrl);
logger.info("cds-hooks fhir server address: {}", hook.getRequest().getFhirServerUrl());
logger.info("cds-hooks cql_logging_enabled: {}", this.getProviderConfiguration().getCqlLoggingEnabled());
PlanDefinition planDefinition = read(Ids.newId(PlanDefinition.class, hook.getRequest().getServiceName()));
AtomicBoolean planDefinitionHookMatchesRequestHook = new AtomicBoolean(false);
planDefinition.getAction().forEach(action -> {
action.getTrigger().forEach(trigger -> {
if (hookName.equals(trigger.getName())) {
planDefinitionHookMatchesRequestHook.set(true);
return;
}
});
if (planDefinitionHookMatchesRequestHook.get()) {
return;
}
});
if (!planDefinitionHookMatchesRequestHook.get()) {
throw new ServletException("ERROR: Request hook does not match the service called.");
}
// No tenant information available, so create local system request
RequestDetails requestDetails = new SystemRequestDetails();
LibraryLoader libraryLoader = libraryLoaderFactory.create(Lists.newArrayList(jpaLibraryContentProviderFactory.create(requestDetails)));
CanonicalType canonical = planDefinition.getLibrary().get(0);
Library library = search(Library.class, Searches.byCanonical(canonical)).single();
org.cqframework.cql.elm.execution.Library elm = libraryLoader.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()));
Context context = new Context(elm);
context.setDebugMap(this.getDebugMap());
// provider case
// No tenant information available for cds-hooks
TerminologyProvider serverTerminologyProvider = myJpaTerminologyProviderFactory.create(requestDetails);
context.registerDataProvider("http://hl7.org/fhir", // TODO make sure tooling
fhirRetrieveProviderFactory.create(requestDetails, serverTerminologyProvider));
// handles remote
context.registerTerminologyProvider(serverTerminologyProvider);
context.registerLibraryLoader(libraryLoader);
context.setContextValue("Patient", hook.getRequest().getContext().getPatientId().replace("Patient/", ""));
context.setExpressionCaching(true);
EvaluationContext<PlanDefinition> evaluationContext = new R4EvaluationContext(hook, FhirContext.forCached(FhirVersionEnum.R4).newRestfulGenericClient(baseUrl), context, elm, planDefinition, this.getProviderConfiguration(), this.modelResolver);
this.setAccessControlHeaders(response);
response.setHeader("Content-Type", ContentType.APPLICATION_JSON.getMimeType());
R4HookEvaluator evaluator = new R4HookEvaluator(this.modelResolver);
String jsonResponse = toJsonResponse(evaluator.evaluate(evaluationContext));
logger.info(jsonResponse);
response.getWriter().println(jsonResponse);
} catch (BaseServerResponseException e) {
this.setAccessControlHeaders(response);
// This will be overwritten with the correct status code downstream if needed.
response.setStatus(500);
response.getWriter().println("ERROR: Exception connecting to remote server.");
this.printMessageAndCause(e, response);
this.handleServerResponseException(e, response);
this.printStackTrack(e, response);
logger.error(e.toString());
} catch (DataProviderException e) {
this.setAccessControlHeaders(response);
// This will be overwritten with the correct status code downstream if needed.
response.setStatus(500);
response.getWriter().println("ERROR: Exception in DataProvider.");
this.printMessageAndCause(e, response);
if (e.getCause() != null && (e.getCause() instanceof BaseServerResponseException)) {
this.handleServerResponseException((BaseServerResponseException) e.getCause(), response);
}
this.printStackTrack(e, response);
logger.error(e.toString());
} catch (CqlException e) {
this.setAccessControlHeaders(response);
// This will be overwritten with the correct status code downstream if needed.
response.setStatus(500);
response.getWriter().println("ERROR: Exception in CQL Execution.");
this.printMessageAndCause(e, response);
if (e.getCause() != null && (e.getCause() instanceof BaseServerResponseException)) {
this.handleServerResponseException((BaseServerResponseException) e.getCause(), response);
}
this.printStackTrack(e, response);
logger.error(e.toString());
} catch (Exception e) {
logger.error(e.toString());
throw new ServletException("ERROR: Exception in cds-hooks processing.", e);
}
}
use of org.opencds.cqf.cql.engine.exception.CqlException in project quality-measure-and-cohort-service by Alvearie.
the class CohortServiceExceptionMapper method toServiceErrorList.
public ServiceErrorList toServiceErrorList(Throwable ex) {
List<ServiceError> errorsList = new ArrayList<>();
// The IBM Cloud API Handbook mandates that REST errors be returned using
// an error container model class (ServiceErrorList) which in turn contains
// a list of error objects (ServiceError) which contains specific error fields
// serviceErrorList contains the status request (ie 400, 500 etc.) for our service
// and the status code for underlying services is captured in the serviceError objects
// within the list
ServiceErrorList serviceErrorList = new ServiceErrorList().errors(errorsList);
ServiceError se;
String description = "";
String reason = "";
int serviceErrorCode = 500;
int serviceErrorListCode = 500;
ErrorSource errorSource;
try {
if (ex instanceof FhirClientConnectionException) {
FhirClientConnectionException fcce = (FhirClientConnectionException) ex;
serviceErrorCode = fcce.getStatusCode();
// if something more specific is not returned
if (serviceErrorCode == 0) {
serviceErrorCode = Status.BAD_REQUEST.getStatusCode();
}
serviceErrorListCode = serviceErrorCode;
Status status = Status.fromStatusCode(serviceErrorCode);
reason = fcce.getLocalizedMessage();
if (reason == null || reason.trim().isEmpty()) {
reason = status.getReasonPhrase();
}
description = "Reason: FhirClientConnectionException";
errorSource = ErrorSource.COHORT_SERVICE;
} else // The 401 error code information is captured in the ServiceError object
if (ex instanceof AuthenticationException) {
AuthenticationException ae = (AuthenticationException) ex;
serviceErrorListCode = Status.BAD_REQUEST.getStatusCode();
serviceErrorCode = ae.getStatusCode();
description = "Could not authenticate with FHIR server.";
errorSource = ErrorSource.FHIR_SERVER;
} else if (ex instanceof ResourceNotFoundException) {
ResourceNotFoundException rnfe = (ResourceNotFoundException) ex;
serviceErrorListCode = Status.BAD_REQUEST.getStatusCode();
serviceErrorCode = rnfe.getStatusCode();
reason = "FHIR Resource Not Found: " + rnfe.getLocalizedMessage();
description = rnfe.getResponseBody();
errorSource = ErrorSource.FHIR_SERVER;
} else // library ids don't resolve properly
if (ex instanceof IllegalArgumentException || ex instanceof UnsupportedOperationException) {
serviceErrorCode = Status.BAD_REQUEST.getStatusCode();
serviceErrorListCode = serviceErrorCode;
errorSource = ErrorSource.COHORT_SERVICE;
} else // will get thrown by the CQL engine generally due to language-related issues
if (ex instanceof CqlException) {
serviceErrorCode = Status.BAD_REQUEST.getStatusCode();
serviceErrorListCode = serviceErrorCode;
errorSource = ErrorSource.COHORT_SERVICE;
} else // parsing errors.
if (ex instanceof MismatchedInputException) {
serviceErrorCode = Status.BAD_REQUEST.getStatusCode();
serviceErrorListCode = serviceErrorCode;
errorSource = ErrorSource.COHORT_SERVICE;
} else if (ex instanceof JsonParseException) {
serviceErrorCode = Status.BAD_REQUEST.getStatusCode();
serviceErrorListCode = serviceErrorCode;
description = "Invalid JSON input";
errorSource = ErrorSource.COHORT_SERVICE;
} else // will get thrown by HAPI FHIR when a requested resource is not found in the target FHIR server
if (ex instanceof BaseServerResponseException) {
serviceErrorCode = ((BaseServerResponseException) ex).getStatusCode();
serviceErrorListCode = serviceErrorCode;
BaseServerResponseException sre = (BaseServerResponseException) ex;
reason = "Exception while communicating with FHIR";
errorSource = ErrorSource.FHIR_SERVER;
if (sre.getResponseBody() != null) {
description = sre.getResponseBody();
} else {
// Some errors do not have a response body
description = sre.getLocalizedMessage();
}
} else // catch everything else and return a 500
{
serviceErrorCode = Status.INTERNAL_SERVER_ERROR.getStatusCode();
serviceErrorListCode = serviceErrorCode;
description = ex.getMessage();
errorSource = ErrorSource.COHORT_SERVICE;
}
if (reason.isEmpty()) {
reason = ex.getLocalizedMessage();
}
se = new ServiceError(serviceErrorCode, reason);
se.setDescription(description);
errorsList.add(se);
// loop through the exception chain logging the cause of each one
// since these can contain valuable information about the root problems
createServiceErrorsForExceptions(ex, serviceErrorCode, errorsList);
serviceErrorList = serviceErrorList.statusCode(serviceErrorListCode).errorSource(errorSource);
logger.error("HTTP Status: " + serviceErrorList.getStatusCode(), ex);
} catch (Throwable nestedEx) {
// This should not really occur unless there is a bug in this code.
// Build a 500 ServiceError with some detail
se = new ServiceError(Status.INTERNAL_SERVER_ERROR.getStatusCode(), nestedEx.getLocalizedMessage());
se.setDescription("Reason: Uncaught nested exception");
logger.error("HTTP Status: " + se.getCode() + ", Nested Exception", nestedEx);
logger.error("Original Exception", ex);
serviceErrorList = serviceErrorList.statusCode(se.getCode()).errorSource(ErrorSource.COHORT_SERVICE);
errorsList.add(se);
}
return serviceErrorList;
}
use of org.opencds.cqf.cql.engine.exception.CqlException in project quality-measure-and-cohort-service by Alvearie.
the class CohortServiceExceptionMapperTest method testToResponseCqlException.
@Test
public void testToResponseCqlException() throws Exception {
Response response = exMapper.toResponse(new CqlException("Unexpected exception caught during execution", new IllegalArgumentException("Failed to resolve ValueSet")));
ServiceErrorList actual = (ServiceErrorList) response.getEntity();
ServiceErrorList expected = new ServiceErrorList();
expected.setStatusCode(400);
expected.getErrors().add(newServiceError(400, "Unexpected exception caught during execution", ""));
expected.getErrors().add(newServiceError(400, "Failed to resolve ValueSet", null));
expected.setErrorSource(ErrorSource.COHORT_SERVICE);
testErrorListEquality(expected, actual);
}
use of org.opencds.cqf.cql.engine.exception.CqlException in project quality-measure-and-cohort-service by Alvearie.
the class CqlEvaluatorIntegrationTest method runUnsupportedValueSetPropertyTest.
private void runUnsupportedValueSetPropertyTest(String expression) throws Exception {
Patient patient = getPatient("123", Enumerations.AdministrativeGender.FEMALE, "1983-12-02");
Condition condition = new Condition();
condition.setId("Condition");
condition.setSubject(new Reference(patient));
condition.getCode().addCoding().setSystem("SNOMED-CT").setCode("1234");
// This stub works for [Condition] c where c.code in "ValueSet"
mockFhirResourceRetrieval("/Condition?subject=Patient%2F123&_format=json", condition);
CqlEvaluator evaluator = setupTestFor(patient, "cql.valueset", ClasspathCqlLibraryProvider.FHIR_HELPERS_CLASSPATH);
CqlException ex = assertThrows("Missing expected exception", CqlException.class, () -> {
evaluator.evaluate(new CqlVersionedIdentifier("TestUnsupported", "1.0.0"), null, newPatientContext("123"), Collections.singleton(expression));
});
assertTrue("Unexpected exception message: " + ex.getMessage(), ex.getMessage().contains("version and code system bindings are not supported at this time"));
}
Aggregations