diff --git a/fhir-model/src/main/java/com/ibm/fhir/model/visitor/CopyingVisitor.java b/fhir-model/src/main/java/com/ibm/fhir/model/visitor/CopyingVisitor.java index 7b400a9bea9..4879b2a65c7 100644 --- a/fhir-model/src/main/java/com/ibm/fhir/model/visitor/CopyingVisitor.java +++ b/fhir-model/src/main/java/com/ibm/fhir/model/visitor/CopyingVisitor.java @@ -20,8 +20,6 @@ import javax.lang.model.SourceVersion; import com.ibm.fhir.model.builder.Builder; -import com.ibm.fhir.model.resource.Bundle; -import com.ibm.fhir.model.resource.Parameters; import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.type.Code; import com.ibm.fhir.model.type.Element; @@ -171,16 +169,14 @@ private void _visitEnd(java.lang.String elementName, int index, Visitable visite Builder parentBuilder = parent.getBuilder(); Object obj = wrapper.getBuilder().build(); - MethodHandle methodHandle; + Class expectedType = ModelSupport.getElementType(parentBuilder.getClass().getEnclosingClass(), elementName); + if (obj != null && !expectedType.isInstance(obj)) { + throw new IllegalStateException("Expected argument of type " + expectedType + " but found " + obj.getClass()); + } + try { - MethodType mt; - if ((visited instanceof Element && ModelSupport.isChoiceElement(parentBuilder.getClass().getEnclosingClass(), elementName)) - || (visited instanceof Resource && isResourceContainer(parentBuilder, elementName))) { - mt = MethodType.methodType(parentBuilder.getClass(), elementOrResource); - } else { - mt = MethodType.methodType(parentBuilder.getClass(), visited.getClass()); - } - methodHandle = MethodHandles.publicLookup().findVirtual(parentBuilder.getClass(), setterName(elementName), mt); + MethodType mt = MethodType.methodType(parentBuilder.getClass(), expectedType); + MethodHandle methodHandle = MethodHandles.publicLookup().findVirtual(parentBuilder.getClass(), setterName(elementName), mt); methodHandle.invoke(parentBuilder, obj); } catch (Throwable t) { throw new RuntimeException("Unexpected error while visiting " + parentBuilder.getClass() + "." + elementName, t); @@ -191,12 +187,6 @@ private void _visitEnd(java.lang.String elementName, int index, Visitable visite } } - private boolean isResourceContainer(Builder parentBuilder, String elementName) { - return (parentBuilder instanceof Bundle.Entry.Builder && "resource".equals(elementName)) || - (parentBuilder instanceof Bundle.Entry.Response.Builder && "outcome".equals(elementName)) || - (parentBuilder instanceof Parameters.Parameter.Builder && "resource".equals(elementName)); - } - /** * Subclasses may override doVisitListStart */ @@ -214,10 +204,16 @@ public void visitEnd(String elementName, List visitables, C doVisitListEnd(elementName, visitables, type); ListWrapper listWrapper = listStack.pop(); if (listWrapper.isDirty()) { + for (Visitable obj : listWrapper.getList()) { + if (!type.isInstance(obj)) { + throw new IllegalStateException("Expected argument of type " + type + " but found " + obj.getClass()); + } + } BuilderWrapper parent = builderStack.peek(); parent.dirty(true); Builder parentBuilder = parent.getBuilder(); MethodHandles.Lookup lookup = MethodHandles.publicLookup(); + MethodType mt = MethodType.methodType(parentBuilder.getClass(), java.util.Collection.class); MethodHandle methodHandle; try { diff --git a/fhir-path/src/main/java/com/ibm/fhir/path/patch/FHIRPathPatch.java b/fhir-path/src/main/java/com/ibm/fhir/path/patch/FHIRPathPatch.java index fc8934306f7..0272f511207 100644 --- a/fhir-path/src/main/java/com/ibm/fhir/path/patch/FHIRPathPatch.java +++ b/fhir-path/src/main/java/com/ibm/fhir/path/patch/FHIRPathPatch.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2020 + * (C) Copyright IBM Corp. 2020, 2021 * * SPDX-License-Identifier: Apache-2.0 */ @@ -26,7 +26,7 @@ public class FHIRPathPatch implements FHIRPatch { private FHIRPathPatch(Builder builder) { this.operations = Collections.unmodifiableList(builder.operations); } - + @Override public T apply(T resource) throws FHIRPatchException { for (FHIRPathPatchOperation fhirPathPatchOperation : operations) { @@ -34,7 +34,7 @@ public T apply(T resource) throws FHIRPatchException { } return resource; } - + /** * Convert the FHIRPathPatch to a FHIR Parameters resource */ @@ -45,22 +45,22 @@ public Parameters toParameters() { } return builder.build(); } - + public Builder toBuilder() { return new Builder().from(this); } - + public static Builder builder() { return new Builder(); } - - public static class Builder { + + public static class Builder { private List operations = new ArrayList<>(3); - + private Builder() { // hidden constructor } - + /** * Add an add operation to the FHIRPathPatch */ @@ -68,7 +68,7 @@ public Builder add(String path, String elementName, Element element) { operations.add(new FHIRPathPatchAdd(path, elementName, element)); return this; } - + /** * Add a delete operation to the FHIRPathPatch */ @@ -76,7 +76,7 @@ public Builder delete(String path) { operations.add(new FHIRPathPatchDelete(path)); return this; } - + /** * Add an insert operation to the FHIRPathPatch */ @@ -84,7 +84,7 @@ public Builder insert(String path, Element element, Integer index) { operations.add(new FHIRPathPatchInsert(path, element, index)); return this; } - + /** * Add a move operation to the FHIRPathPatch */ @@ -92,7 +92,7 @@ public Builder move(String path, Integer source, Integer destination) { operations.add(new FHIRPathPatchMove(path, source, destination)); return this; } - + /** * Add an add operation to the FHIRPathPatch */ @@ -100,7 +100,7 @@ public Builder replace(String path, Element element) { operations.add(new FHIRPathPatchReplace(path, element)); return this; } - + /** * Add all patch operations from the passed FHIRPathPatch */ @@ -108,10 +108,10 @@ public Builder from(FHIRPathPatch patch) { operations.addAll(patch.operations); return this; } - + /** * Build the {@link FHIRPathPatch} - * + * * @return * An immutable object of type {@link FHIRPathPatch} */ @@ -119,10 +119,10 @@ public FHIRPathPatch build() { return new FHIRPathPatch(this); } } - + /** * Parse a FHIRPathPatch from a FHIR Parameters resource - * + * * @throws IllegalArgumentException if the Parameters object does not satisfy the requirements of a FHIRPath Patch */ public static FHIRPathPatch from(Parameters params) { @@ -138,10 +138,10 @@ public static FHIRPathPatch from(Parameters params) { return builder.build(); } - + /** * Parse the passed Parameter and add it to the builder - * + * * @throws IllegalArgumentException if the Parameter object does not represent a valid FHIRPath Patch operation */ private static void addOperation(Builder builder, Parameter operation) { diff --git a/fhir-path/src/main/java/com/ibm/fhir/path/util/AddingVisitor.java b/fhir-path/src/main/java/com/ibm/fhir/path/util/AddingVisitor.java index b8ca43ba39e..1ff11aac37a 100644 --- a/fhir-path/src/main/java/com/ibm/fhir/path/util/AddingVisitor.java +++ b/fhir-path/src/main/java/com/ibm/fhir/path/util/AddingVisitor.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2020 + * (C) Copyright IBM Corp. 2020, 2021 * * SPDX-License-Identifier: Apache-2.0 */ @@ -26,6 +26,7 @@ class AddingVisitor extends CopyingVisitor { * @param parentPath a "simple" FHIRPath path to the parent of the element being added * @param elementName the name of the element to add * @param value the element to add + * @throws IllegalArgumentException */ public AddingVisitor(Visitable parent, String parentPath, String elementName, Visitable value) { this.path = Objects.requireNonNull(parentPath); @@ -34,17 +35,18 @@ public AddingVisitor(Visitable parent, String parentPath, String elementName, Vi this.value = Objects.requireNonNull(value) instanceof Code ? convertToCodeSubtype(parent, elementName, (Code)value) : value; } - + @Override protected void doVisitListEnd(String elementName, List visitables, Class type) { - if (getPath().equals(path) && - type.isAssignableFrom(value.getClass()) && - elementName.equals(this.elementNameToAdd)) { + if (getPath().equals(path) && elementName.equals(this.elementNameToAdd)) { + if (!type.isAssignableFrom(value.getClass())) { + throw new IllegalStateException("target " + type + " is not assignable from " + value.getClass()); + } getList().add(value); markListDirty(); } } - + @Override public boolean visit(String elementName, int index, Visitable value) { if (!isRepeatingElement) { @@ -56,7 +58,7 @@ public boolean visit(String elementName, int index, Visitable value) { } return true; } - + @Override protected void doVisitEnd(String elementName, int elementIndex, Resource resource) { if (!isRepeatingElement) { @@ -65,7 +67,7 @@ protected void doVisitEnd(String elementName, int elementIndex, Resource resourc } } } - + @Override protected void doVisitEnd(String elementName, int elementIndex, Element element) { if (!isRepeatingElement) { diff --git a/fhir-path/src/main/java/com/ibm/fhir/path/util/FHIRPathUtil.java b/fhir-path/src/main/java/com/ibm/fhir/path/util/FHIRPathUtil.java index 4ccac896d1b..61950c5d19e 100644 --- a/fhir-path/src/main/java/com/ibm/fhir/path/util/FHIRPathUtil.java +++ b/fhir-path/src/main/java/com/ibm/fhir/path/util/FHIRPathUtil.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2019, 2020 + * (C) Copyright IBM Corp. 2019, 2021 * * SPDX-License-Identifier: Apache-2.0 */ @@ -865,14 +865,13 @@ public static T add(T elementOrResource, String fhirPath, Visitable parent = node.isResourceNode() ? node.asResourceNode().resource() : node.asElementNode().element(); - AddingVisitor addingVisitor = new AddingVisitor<>(parent, node.path(), elementName, value); - try { + AddingVisitor addingVisitor = new AddingVisitor<>(parent, node.path(), elementName, value); elementOrResource.accept(addingVisitor); - } catch (IllegalStateException e) { + return addingVisitor.getResult(); + } catch (IllegalArgumentException | IllegalStateException e) { throw new FHIRPatchException("An error occurred while adding the value", fhirPath, e); } - return addingVisitor.getResult(); } /** @@ -883,14 +882,14 @@ public static T add(T elementOrResource, String fhirPath, */ public static T delete(T elementOrResource, String fhirPath) throws FHIRPathException, FHIRPatchException { FHIRPathNode node = evaluateToSingle(elementOrResource, fhirPath); - DeletingVisitor deletingVisitor = new DeletingVisitor(node.path()); try { + DeletingVisitor deletingVisitor = new DeletingVisitor(node.path()); elementOrResource.accept(deletingVisitor); - } catch (IllegalStateException e) { + return deletingVisitor.getResult(); + } catch (IllegalArgumentException | IllegalStateException e) { throw new FHIRPatchException("An error occurred while deleting the value", fhirPath, e); } - return deletingVisitor.getResult(); } /** @@ -909,16 +908,22 @@ public static T replace(T elementOrResource, String fhirPa Visitable parent = parentNode.isResourceNode() ? parentNode.asResourceNode().resource() : parentNode.asElementNode().element(); - ReplacingVisitor replacingVisitor = new ReplacingVisitor(parent, elementName, node.path(), value); - try { + ReplacingVisitor replacingVisitor = new ReplacingVisitor(parent, elementName, node.path(), value); elementOrResource.accept(replacingVisitor); - } catch (IllegalStateException e) { + return replacingVisitor.getResult(); + } catch (IllegalArgumentException | IllegalStateException e) { throw new FHIRPatchException("An error occurred while replacing the value", fhirPath, e); } - return replacingVisitor.getResult(); } + /** + * @param elementOrResource + * @param fhirPath + * @return + * @throws FHIRPathException + * @throws FHIRPatchException if the fhirPath does not evaluate to a single node + */ private static FHIRPathNode evaluateToSingle(Visitable elementOrResource, String fhirPath) throws FHIRPathException, FHIRPatchException { /* * 1. The FHIRPath statement must return a single element. @@ -933,6 +938,9 @@ private static FHIRPathNode evaluateToSingle(Visitable elementOrResource, String * 5. Except for the delete operation, it is an error if no element matches the specified path. */ Collection nodes = evaluator.evaluate(elementOrResource, fhirPath); + if (!isSingleton(nodes)) { + throw new FHIRPatchException("Expected a singleton but instead found " + nodes.size() + " nodes", fhirPath); + } return getSingleton(nodes); } @@ -959,14 +967,13 @@ public static T insert(T elementOrResource, String fhirPat Visitable parent = parentNode.isResourceNode() ? parentNode.asResourceNode().resource() : parentNode.asElementNode().element(); - InsertingVisitor insertingVisitor = new InsertingVisitor(parent, parentNode.path(), elementName, index, value); - try { + InsertingVisitor insertingVisitor = new InsertingVisitor(parent, parentNode.path(), elementName, index, value); elementOrResource.accept(insertingVisitor); - } catch (IllegalStateException e) { + return insertingVisitor.getResult(); + } catch (IllegalArgumentException | IllegalStateException e) { throw new FHIRPatchException("An error occurred while inserting the value", fhirPath, e); } - return insertingVisitor.getResult(); } /** @@ -990,14 +997,13 @@ public static T move(T elementOrResource, String fhirPath, FHIRPathTree tree = evaluator.getEvaluationContext().getTree(); FHIRPathNode parent = getCommonParent(fhirPath, nodes, tree); - MovingVisitor movingVisitor = new MovingVisitor(parent.path(), elementName, source, target); - try { + MovingVisitor movingVisitor = new MovingVisitor(parent.path(), elementName, source, target); elementOrResource.accept(movingVisitor); - } catch (IllegalStateException e) { + return movingVisitor.getResult(); + } catch (IllegalArgumentException | IllegalStateException e) { throw new FHIRPatchException("An error occurred while moving the value", fhirPath, e); } - return movingVisitor.getResult(); } /** diff --git a/fhir-path/src/main/java/com/ibm/fhir/path/util/InsertingVisitor.java b/fhir-path/src/main/java/com/ibm/fhir/path/util/InsertingVisitor.java index 3861f72464e..00cc51f289b 100644 --- a/fhir-path/src/main/java/com/ibm/fhir/path/util/InsertingVisitor.java +++ b/fhir-path/src/main/java/com/ibm/fhir/path/util/InsertingVisitor.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2020 + * (C) Copyright IBM Corp. 2020, 2021 * * SPDX-License-Identifier: Apache-2.0 */ @@ -23,6 +23,7 @@ class InsertingVisitor extends CopyingVisitor { * @param elementName * @param index * @param value + * @throws IllegalArgumentException */ public InsertingVisitor(Visitable parent, String parentPath, String elementName, int index, Visitable value) { this.parentPath = Objects.requireNonNull(parentPath); @@ -35,6 +36,9 @@ public InsertingVisitor(Visitable parent, String parentPath, String elementName, @Override protected void doVisitListEnd(String elementName, List visitables, Class type) { if (getPath().equals(parentPath) && elementName.equals(this.elementNameToInsert)) { + if (!type.isInstance(value)) { + throw new IllegalStateException("target " + type + " is not assignable from " + value.getClass()); + } getList().add(index, value); markListDirty(); } diff --git a/fhir-path/src/main/java/com/ibm/fhir/path/util/ReplacingVisitor.java b/fhir-path/src/main/java/com/ibm/fhir/path/util/ReplacingVisitor.java index a5ac5196039..ee818794bc5 100644 --- a/fhir-path/src/main/java/com/ibm/fhir/path/util/ReplacingVisitor.java +++ b/fhir-path/src/main/java/com/ibm/fhir/path/util/ReplacingVisitor.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2020 + * (C) Copyright IBM Corp. 2020, 2021 * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,6 +17,13 @@ class ReplacingVisitor extends CopyingVisitor { private String pathToReplace; private Visitable newValue; + /** + * @param parent + * @param elementName + * @param pathToReplace + * @param newValue + * @throws IllegalArgumentException + */ public ReplacingVisitor(Visitable parent, String elementName, String pathToReplace, Visitable newValue) { this.pathToReplace = Objects.requireNonNull(pathToReplace); this.newValue = Objects.requireNonNull(newValue) instanceof Code ? diff --git a/fhir-path/src/test/java/com/ibm/fhir/path/patch/test/FHIRPathPatchBuilderTest.java b/fhir-path/src/test/java/com/ibm/fhir/path/patch/test/FHIRPathPatchBuilderTest.java index e3688f4d2ee..ecfb1936a14 100644 --- a/fhir-path/src/test/java/com/ibm/fhir/path/patch/test/FHIRPathPatchBuilderTest.java +++ b/fhir-path/src/test/java/com/ibm/fhir/path/patch/test/FHIRPathPatchBuilderTest.java @@ -1,6 +1,6 @@ /* - * (C) Copyright IBM Corp. 2019, 2020 - * + * (C) Copyright IBM Corp. 2019, 2021 + * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,43 +17,152 @@ import com.ibm.fhir.model.patch.exception.FHIRPatchException; import com.ibm.fhir.model.resource.Patient; +import com.ibm.fhir.model.resource.Patient.Communication; +import com.ibm.fhir.model.resource.Patient.Contact; +import com.ibm.fhir.model.type.Code; +import com.ibm.fhir.model.type.CodeableConcept; +import com.ibm.fhir.model.type.Coding; +import com.ibm.fhir.model.type.Date; +import com.ibm.fhir.model.type.DateTime; import com.ibm.fhir.model.type.Extension; import com.ibm.fhir.model.type.HumanName; import com.ibm.fhir.model.type.Identifier; import com.ibm.fhir.model.type.Time; import com.ibm.fhir.model.type.Uri; +import com.ibm.fhir.model.type.code.AccountStatus; +import com.ibm.fhir.model.type.code.AdministrativeGender; +import com.ibm.fhir.model.type.code.DataAbsentReason; import com.ibm.fhir.path.patch.FHIRPathPatch; public class FHIRPathPatchBuilderTest { @Test private void patchBuilderTestIncremental() throws Exception { Patient patient = Patient.builder().id("test").build(); - + Patient patchedPatient = buildAdd().apply(patient); patient = addViaBuilder(patient); assertEquals(patchedPatient, patient); - + patchedPatient = buildDelete().apply(patient); patient = deleteViaBuilder(patient); assertEquals(patchedPatient, patient); - + patchedPatient = buildInsert().apply(patient); patient = insertViaBuilder(patient); assertEquals(patchedPatient, patient); - + patchedPatient = buildMove().apply(patient); patient = moveViaBuilder(patient); assertEquals(patchedPatient, patient); - + patchedPatient = buildReplace().apply(patient); patient = replaceViaBuilder(patient); assertEquals(patchedPatient, patient); } - + + @Test(expectedExceptions = FHIRPatchException.class) + private void patchBuilderTestBadAddList() throws Exception { + Patient patient = Patient.builder().id("test").build(); + Patient modifiedPatient = FHIRPathPatch.builder() + .add("Patient", "communication", Contact.builder().name(HumanName.builder().family(string("Last")).build()).build()) + .build() + .apply(patient); + System.out.println(modifiedPatient); + } + + @Test(expectedExceptions = FHIRPatchException.class) + private void patchBuilderTestBadAddSingle() throws Exception { + Patient patient = Patient.builder().id("test").build(); + Patient modifiedPatient = FHIRPathPatch.builder() + .add("Patient", "birthDate", DateTime.now()) + .build() + .apply(patient); + System.out.println(modifiedPatient); + } + + @Test(expectedExceptions = FHIRPatchException.class) + private void patchBuilderTestBadAddSingleCode() throws Exception { + Patient patient = Patient.builder().id("test").build(); + Patient modifiedPatient = FHIRPathPatch.builder() + .add("Patient", "active", AccountStatus.UNKNOWN) + .build() + .apply(patient); + System.out.println(modifiedPatient); + } + + @Test(expectedExceptions = FHIRPatchException.class) + private void patchBuilderTestBadAddDeepCode() throws Exception { + Patient patient = Patient.builder().id("test").build(); + Patient modifiedPatient = FHIRPathPatch.builder() + .add("Patient", "contact", Contact.builder() + .name(HumanName.builder().family(string("Last")).build()) + .build()) + .add("Patient.contact", "gender", DataAbsentReason.MASKED) + .build() + .apply(patient); + System.out.println(modifiedPatient); + } + + @Test(expectedExceptions = FHIRPatchException.class) + private void patchBuilderTestBadInsert() throws Exception { + Patient patient = Patient.builder().id("test").build(); + Patient modifiedPatient = FHIRPathPatch.builder() + .add("Patient", "communication", Communication.builder() + .language(CodeableConcept.builder() + .coding(Coding.builder() + .system(Uri.of("urn:ietf:bcp:47")) + .code(Code.of("en")) + .build()) + .build()) + .build()) + .insert("Patient.communication", Contact.builder().name(HumanName.builder().family(string("Last")).build()).build(), 1) + .build() + .apply(patient); + System.out.println(modifiedPatient); + } + + @Test(expectedExceptions = FHIRPatchException.class) + private void patchBuilderTestBadReplaceList() throws Exception { + Patient patient = Patient.builder().id("test").build(); + Patient modifiedPatient = FHIRPathPatch.builder() + .add("Patient", "communication", Communication.builder() + .language(CodeableConcept.builder() + .coding(Coding.builder() + .system(Uri.of("urn:ietf:bcp:47")) + .code(Code.of("en")) + .build()) + .build()) + .build()) + .replace("Patient.communication[0]", Contact.builder().name(HumanName.builder().family(string("Last")).build()).build()) + .build() + .apply(patient); + System.out.println(modifiedPatient); + } + + @Test(expectedExceptions = FHIRPatchException.class) + private void patchBuilderTestBadReplaceSingle() throws Exception { + Patient patient = Patient.builder().id("test").build(); + FHIRPathPatch.builder() + .add("Patient", "birthDate", Date.of("2021")) + .replace("Patient.birthDate", DateTime.now()) + .build() + .apply(patient); + } + + @Test(expectedExceptions = FHIRPatchException.class) + private void patchBuilderTestBadReplaceSingleCode() throws Exception { + Patient patient = Patient.builder().id("test").build(); + FHIRPathPatch.builder() + .add("Patient", "active", com.ibm.fhir.model.type.Boolean.TRUE) + .replace("Patient.active", Code.of("false")) + .build() + .apply(patient); + } + @Test private void patchBuilderTestAll() throws Exception { Patient patient = Patient.builder().id("test").build(); - + FHIRPathPatch allPatch = FHIRPathPatch.builder() .from(buildAdd()) .from(buildDelete()) @@ -61,15 +170,15 @@ private void patchBuilderTestAll() throws Exception { .from(buildMove()) .from(buildReplace()) .build(); - + Patient patchedPatient = allPatch.apply(patient); - + patient = addViaBuilder(patient); patient = deleteViaBuilder(patient); patient = insertViaBuilder(patient); patient = moveViaBuilder(patient); patient = replaceViaBuilder(patient); - + assertEquals(patchedPatient, patient); } @@ -91,6 +200,10 @@ private Patient addViaBuilder(Patient patient) { .build()) .build(), HumanName.builder().family(string("Last")).build()) + .contact(Contact.builder() + .name(HumanName.builder().family(string("Last")).build()) + .gender(AdministrativeGender.FEMALE) + .build()) .active(com.ibm.fhir.model.type.Boolean.TRUE) .build(); } @@ -101,6 +214,8 @@ private FHIRPathPatch buildAdd() throws FHIRPatchException { .add("Patient.identifier", "value", string("it-me")) .add("Patient", "name", HumanName.builder().family(string("Last")).build()) .add("Patient", "name", HumanName.builder().family(string("Last")).build()) + .add("Patient", "contact", Contact.builder().name(HumanName.builder().family(string("Last")).build()).build()) + .add("Patient.contact", "gender", AdministrativeGender.FEMALE) .add("Patient.name[0]", "given", string("First")) .add("Patient.name[0]", "extension", Extension.builder().url("myExtension").build()) .add("Patient.name[0].extension", "extension", Extension.builder().url("lunchTime").build()) @@ -108,7 +223,7 @@ private FHIRPathPatch buildAdd() throws FHIRPatchException { .add("Patient", "active", com.ibm.fhir.model.type.Boolean.TRUE) .build(); } - + private Patient deleteViaBuilder(Patient patient) { return patient.toBuilder() .identifier(Collections.emptySet()) @@ -126,11 +241,11 @@ private FHIRPathPatch buildDelete() throws FHIRPatchException { private Patient insertViaBuilder(Patient patient) { List names = new ArrayList<>(patient.getName()); names.add(2, HumanName.builder().family(string("Inserted")).build()); - + List name1extensions = new ArrayList<>(names.get(0).getExtension()); name1extensions.add(0, Extension.builder().url("inserted").value(string("value")).build()); names.set(0, names.get(0).toBuilder().extension(name1extensions).build()); - + return patient.toBuilder() .name(names) .build(); @@ -139,7 +254,7 @@ private Patient insertViaBuilder(Patient patient) { private FHIRPathPatch buildInsert() throws FHIRPatchException { return FHIRPathPatch.builder() .insert("Patient.name", HumanName.builder().family(string("Inserted")).build(), 2) - .insert("Patient.name[0].extension", + .insert("Patient.name[0].extension", Extension.builder().url("inserted").value(string("value")).build(), 0) .build(); } diff --git a/fhir-path/src/test/java/com/ibm/fhir/path/patch/test/FHIRPathUtilTest.java b/fhir-path/src/test/java/com/ibm/fhir/path/patch/test/FHIRPathUtilTest.java new file mode 100644 index 00000000000..c620ae41f96 --- /dev/null +++ b/fhir-path/src/test/java/com/ibm/fhir/path/patch/test/FHIRPathUtilTest.java @@ -0,0 +1,48 @@ +/* + * (C) Copyright IBM Corp. 2021 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package com.ibm.fhir.path.patch.test; + +import static org.testng.Assert.assertEquals; + +import org.testng.annotations.Test; + +import com.ibm.fhir.model.patch.exception.FHIRPatchException; +import com.ibm.fhir.model.resource.Bundle; +import com.ibm.fhir.model.resource.Bundle.Entry; +import com.ibm.fhir.model.resource.Patient; +import com.ibm.fhir.model.resource.Practitioner; +import com.ibm.fhir.model.type.Uri; +import com.ibm.fhir.model.type.code.BundleType; +import com.ibm.fhir.path.exception.FHIRPathException; +import com.ibm.fhir.path.util.FHIRPathUtil; + +/** + * Tests against the FHIRPathPatch helper methods in FHIRPathUtil + */ +public class FHIRPathUtilTest { + Bundle bundle = Bundle.builder().type(BundleType.COLLECTION).build(); + Patient patient = Patient.builder().id("test").build(); + Practitioner practitioner = Practitioner.builder().id("test").build(); + + @Test + private void testAddListResource() throws FHIRPathException, FHIRPatchException { + Patient modifiedPatient = FHIRPathUtil.add(patient, "Patient", "contained", practitioner); + assertEquals(modifiedPatient.getContained().get(0), practitioner); + } + + @Test + private void testAddSingleResource() throws FHIRPathException, FHIRPatchException { + Entry emptyEntry = Entry.builder().fullUrl(Uri.of("test")).build(); + + Bundle modifiedBundle = FHIRPathUtil.add(bundle, "Bundle", "entry", emptyEntry); + modifiedBundle = FHIRPathUtil.add(modifiedBundle, "Bundle", "entry", emptyEntry); + modifiedBundle = FHIRPathUtil.add(modifiedBundle, "Bundle.entry[0]", "resource", patient); + modifiedBundle = FHIRPathUtil.add(modifiedBundle, "Bundle.entry[1]", "resource", practitioner); + + assertEquals(modifiedBundle.getEntry().get(0).getResource(), patient); + assertEquals(modifiedBundle.getEntry().get(1).getResource(), practitioner); + } +}