From 5937efd8fa676f99c1d2a7175ee9c8e6b19c5669 Mon Sep 17 00:00:00 2001 From: Dmytro Rud Date: Thu, 4 Jul 2024 09:23:46 +0200 Subject: [PATCH] #451: Fixed translation of FilterSets between DSMLv2 and JSON --- .../stub/json/FilterIntermediaryUtils.groovy | 5 +- .../json/SearchRequestIntermediary.groovy | 4 + .../ihe/hpd/stub/dsmlv2/DsmlMessage.java | 6 ++ .../stub/json/ControlListDeserializer.java | 76 ++++++++++++++++ .../hpd/stub/json/ControlListSerializer.java | 86 +++++++++++++++++++ .../commons/ihe/hpd/stub/json/JsonTest.groovy | 14 +++ 6 files changed, 188 insertions(+), 3 deletions(-) create mode 100644 commons/ihe/hpd/src/main/java/org/openehealth/ipf/commons/ihe/hpd/stub/json/ControlListDeserializer.java create mode 100644 commons/ihe/hpd/src/main/java/org/openehealth/ipf/commons/ihe/hpd/stub/json/ControlListSerializer.java diff --git a/commons/ihe/hpd/src/main/groovy/org/openehealth/ipf/commons/ihe/hpd/stub/json/FilterIntermediaryUtils.groovy b/commons/ihe/hpd/src/main/groovy/org/openehealth/ipf/commons/ihe/hpd/stub/json/FilterIntermediaryUtils.groovy index b30cb480af..1aeaf258cf 100644 --- a/commons/ihe/hpd/src/main/groovy/org/openehealth/ipf/commons/ihe/hpd/stub/json/FilterIntermediaryUtils.groovy +++ b/commons/ihe/hpd/src/main/groovy/org/openehealth/ipf/commons/ihe/hpd/stub/json/FilterIntermediaryUtils.groovy @@ -15,12 +15,11 @@ */ package org.openehealth.ipf.commons.ihe.hpd.stub.json +import jakarta.xml.bind.JAXBElement import org.openehealth.ipf.commons.ihe.hpd.stub.dsmlv2.Filter import org.openehealth.ipf.commons.ihe.hpd.stub.dsmlv2.FilterSet import org.openehealth.ipf.commons.ihe.hpd.stub.dsmlv2.ObjectFactory -import javax.xml.bind.JAXBElement - /** * @author Dmytro Rud */ @@ -31,7 +30,7 @@ class FilterIntermediaryUtils { static final List FILTER_TYPES = ['and', 'or', 'not', 'equalityMatch', 'substring', 'greaterOrEqual', 'lessOrEqual', 'present', 'approxMatch', 'extensibleMatch'] static FilterIntermediary fromDsml(String filterType, Object dsml) { - return Class.forName("${FilterIntermediaryUtils.packageName + '.' + filterType.capitalize()}").declaredMethods.find { it.name == 'fromDsml' }.invoke(null, dsml) as FilterIntermediary + return Class.forName(FilterIntermediaryUtils.packageName + '.' + filterType.capitalize()).declaredMethods.find { it.name == 'fromDsml' }.invoke(null, dsml) as FilterIntermediary } static FilterIntermediary fromFilter(Filter f) { diff --git a/commons/ihe/hpd/src/main/groovy/org/openehealth/ipf/commons/ihe/hpd/stub/json/SearchRequestIntermediary.groovy b/commons/ihe/hpd/src/main/groovy/org/openehealth/ipf/commons/ihe/hpd/stub/json/SearchRequestIntermediary.groovy index 066c5ba391..35a1cd32f6 100644 --- a/commons/ihe/hpd/src/main/groovy/org/openehealth/ipf/commons/ihe/hpd/stub/json/SearchRequestIntermediary.groovy +++ b/commons/ihe/hpd/src/main/groovy/org/openehealth/ipf/commons/ihe/hpd/stub/json/SearchRequestIntermediary.groovy @@ -15,6 +15,8 @@ */ package org.openehealth.ipf.commons.ihe.hpd.stub.json +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import com.fasterxml.jackson.databind.annotation.JsonSerialize import groovy.transform.CompileStatic import org.openehealth.ipf.commons.ihe.hpd.stub.dsmlv2.* @@ -25,6 +27,8 @@ import org.openehealth.ipf.commons.ihe.hpd.stub.dsmlv2.* class SearchRequestIntermediary { String requestId + @JsonSerialize(using = ControlListSerializer.class) + @JsonDeserialize(using = ControlListDeserializer.class) List controls FilterIntermediary filter List attributes diff --git a/commons/ihe/hpd/src/main/java/org/openehealth/ipf/commons/ihe/hpd/stub/dsmlv2/DsmlMessage.java b/commons/ihe/hpd/src/main/java/org/openehealth/ipf/commons/ihe/hpd/stub/dsmlv2/DsmlMessage.java index e3f59bb371..5374081a92 100644 --- a/commons/ihe/hpd/src/main/java/org/openehealth/ipf/commons/ihe/hpd/stub/dsmlv2/DsmlMessage.java +++ b/commons/ihe/hpd/src/main/java/org/openehealth/ipf/commons/ihe/hpd/stub/dsmlv2/DsmlMessage.java @@ -16,6 +16,10 @@ package org.openehealth.ipf.commons.ihe.hpd.stub.dsmlv2; import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.openehealth.ipf.commons.ihe.hpd.stub.json.ControlListDeserializer; +import org.openehealth.ipf.commons.ihe.hpd.stub.json.ControlListSerializer; import java.util.ArrayList; import java.util.List; @@ -67,6 +71,8 @@ @JsonTypeInfo(use = JsonTypeInfo.Id.MINIMAL_CLASS) public class DsmlMessage { + @JsonSerialize(using = ControlListSerializer.class) + @JsonDeserialize(using = ControlListDeserializer.class) protected List control; @XmlAttribute(name = "requestID") protected String requestID; diff --git a/commons/ihe/hpd/src/main/java/org/openehealth/ipf/commons/ihe/hpd/stub/json/ControlListDeserializer.java b/commons/ihe/hpd/src/main/java/org/openehealth/ipf/commons/ihe/hpd/stub/json/ControlListDeserializer.java new file mode 100644 index 0000000000..5621f3c476 --- /dev/null +++ b/commons/ihe/hpd/src/main/java/org/openehealth/ipf/commons/ihe/hpd/stub/json/ControlListDeserializer.java @@ -0,0 +1,76 @@ +/* + * Copyright 2024 the original author or authors. + * + * 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openehealth.ipf.commons.ihe.hpd.stub.json; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import org.apache.commons.lang3.NotImplementedException; +import org.openehealth.ipf.commons.ihe.hpd.controls.ControlUtils; +import org.openehealth.ipf.commons.ihe.hpd.controls.sorting.SortControl2; +import org.openehealth.ipf.commons.ihe.hpd.controls.sorting.SortResponseControl2; +import org.openehealth.ipf.commons.ihe.hpd.stub.dsmlv2.Control; + +import javax.naming.ldap.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * JSON deserializer for {@link List}<{@link Control}>. + * + * @author Dmytro Rud + */ +public class ControlListDeserializer extends JsonDeserializer { + + @Override + public List deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException { + JsonNode arrayNode = jsonParser.readValueAsTree(); + if (!arrayNode.isArray()) { + throw new IllegalArgumentException("'controls' shall be a JSON array"); + } + List result = new ArrayList<>(); + Iterator controlNodes = arrayNode.elements(); + while (controlNodes.hasNext()) { + result.add(ControlUtils.toDsmlv2(deserializeControl(controlNodes.next()))); + } + return result; + } + + private static BasicControl deserializeControl(JsonNode node) throws IOException { + switch (node.get("type").textValue()) { + case PagedResultsControl.OID: + return new PagedResultsControl(node.get("size").intValue(), node.get("cookie").binaryValue(), node.get("critical").booleanValue()); + case SortControl.OID: + ArrayList keys = new ArrayList<>(); + JsonNode keysNode = node.get("keys"); + Iterator keyNodes = keysNode.elements(); + while (keyNodes.hasNext()) { + JsonNode keyNode = keyNodes.next(); + keys.add(new SortKey(keyNode.get("attrId").textValue(), keyNode.get("ascending").booleanValue(), keyNode.get("matchingRuleId").textValue())); + } + return new SortControl2(node.get("critical").asBoolean(), keys.toArray(new SortKey[0])); + case SortResponseControl.OID: + return new SortResponseControl2(node.get("resultCode").intValue(), node.get("failedAttrId").asText(), node.get("critical").booleanValue()); + default: + throw new NotImplementedException("Cannot handle control type " + node.get("type").asText()); + } + } + +} diff --git a/commons/ihe/hpd/src/main/java/org/openehealth/ipf/commons/ihe/hpd/stub/json/ControlListSerializer.java b/commons/ihe/hpd/src/main/java/org/openehealth/ipf/commons/ihe/hpd/stub/json/ControlListSerializer.java new file mode 100644 index 0000000000..01e89ddfbe --- /dev/null +++ b/commons/ihe/hpd/src/main/java/org/openehealth/ipf/commons/ihe/hpd/stub/json/ControlListSerializer.java @@ -0,0 +1,86 @@ +/* + * Copyright 2024 the original author or authors. + * + * 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 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openehealth.ipf.commons.ihe.hpd.stub.json; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; +import org.apache.commons.lang3.NotImplementedException; +import org.openehealth.ipf.commons.ihe.hpd.controls.ControlUtils; +import org.openehealth.ipf.commons.ihe.hpd.controls.sorting.SortControl2; +import org.openehealth.ipf.commons.ihe.hpd.controls.sorting.SortResponseControl2; +import org.openehealth.ipf.commons.ihe.hpd.stub.dsmlv2.Control; + +import javax.naming.ldap.*; +import java.io.IOException; +import java.util.List; + +/** + * JSON serializer for {@link List}<{@link Control}>. + * + * @author Dmytro Rud + */ +public class ControlListSerializer extends StdSerializer { + + public ControlListSerializer() { + super(List.class); + } + + @Override + public void serialize(List list, JsonGenerator gen, SerializerProvider provider) throws IOException { + gen.writeStartArray(list); + for (Object object : list) { + Control dsmlcontrol = (Control) object; + BasicControl control = ControlUtils.extractControl((byte[]) dsmlcontrol.getControlValue(), dsmlcontrol.getType(), dsmlcontrol.isCriticality()); + gen.writeStartObject(); + gen.writeStringField("type", control.getID()); + gen.writeBooleanField("critical", control.isCritical()); + switch (dsmlcontrol.getType()) { + case PagedResultsControl.OID: + PagedResultsResponseControl paginationControl = (PagedResultsResponseControl) control; + gen.writeNumberField("size", paginationControl.getResultSize()); + if (paginationControl.getCookie() != null) { + gen.writeBinaryField("cookie", paginationControl.getCookie()); + } + break; + case SortControl.OID: + SortControl2 sortControl = (SortControl2) control; + gen.writeArrayFieldStart("keys"); + for (SortKey sortKey : sortControl.getKeys()) { + gen.writeStartObject(); + gen.writeStringField("attrId", sortKey.getAttributeID()); + gen.writeStringField("matchingRuleId", sortKey.getMatchingRuleID()); + gen.writeBooleanField("ascending", sortKey.isAscending()); + gen.writeEndObject(); + } + gen.writeEndArray(); + break; + case SortResponseControl.OID: + SortResponseControl2 sortResponseControl = (SortResponseControl2) control; + gen.writeNumberField("resultCode", sortResponseControl.getResultCode()); + if (sortResponseControl.getFailedAttributeName() != null) { + gen.writeStringField("failedAttrId", sortResponseControl.getFailedAttributeName()); + } + break; + default: + throw new NotImplementedException("Cannot handle control type " + dsmlcontrol.getType()); + } + gen.writeEndObject(); + } + gen.writeEndArray(); + } + +} diff --git a/commons/ihe/hpd/src/test/groovy/org/openehealth/ipf/commons/ihe/hpd/stub/json/JsonTest.groovy b/commons/ihe/hpd/src/test/groovy/org/openehealth/ipf/commons/ihe/hpd/stub/json/JsonTest.groovy index 7a66359e71..f99c1ee993 100644 --- a/commons/ihe/hpd/src/test/groovy/org/openehealth/ipf/commons/ihe/hpd/stub/json/JsonTest.groovy +++ b/commons/ihe/hpd/src/test/groovy/org/openehealth/ipf/commons/ihe/hpd/stub/json/JsonTest.groovy @@ -22,11 +22,17 @@ import groovy.util.logging.Slf4j import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test import org.openehealth.ipf.commons.ihe.hpd.HpdValidator +import org.openehealth.ipf.commons.ihe.hpd.controls.ControlUtils +import org.openehealth.ipf.commons.ihe.hpd.controls.sorting.SortControl2 +import org.openehealth.ipf.commons.ihe.hpd.controls.sorting.SortResponseControl2 import org.openehealth.ipf.commons.ihe.hpd.stub.dsmlv2.* import org.openehealth.ipf.commons.xml.XmlUtils import jakarta.xml.bind.JAXBElement +import javax.naming.ldap.PagedResultsControl +import javax.naming.ldap.SortKey + @Slf4j class JsonTest { @@ -53,6 +59,14 @@ class JsonTest { dn: 'ou=HPDElectronicService', scope: SearchRequest.SearchScope.WHOLE_SUBTREE, derefAliases: SearchRequest.DerefAliasesType.NEVER_DEREF_ALIASES, + control: [ + ControlUtils.toDsmlv2(new SortControl2(true, [ + new SortKey('attr1', true, 'alg1'), + new SortKey('attr2', false, 'alg2'), + ] as SortKey[])), + ControlUtils.toDsmlv2(new SortResponseControl2(42, 'attr3', true)), + ControlUtils.toDsmlv2(new PagedResultsControl(100, 'cookie'.bytes, false)), + ], filter: new Filter( equalityMatch: new AttributeValueAssertion(name: 'hcIdentifier', value: '1234567') ),