diff --git a/docker/aktin-client/.env b/docker/aktin-client/.env index c830d48..3e53bc2 100644 --- a/docker/aktin-client/.env +++ b/docker/aktin-client/.env @@ -22,3 +22,4 @@ LOG_LEVEL_FEASIBILITY=FINE HARD_RATE_LIMIT_NREQUESTS=2 HARD_RATE_LIMIT_RESET_MINUTES=1 OBFUSCATOR_EPSILON=0.28 +CQL_SOCKET_TIMEOUT_MS=60000 diff --git a/docker/aktin-client/sysproc.properties b/docker/aktin-client/sysproc.properties index d600ce6..8198a2e 100644 --- a/docker/aktin-client/sysproc.properties +++ b/docker/aktin-client/sysproc.properties @@ -21,6 +21,7 @@ process.args=${PROCESS_ARGS} plugin.feasibility.cql.fhirbaseurl=${CQL_FHIR_BASE_URL} plugin.feasibility.cql.fhiruser=${CQL_FHIR_USER} plugin.feasibility.cql.fhirpw=${CQL_FHIR_PW} +plugin.feasibility.cql.sockettimeout=${CQL_SOCKET_TIMEOUT_MS} plugin.feasibility.flare.url=${FLARE_URL} plugin.feasibility.flare.user=${FLARE_USER} plugin.feasibility.flare.pw=${FLARE_PW} diff --git a/pom.xml b/pom.xml index 5bba687..3a9bd27 100644 --- a/pom.xml +++ b/pom.xml @@ -112,6 +112,18 @@ 2.14.1 + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.14.1 + + + + com.google.guava + guava + 32.1.2-jre + + diff --git a/src/main/java/feasibility/FeasibilityExecutionPlugin.java b/src/main/java/feasibility/FeasibilityExecutionPlugin.java index fe1755f..7e2450a 100644 --- a/src/main/java/feasibility/FeasibilityExecutionPlugin.java +++ b/src/main/java/feasibility/FeasibilityExecutionPlugin.java @@ -48,12 +48,15 @@ protected void loadConfig(Properties properties) throws IOException{ String fhirUserCql = properties.getProperty("plugin.feasibility.cql.fhiruser"); String fhirPwCql = properties.getProperty("plugin.feasibility.cql.fhirpw"); + int socketTimeout = Integer.valueOf(properties.getProperty("plugin.feasibility.cql.sockettimeout")); if(! fhirUserCql.equals("") && ! fhirPwCql.equals("")) { IClientInterceptor authInterceptor = new BasicAuthInterceptor(fhirUserCql, fhirPwCql); genFhirClient.registerInterceptor(authInterceptor); } + fhirContext.getRestfulClientFactory().setSocketTimeout(socketTimeout); + this.cqlExecutor = new CqlExecutor( new FhirConnector(fhirContext.newRestfulGenericClient(fhirBaseUrl)), new FhirHelper(fhirContext) diff --git a/src/main/java/feasibilityValidator/SqValidator.java b/src/main/java/feasibilityValidator/SqValidator.java index 4971aed..777becc 100644 --- a/src/main/java/feasibilityValidator/SqValidator.java +++ b/src/main/java/feasibilityValidator/SqValidator.java @@ -1,6 +1,5 @@ package feasibilityValidator; -import feasibility.CqlExecutor; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; diff --git a/src/main/resources/feasibilityValidator/sq-schema.json b/src/main/resources/feasibilityValidator/sq-schema.json index 9ba3e2d..396c8c3 100644 --- a/src/main/resources/feasibilityValidator/sq-schema.json +++ b/src/main/resources/feasibilityValidator/sq-schema.json @@ -1,167 +1,389 @@ { - "$schema": "http://to_be_decided.com/draft-2/schema#", - + "$schema": "http://medizininformatik-initiative/v3/StructuredQuery/schema#", "definitions": { - "termCode": { "type": "object", "description": "The termCode defines a concept based on a coding system (i.e. LOINC). The triplet of code, system and version identify the concept.", "properties": { - "code": { "type": "string" }, - "system": { "type": "string"}, - "version": { "type": "string"}, - "display": { "type": "string"} + "code": { + "type": "string" + }, + "system": { + "type": "string" + }, + "version": { + "type": "string" + }, + "display": { + "type": "string" + } }, - "required": ["code", "system", "display"], + "required": [ + "code", + "system", + "display" + ] }, - "criterion": { "type": "object", "properties": { + "context": { + "$ref": "#/definitions/termCode" + }, "termCodes": { "type": "array", "minItems": 1, - "items": { "$ref": "#/definitions/termCode" } + "items": { + "$ref": "#/definitions/termCode" + } + }, + "valueFilter": { + "$ref": "#/definitions/valueFilter" }, - "valueFilter": { "$ref": "#/definitions/valueFilter"}, - "attributeFilters": { "$ref": "#/definitions/attributeFilters"}, - "timeRestriction": { "$ref": "#/definitions/timeRestriction"} + "attributeFilters": { + "$ref": "#/definitions/attributeFilters" + }, + "timeRestriction": { + "$ref": "#/definitions/timeRestriction" + } }, - "required": ["termCodes"] + "required": [ + "termCodes", + "context" + ] }, - "timeRestriction": { "type": "object", "description": "TimeRestirction specify the interval within the critiera has to be fullfilled. An intersection of the criterias interval with the interval defined in this timeRestriction is sufficient", "properties": { - "beforeDate": { "type": "string", "format": "date"}, - "afterDate": { "type": "string", "format": "date"} + "beforeDate": { + "type": "string", + "format": "date" + }, + "afterDate": { + "type": "string", + "format": "date" + } }, - "anyOf": [ {"required": ["beforeDate"]}, {"required": ["afterDate"]}] + "anyOf": [ + { + "required": [ + "beforeDate" + ] + }, + { + "required": [ + "afterDate" + ] + } + ] }, - "unit": { "type": "object", "title": "UCUM Unit", "description": "The unit is a ucum unit (https://ucum.org/trac)", "properties": { - "code": { "type": "string" }, - "display": {"type": "string"} + "code": { + "type": "string" + }, + "display": { + "type": "string" + } }, - "required": ["code", "display"] + "required": [ + "code", + "display" + ] }, - "attributeFilters": { "type": "array", - "items": {"$ref": "#/definitions/attributeFilter"} + "items": { + "$ref": "#/definitions/attributeFilter" + } }, - "valueFilter": { "type": "object", "properties": { - "type": { "enum": ["concept", "quantity-comparator", "quantity-range"]} + "type": { + "enum": [ + "concept", + "quantity-comparator", + "quantity-range" + ] + } }, - "required": ["type"], + "required": [ + "type" + ], "allOf": [ { "if": { - "properties": {"type": {"const": "concept"}} + "properties": { + "type": { + "const": "concept" + } + } }, "then": { "properties": { - "type": {"const": "concept"}, + "type": { + "const": "concept" + }, "selectedConcepts": { "type": "array", "minItems": 1, - "items": {"$ref": "#/definitions/termCode"} + "items": { + "$ref": "#/definitions/termCode" + } } }, - "required": ["type", "selectedConcepts"] + "required": [ + "type", + "selectedConcepts" + ] } }, { "if": { - "properties": {"type": {"const": "quantity-comperator"}} + "properties": { + "type": { + "const": "quantity-comperator" + } + } }, "then": { "properties": { - "type": {"const": "quantity-comperator"}, - "comparator": { "enum": ["gt", "ge", "lt", "le", "eq", "ne"] }, - "value": { "type": "number" }, - "unit" : {"$ref": "#/definitions/unit" } + "type": { + "const": "quantity-comperator" + }, + "comparator": { + "enum": [ + "gt", + "ge", + "lt", + "le", + "eq", + "ne" + ] + }, + "value": { + "type": "number" + }, + "unit": { + "$ref": "#/definitions/unit" + } }, - "required": ["type", "comparator", "value"] + "required": [ + "type", + "comparator", + "value" + ] } }, { - "if" : { - "properties": {"type": {"const": "quantity-range"}} + "if": { + "properties": { + "type": { + "const": "quantity-range" + } + } }, - "then" : { + "then": { "properties": { - "type": {"const": "quantity-range"}, - "minValue": { "type": "number" }, - "maxValue": { "type": "number" }, - "unit" : {"$ref": "#/definitions/unit" } + "type": { + "const": "quantity-range" + }, + "minValue": { + "type": "number" + }, + "maxValue": { + "type": "number" + }, + "unit": { + "$ref": "#/definitions/unit" + } }, - "required": ["type", "minValue", "maxValue"] + "required": [ + "type", + "minValue", + "maxValue" + ] } } ] }, - "attributeFilter": { "type": "object", "properties": { - "type": { "enum": ["concept", "quantity-comparator", "quantity-range"]} + "type": { + "enum": [ + "concept", + "quantity-comparator", + "quantity-range", + "reference" + ] + } }, - "required": ["type"], + "required": [ + "type" + ], "allOf": [ { "if": { - "properties": {"type": {"const": "concept"}} + "properties": { + "type": { + "const": "concept" + } + } }, "then": { "properties": { - "attributeCode": {"$ref": "#/definitions/termCode"}, - "type": {"const": "concept"}, + "attributeCode": { + "$ref": "#/definitions/termCode" + }, + "type": { + "const": "concept" + }, "selectedConcepts": { "type": "array", "minItems": 1, - "items": {"$ref": "#/definitions/termCode"} + "items": { + "$ref": "#/definitions/termCode" + } } }, - "required": ["type", "selectedConcepts", "attributeCode"] + "required": [ + "type", + "selectedConcepts", + "attributeCode" + ] } }, { "if": { - "properties": {"type": {"const": "quantity-comperator"}} + "properties": { + "type": { + "const": "quantity-comperator" + } + } }, "then": { "properties": { - "attributeCode": {"$ref": "#/definitions/termCode"}, - "type": {"const": "quantity-comperator"}, - "comparator": { "enum": ["gt", "ge", "lt", "le", "eq", "ne"] }, - "value": { "type": "number" }, - "unit" : {"$ref": "#/definitions/unit" } + "attributeCode": { + "$ref": "#/definitions/termCode" + }, + "type": { + "const": "quantity-comperator" + }, + "comparator": { + "enum": [ + "gt", + "ge", + "lt", + "le", + "eq", + "ne" + ] + }, + "value": { + "type": "number" + }, + "unit": { + "$ref": "#/definitions/unit" + } }, - "required": ["type", "comparator", "value", "attributeCode"] + "required": [ + "type", + "comparator", + "value", + "attributeCode" + ] } }, { - "if" : { - "properties": {"type": {"const": "quantity-range"}} + "if": { + "properties": { + "type": { + "const": "quantity-range" + } + } }, - "then" : { + "then": { "properties": { - "attributeCode": {"$ref": "#/definitions/termCode"}, - "type": {"const": "quantity-range"}, - "minValue": { "type": "number" }, - "maxValue": { "type": "number" }, - "unit" : {"$ref": "#/definitions/unit" } + "attributeCode": { + "$ref": "#/definitions/termCode" + }, + "type": { + "const": "quantity-range" + }, + "minValue": { + "type": "number" + }, + "maxValue": { + "type": "number" + }, + "unit": { + "$ref": "#/definitions/unit" + } }, - "required": ["type", "minValue", "maxValue", "attributeCode"] + "required": [ + "type", + "minValue", + "maxValue", + "attributeCode" + ] + } + }, + { + "if": { + "properties": { + "type": { + "const": "reference" + } + } + }, + "then": { + "properties": { + "attributeCode": { + "$ref": "#/definitions/termCode" + }, + "type": { + "const": "reference" + }, + "criteria": { + "type": "array", + "minItems": 1, + "items": { + "allOf": [ + { + "properties": { + "attributeFilters": { + "type": "array", + "items": { + "$ref": "#/definitions/attributeFilter", + "not": { + "properties": { + "type": { + "const": "reference" + } + } + } + } + } + } + }, + { + "$ref": "#/definitions/criterion" + } + ] + } + } + }, + "required": [ + "type", + "criteria", + "attributeCode" + ] } } ] @@ -172,7 +394,10 @@ "description": "Within a query the inclusion and exclusion criteria are conjuncted with AND NOT", "type": "object", "properties": { - "version": {"type": "string", "format": "uri"}, + "version": { + "type": "string", + "format": "uri" + }, "inclusionCriteria": { "type": "array", "minItems": 1, @@ -181,7 +406,9 @@ "type": "array", "minItems": 1, "description": "All elements within the array are conjuncted with an OR operator", - "items": { "$ref": "#/definitions/criterion" } + "items": { + "$ref": "#/definitions/criterion" + } } }, "exclusionCriteria": { @@ -192,10 +419,17 @@ "type": "array", "minItems": 1, "description": "All elements within the array are conjuncted with an AND operator", - "items": { "$ref": "#/definitions/criterion" } + "items": { + "$ref": "#/definitions/criterion" + } } }, - "display" : {"type": "string"} + "display": { + "type": "string" + } }, - "required": ["version", "inclusionCriteria" ] -} \ No newline at end of file + "required": [ + "version", + "inclusionCriteria" + ] +}v3 \ No newline at end of file diff --git a/src/test/resources/example-sq.json b/src/test/resources/example-sq.json index 6284165..c62cb23 100644 --- a/src/test/resources/example-sq.json +++ b/src/test/resources/example-sq.json @@ -3,19 +3,26 @@ "inclusionCriteria": [ [ { + "attributeFilters": [], "termCodes": [ { "code": "263495000", - "system": "http://snomed.info/sct", - "display": "Geschlecht" + "display": "Geschlecht", + "system": "http://snomed.info/sct" } ], + "context": { + "code": "Patient", + "display": "Patient", + "system": "fdpg.mii.cds", + "version": "1.0.0" + }, "valueFilter": { "selectedConcepts": [ { - "code": "female", - "system": "http://hl7.org/fhir/administrative-gender", - "display": "Female" + "code": "male", + "display": "Male", + "system": "http://hl7.org/fhir/administrative-gender" } ], "type": "concept" diff --git a/src/test/resources/sysproc.properties b/src/test/resources/sysproc.properties index ceb99b1..886568a 100644 --- a/src/test/resources/sysproc.properties +++ b/src/test/resources/sysproc.properties @@ -9,7 +9,7 @@ properties.mapvars=env # e.g. broker.endpoint.uri=http://${HOST}:${PORT}/broker/ -#roker.request.mediatype=text/cql +#broker.request.mediatype=text/cql broker.request.mediatype=application/sq+json broker.result.mediatype=application/json broker.endpoint.uri=http://localhost:8082/broker/ @@ -35,6 +35,7 @@ client.executor.threads=1 plugin.feasibility.cql.fhirbaseurl=http://localhost:8081/fhir plugin.feasibility.cql.fhiruser= plugin.feasibility.cql.fhirpw= +plugin.feasibility.cql.sockettimeout=60000 plugin.feasibility.flare.url=http://localhost:8084/query/execute plugin.feasibility.flare.user= plugin.feasibility.flare.pw=