diff --git a/Dockerfile b/Dockerfile index 2c86ccd..0a8f155 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,4 +19,4 @@ EXPOSE 8080 # execute it # CMD ["mvn", "exec:java"] -CMD ["java", "-jar", "target/cqlTranslationServer-2.4.0.jar", "-d"] +CMD ["java", "-jar", "target/cqlTranslationServer-2.5.0.jar", "-d"] diff --git a/README.md b/README.md index f50272f..9f2e57c 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Build: Execute via the command line: - java -jar target/cqlTranslationServer-2.4.0.jar + java -jar target/cqlTranslationServer-2.5.0.jar _NOTE: The cqlTranslationServer jar assumes that all dependency jars are located in a `libs` directory relative to the jar's location. If you move the jar from the `target` directory, you will need to move the `target/libs` directory as well. This project no longer produces an "uber-jar", as the CQL-to-ELM classes do not function properly when repackaged into a single jar file._ @@ -17,7 +17,8 @@ _NOTE: The cqlTranslationServer jar assumes that all dependency jars are located CQL Translation Service versions prior to version 2.0.0 always mirrored the CQL Tools (CQL-to-ELM translator) version they exposed. Starting with version 2.0.0, semantic versioning is now used. As a result, the version of the CQL Translation Service differs from the version of the CQL Tools that it exposes. The following table shows the relationship between [CQL Translation Service releases](https://github.com/cqframework/cql-translation-service/releases) and [CQL Tools releases](https://github.com/cqframework/clinical_quality_language/releases). Note that prior releases from the [MITRE repo](https://github.com/mitre/cql-translation-service/releases) are not included here. | CQL Translation Service | CQL Tools | -|-------------------------|-----------------------------------------| +| ----------------------- | --------------------------------------- | +| 2.5.0 | 3.15.0 | | 2.4.0 | 3.7.1 | | 2.3.0 | 3.3.2 | | 2.2.0 | 2.11.0 | @@ -167,32 +168,33 @@ Will return: ### CQL-to-ELM Translator Options -The CQL-to-ELM translator supports many options to control the output. These options can be passed to the service as query parameters when you post CQL to the service (e.g., `POST http://localhost:8080/cql/translator?annotations=true&result-types=true`). These query parameters are supported for both simple requests and multipart requests. See the table below for the available options: - -|Option|Values|Default| -|----|----|----| -|date-range-optimization|true\|false|false| -|annotations|true\|false|false| -|locators|true\|false|false| -|result-types|true\|false|false| -|signatures|None\|Differing\|Overloads\|All|None| -|detailed-errors|true\|false|false| -|disable-list-traversal|true\|false|false| -|disable-list-demotion|true\|false|false| -|disable-list-promotion|true\|false|false| -|enable-interval-demotion|true\|false|false| -|enable-interval-promotion|true\|false|false| -|disable-method-invocation|true\|false|false| -|require-from-keyword|true\|false|false| -|strict|true\|false|false| -|debug|true\|false|false| -|validate-units|true\|false|false| +The CQL-to-ELM translator supports many options to control the output. These options can be passed to the service as query parameters when you post CQL to the service (e.g., `POST http://localhost:8080/cql/translator?annotations=true&result-types=true`). These query parameters are supported for both simple requests and multipart requests. See the table below for the available options: + +| Option | Values | Default | +| ------------------------- | ------------------------------- | ------- | +| date-range-optimization | true\|false | false | +| annotations | true\|false | false | +| locators | true\|false | false | +| result-types | true\|false | false | +| signatures | None\|Differing\|Overloads\|All | None | +| detailed-errors | true\|false | false | +| disable-list-traversal | true\|false | false | +| disable-list-demotion | true\|false | false | +| disable-list-promotion | true\|false | false | +| enable-interval-demotion | true\|false | false | +| enable-interval-promotion | true\|false | false | +| disable-method-invocation | true\|false | false | +| require-from-keyword | true\|false | false | +| strict | true\|false | false | +| debug | true\|false | false | +| validate-units | true\|false | false | For more information on each of these options, see the [CQL-to-ELM Overview](https://github.com/cqframework/clinical_quality_language/blob/master/Src/java/cql-to-elm/OVERVIEW.md#usage). _**NOTE:**_ -* _Previous versions of the CQL-to-ELM Translation Service defaulted **annotations** to true. To align better with the CQL-to-ELM console client, the translation service now defaults annotations to false._ -* _Previous versions of the CQL-to-ELM Translation Service allowed list-promotion to be disabled via an extra multipart form field named **disablePromotion**. This is no longer supported, as it was ambiguous and inconsistent with the CQL-to-ELM console client. The **disable-list-promotion** query parameter should be used instead._ + +- _Previous versions of the CQL-to-ELM Translation Service defaulted **annotations** to true. To align better with the CQL-to-ELM console client, the translation service now defaults annotations to false._ +- _Previous versions of the CQL-to-ELM Translation Service allowed list-promotion to be disabled via an extra multipart form field named **disablePromotion**. This is no longer supported, as it was ambiguous and inconsistent with the CQL-to-ELM console client. The **disable-list-promotion** query parameter should be used instead._ ## Formatter Endpoint @@ -231,7 +233,6 @@ formatted part for each CQL part in the submitted package. Example usage via HTTP request: - POST /cql/formatter HTTP/1.1 Host: localhost:8080 Content-Type: multipart/form-data; boundary=X-INSOMNIA-BOUNDARY @@ -284,22 +285,21 @@ Will return: 42 --Boundary_2_1638692479_1658770032240-- - ## Docker Deployment You may deploy pre-built Docker images into your existing hosting environment with: - docker run -d -p 8080:8080 --restart unless-stopped cqframework/cql-translation-service:latest # or any official tag + docker run -d -p 8080:8080 --restart unless-stopped cqframework/cql-translation-service:latest # or any official tag And you're done. No environment variables or further configuration are needed. Jedis may use your existing Kubernetes, Open Shift etc installations as you see fit. :) To build your own image for your current architecture: - docker build -t cqframework/cql-translation-service:latest . # but use your your own repo and tag strings! + docker build -t cqframework/cql-translation-service:latest . # but use your your own repo and tag strings! To build your own image for multiple architectures (e.g., Intel and Mac M1): - docker buildx build --platform linux/amd64,linux/arm64 -t cqframework/cql-translation-service:latest . # but use your your own repo and tag strings! + docker buildx build --platform linux/amd64,linux/arm64 -t cqframework/cql-translation-service:latest . # but use your your own repo and tag strings! Note that Docker doesn't support loading multi-platform builds locally, so the above multi-platform build commmand is only helpful when used with `--push`. See: [https://github.com/docker/buildx/issues/59](https://github.com/docker/buildx/issues/59). @@ -315,7 +315,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/pom.xml b/pom.xml index 38d6023..493a267 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.mitre.bonnie cqlTranslationServer jar - 2.4.0 + 2.5.0 cqlTranslationServer @@ -26,6 +26,13 @@ pom import + + org.junit + junit-bom + 5.10.2 + pom + import + @@ -53,7 +60,7 @@ org.glassfish.jaxb jaxb-runtime - 3.1.0-M1 + 4.0.5 runtime @@ -62,9 +69,8 @@ 3.15.6.Final - junit - junit - 4.13.2 + org.junit.jupiter + junit-jupiter test @@ -194,8 +200,8 @@ - 3.7.1 - 3.0.12 + 3.15.0 + 3.1.8 UTF-8 diff --git a/src/test/java/org/mitre/bonnie/cqlTranslationServer/FormatterResourceTest.java b/src/test/java/org/mitre/bonnie/cqlTranslationServer/FormatterResourceTest.java index 723ddad..8e9f1b5 100644 --- a/src/test/java/org/mitre/bonnie/cqlTranslationServer/FormatterResourceTest.java +++ b/src/test/java/org/mitre/bonnie/cqlTranslationServer/FormatterResourceTest.java @@ -1,7 +1,7 @@ package org.mitre.bonnie.cqlTranslationServer; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.ClientBuilder; @@ -14,9 +14,9 @@ import org.glassfish.grizzly.http.server.HttpServer; import org.glassfish.jersey.media.multipart.FormDataMultiPart; import org.glassfish.jersey.media.multipart.MultiPartFeature; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; public class FormatterResourceTest { @@ -26,7 +26,7 @@ public class FormatterResourceTest { // The CQL formatter is hard-coded to use \r\n for line breaks private final String newLine = "\r\n"; - @Before + @BeforeEach public void setUp() throws Exception { // start the server server = Main.startServer(); @@ -36,13 +36,13 @@ public void setUp() throws Exception { target = c.target(Main.BASE_URI.replace("0.0.0.0", "localhost")); } - @After + @AfterEach public void tearDown() throws Exception { server.shutdownNow(); } @Test - public void testUnformattedLibrary() { + void testUnformattedLibrary() { String input = "library HelloWorld using QDM define Hello: 'World'"; Response resp = target.path("formatter").request(FormatterResource.CQL_TEXT_TYPE).post(Entity.entity(input, FormatterResource.CQL_TEXT_TYPE)); assertEquals(Status.OK.getStatusCode(), resp.getStatus()); @@ -62,7 +62,7 @@ public void testUnformattedLibrary() { } @Test - public void testInvalidCql() { + void testInvalidCql() { String input = "lib HelloWorld using QDM define Hello: 'World'"; Response resp = target.path("formatter").request(FormatterResource.CQL_TEXT_TYPE).post(Entity.entity(input, FormatterResource.CQL_TEXT_TYPE)); assertEquals(Status.BAD_REQUEST.getStatusCode(), resp.getStatus()); @@ -73,7 +73,7 @@ public void testInvalidCql() { } @Test - public void testSingleUnformattedLibraryAsMultipart() { + void testSingleUnformattedLibraryAsMultipart() { String input = "library HelloWorld2 using QDM define Hello: 'World2'"; FormDataMultiPart pkg = new FormDataMultiPart(); pkg.field("foo", input, new MediaType("application", "cql")); @@ -99,7 +99,7 @@ public void testSingleUnformattedLibraryAsMultipart() { } @Test - public void testTwoUnformattedLibrariesAsMultipart() { + void testTwoUnformattedLibrariesAsMultipart() { String input = "library HelloWorld3 using FHIR define Hello: 'World3'"; String input2 = "library FHIRHelpers version '4.0.1' using FHIR version '4.0.1'\n" + "context Patient define \"IsFakeFHIRHelpers\": true"; @@ -142,7 +142,7 @@ public void testTwoUnformattedLibrariesAsMultipart() { } @Test - public void testInvalidCqlInMultipart() { + void testInvalidCqlInMultipart() { String input = "library HelloWorld3 using FHIR define Hello: 'World3'"; String input2 = "library FHIRHelpers version '4.0.1' using FHIR version '4.0.1'\n" + "ctx Patient define \"IsFakeFHIRHelpers\": true"; diff --git a/src/test/java/org/mitre/bonnie/cqlTranslationServer/TranslationResourceTest.java b/src/test/java/org/mitre/bonnie/cqlTranslationServer/TranslationResourceTest.java index 6ed7c70..cc38c3e 100644 --- a/src/test/java/org/mitre/bonnie/cqlTranslationServer/TranslationResourceTest.java +++ b/src/test/java/org/mitre/bonnie/cqlTranslationServer/TranslationResourceTest.java @@ -1,10 +1,10 @@ package org.mitre.bonnie.cqlTranslationServer; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.io.File; import java.io.IOException; @@ -39,9 +39,9 @@ import org.glassfish.jersey.media.multipart.FormDataBodyPart; import org.glassfish.jersey.media.multipart.FormDataMultiPart; import org.glassfish.jersey.media.multipart.MultiPartFeature; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; @@ -51,7 +51,7 @@ public class TranslationResourceTest { private HttpServer server; private WebTarget target; - @Before + @BeforeEach public void setUp() throws Exception { // start the server server = Main.startServer(); @@ -67,7 +67,7 @@ public void setUp() throws Exception { target = c.target(Main.BASE_URI.replace("0.0.0.0", "localhost")); } - @After + @AfterEach public void tearDown() throws Exception { server.shutdownNow(); } @@ -139,7 +139,7 @@ private void validateListPromotionEnabled(String cqlTitle) { } @Test - public void testInvalidCqlAsXml() { + void testInvalidCqlAsXml() { File file = new File(TranslationResourceTest.class.getResource("invalid.cql").getFile()); Response resp = target.path("translator").request(TranslationResource.ELM_XML_TYPE).post(Entity.entity(file, TranslationResource.CQL_TEXT_TYPE)); assertEquals(Status.BAD_REQUEST.getStatusCode(), resp.getStatus()); @@ -159,7 +159,7 @@ public void testInvalidCqlAsXml() { } @Test - public void testInvalidCqlAsJson() { + void testInvalidCqlAsJson() { File file = new File(TranslationResourceTest.class.getResource("invalid.cql").getFile()); Response resp = target.path("translator").request(TranslationResource.ELM_JSON_TYPE).post(Entity.entity(file, TranslationResource.CQL_TEXT_TYPE)); assertEquals(Status.BAD_REQUEST.getStatusCode(), resp.getStatus()); @@ -178,7 +178,7 @@ public void testInvalidCqlAsJson() { } @Test - public void testMissingLibraryAsJson() { + void testMissingLibraryAsJson() { File file = new File(TranslationResourceTest.class.getResource("missingLibrary.cql").getFile()); Response resp = target.path("translator").request(TranslationResource.ELM_JSON_TYPE).post(Entity.entity(file, TranslationResource.CQL_TEXT_TYPE)); assertEquals(Status.BAD_REQUEST.getStatusCode(), resp.getStatus()); @@ -198,7 +198,7 @@ public void testMissingLibraryAsJson() { } @Test - public void testValidLibraryAsJson() { + void testValidLibraryAsJson() { File file = new File(TranslationResourceTest.class.getResource("valid.cql").getFile()); Response resp = target.path("translator").request(TranslationResource.ELM_JSON_TYPE).post(Entity.entity(file, TranslationResource.CQL_TEXT_TYPE)); assertEquals(Status.OK.getStatusCode(), resp.getStatus()); @@ -224,7 +224,7 @@ public void testValidLibraryAsJson() { } @Test - public void testValidLibraryAsJsonWithAnnotationsAndResultTypes() { + void testValidLibraryAsJsonWithAnnotationsAndResultTypes() { File file = new File(TranslationResourceTest.class.getResource("valid.cql").getFile()); Response resp = target.path("translator").queryParam("annotations", "true").queryParam("result-types", "true").request(TranslationResource.ELM_JSON_TYPE).post(Entity.entity(file, TranslationResource.CQL_TEXT_TYPE)); assertEquals(Status.OK.getStatusCode(), resp.getStatus()); @@ -249,27 +249,27 @@ public void testValidLibraryAsJsonWithAnnotationsAndResultTypes() { } @Test - public void testInvalidListPromotionExistsAsJson() { + void testInvalidListPromotionExistsAsJson() { validateListPromotionDisabled("ListPromotionExists.cql", 8, 13, "Could not resolve call to operator Exists with signature (System.Integer)."); } @Test - public void testInvalidListPromotionInAsJson() { + void testInvalidListPromotionInAsJson() { validateListPromotionDisabled("ListPromotionIn.cql", 7, 16, "Could not resolve call to operator In with signature (System.Integer,System.Integer)."); } @Test - public void testValidListPromotionInAsJson() { + void testValidListPromotionInAsJson() { validateListPromotionEnabled("ListPromotionIn.cql"); } @Test - public void testValidListPromotionExistsAsJson() { + void testValidListPromotionExistsAsJson() { validateListPromotionEnabled("ListPromotionExists.cql"); } @Test - public void testSingleLibraryAsMultipart() { + void testSingleLibraryAsMultipart() { File file = new File(TranslationResourceTest.class.getResource("valid.cql").getFile()); FormDataMultiPart pkg = new FormDataMultiPart(); pkg.field("foo", file, new MediaType("application", "cql")); @@ -285,7 +285,7 @@ public void testSingleLibraryAsMultipart() { } @Test - public void testCrossLibraryResolution() { + void testCrossLibraryResolution() { String filenames[] = {"ProvidesDependency.cql", "HasDependency.cql"}; FormDataMultiPart pkg = new FormDataMultiPart(); for (String filename: filenames) { @@ -313,7 +313,7 @@ public void testCrossLibraryResolution() { } @Test - public void testMultipartRequestAsXml() throws Exception { + void testMultipartRequestAsXml() throws Exception { String filenames[] = {"valid.cql"}; FormDataMultiPart pkg = new FormDataMultiPart(); for (String filename: filenames) { @@ -337,7 +337,7 @@ public void testMultipartRequestAsXml() throws Exception { } @Test - public void testMultipartRequestAsJsonAndXml() throws Exception { + void testMultipartRequestAsJsonAndXml() throws Exception { String filenames[] = {"valid.cql"}; FormDataMultiPart pkg = new FormDataMultiPart(); for (String filename: filenames) { @@ -370,7 +370,7 @@ public void testMultipartRequestAsJsonAndXml() throws Exception { } @Test - public void testLibraryThatNeedsUCUM() { + void testLibraryThatNeedsUCUM() { File file = new File(TranslationResourceTest.class.getResource("NeedsUCUM.cql").getFile()); Response resp = target.path("translator").queryParam("signatures", "All").request(TranslationResource.ELM_JSON_TYPE).post(Entity.entity(file, TranslationResource.CQL_TEXT_TYPE)); assertEquals(Status.OK.getStatusCode(), resp.getStatus());