Make patch `add` actually add instead of replace. (#3800)
* Fix some patch problems * Changelog
This commit is contained in:
parent
e0169fa6c7
commit
ab4d9578e7
|
@ -49,7 +49,6 @@ import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
|
||||||
public class RequestPartitionId implements IModelJson {
|
public class RequestPartitionId implements IModelJson {
|
||||||
private static final RequestPartitionId ALL_PARTITIONS = new RequestPartitionId();
|
private static final RequestPartitionId ALL_PARTITIONS = new RequestPartitionId();
|
||||||
private static final ObjectMapper ourObjectMapper = new ObjectMapper().registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
|
private static final ObjectMapper ourObjectMapper = new ObjectMapper().registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
|
||||||
|
|
||||||
@JsonProperty("partitionDate")
|
@JsonProperty("partitionDate")
|
||||||
private final LocalDate myPartitionDate;
|
private final LocalDate myPartitionDate;
|
||||||
@JsonProperty("allPartitions")
|
@JsonProperty("allPartitions")
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
type: fix
|
||||||
|
issue: 3796
|
||||||
|
jira: SMILE-4328
|
||||||
|
title: "Previously, the FHIRPath PATCH operation type `add` was replacing the entire element as opposed to adding the new element to the existing element. This has been corrected."
|
|
@ -6,20 +6,26 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
import org.hl7.fhir.r4.model.CodeType;
|
import org.hl7.fhir.r4.model.CodeType;
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.hl7.fhir.r4.model.Identifier;
|
||||||
import org.hl7.fhir.r4.model.IntegerType;
|
import org.hl7.fhir.r4.model.IntegerType;
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.Patient;
|
import org.hl7.fhir.r4.model.Patient;
|
||||||
import org.hl7.fhir.r4.model.Questionnaire;
|
import org.hl7.fhir.r4.model.Questionnaire;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.hl7.fhir.r4.model.Type;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
public class FhirPatchApplyR4Test {
|
public class FhirPatchApplyR4Test {
|
||||||
|
@ -218,6 +224,7 @@ public class FhirPatchApplyR4Test {
|
||||||
.addPart()
|
.addPart()
|
||||||
.setName("value")
|
.setName("value")
|
||||||
.setValue(new StringType("2"));
|
.setValue(new StringType("2"));
|
||||||
|
|
||||||
operation = patch.addParameter();
|
operation = patch.addParameter();
|
||||||
operation.setName("operation");
|
operation.setName("operation");
|
||||||
operation
|
operation
|
||||||
|
@ -236,6 +243,7 @@ public class FhirPatchApplyR4Test {
|
||||||
.addPart()
|
.addPart()
|
||||||
.setName("value")
|
.setName("value")
|
||||||
.setValue(new Coding("http://smilecdr.com/fhir/document-type", "ADMIN", null));
|
.setValue(new Coding("http://smilecdr.com/fhir/document-type", "ADMIN", null));
|
||||||
|
|
||||||
operation = patch.addParameter();
|
operation = patch.addParameter();
|
||||||
operation.setName("operation");
|
operation.setName("operation");
|
||||||
operation
|
operation
|
||||||
|
@ -259,8 +267,135 @@ public class FhirPatchApplyR4Test {
|
||||||
|
|
||||||
ourLog.info("Outcome:\n{}", ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
|
ourLog.info("Outcome:\n{}", ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
|
||||||
assertEquals("{\"resourceType\":\"Questionnaire\",\"item\":[{\"linkId\":\"1\",\"code\":[{\"system\":\"https://smilecdr.com/fhir/document-type\",\"code\":\"CLINICAL\"}],\"text\":\"Test item\"},{\"linkId\":\"2\",\"code\":[{\"system\":\"http://smilecdr.com/fhir/document-type\",\"code\":\"ADMIN\"}],\"text\":\"Test Item 2\"}]}", ourCtx.newJsonParser().encodeResourceToString(patient));
|
assertEquals("{\"resourceType\":\"Questionnaire\",\"item\":[{\"linkId\":\"1\",\"code\":[{\"system\":\"https://smilecdr.com/fhir/document-type\",\"code\":\"CLINICAL\"}],\"text\":\"Test item\"},{\"linkId\":\"2\",\"code\":[{\"system\":\"http://smilecdr.com/fhir/document-type\",\"code\":\"ADMIN\"}],\"text\":\"Test Item 2\"}]}", ourCtx.newJsonParser().encodeResourceToString(patient));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddDoesNotReplaceHighCardinalityItems() {
|
||||||
|
FhirPatch svc = new FhirPatch(ourCtx);
|
||||||
|
|
||||||
|
//Given: We create a patient with an identifier
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setSystem("first-system").setValue("first-value");
|
||||||
|
|
||||||
|
//Given: We create a patch request to add a second identifier.
|
||||||
|
Identifier theValue = new Identifier().setSystem("second-system").setValue("second-value");
|
||||||
|
Parameters patch = new Parameters();
|
||||||
|
patch.addParameter(createPatchAddOperation( "Patient", "identifier", theValue));
|
||||||
|
|
||||||
|
//When: We apply the patch
|
||||||
|
svc.apply(patient, patch);
|
||||||
|
ourLog.info("Outcome:\n{}", ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
|
||||||
|
|
||||||
|
//Then: New identifier is added, and does not overwrite.
|
||||||
|
assertThat(patient.getIdentifier(), hasSize(2));
|
||||||
|
assertThat(patient.getIdentifier().get(0).getSystem(), is(equalTo("first-system")));
|
||||||
|
assertThat(patient.getIdentifier().get(0).getValue(), is(equalTo("first-value")));
|
||||||
|
assertThat(patient.getIdentifier().get(1).getSystem(), is(equalTo("second-system")));
|
||||||
|
assertThat(patient.getIdentifier().get(1).getValue(), is(equalTo("second-value")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAddToHighCardinalityFieldSetsValueIfEmpty() {
|
||||||
|
FhirPatch svc = new FhirPatch(ourCtx);
|
||||||
|
Patient patient = new Patient();
|
||||||
|
|
||||||
|
//Given: We create a patch request to add an identifier.
|
||||||
|
Parameters patch = new Parameters();
|
||||||
|
Identifier theValue = new Identifier().setSystem("third-system").setValue("third-value");
|
||||||
|
patch.addParameter(createPatchAddOperation("Patient", "identifier", theValue));
|
||||||
|
|
||||||
|
//When: We apply the patch
|
||||||
|
svc.apply(patient, patch);
|
||||||
|
ourLog.info("Outcome:\n{}", ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
|
||||||
|
|
||||||
|
//Then: it applies the new identifier correctly.
|
||||||
|
assertThat(patient.getIdentifier(), hasSize(1));
|
||||||
|
assertThat(patient.getIdentifier().get(0).getSystem(), is(equalTo("third-system")));
|
||||||
|
assertThat(patient.getIdentifier().get(0).getValue(), is(equalTo("third-value")));
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testReplaceToHighCardinalityFieldRemovesAllAndSetsValue() {
|
||||||
|
FhirPatch svc = new FhirPatch(ourCtx);
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.addIdentifier().setSystem("first-system").setValue("first-value");
|
||||||
|
patient.addIdentifier().setSystem("second-system").setValue("second-value");
|
||||||
|
|
||||||
|
//Given: We create a patch request to add an identifier.
|
||||||
|
Identifier theValue = new Identifier().setSystem("third-system").setValue("third-value");
|
||||||
|
Parameters patch = new Parameters();
|
||||||
|
patch.addParameter(createPatchReplaceOperation("Patient.identifier", theValue));
|
||||||
|
|
||||||
|
//When: We apply the patch
|
||||||
|
svc.apply(patient, patch);
|
||||||
|
ourLog.info("Outcome:\n{}", ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
|
||||||
|
|
||||||
|
//Then: it applies the new identifier correctly.
|
||||||
|
assertThat(patient.getIdentifier(), hasSize(1));
|
||||||
|
assertThat(patient.getIdentifier().get(0).getSystem(), is(equalTo("third-system")));
|
||||||
|
assertThat(patient.getIdentifier().get(0).getValue(), is(equalTo("third-value")));
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: https://github.com/hapifhir/hapi-fhir/issues/3796
|
||||||
|
@Test
|
||||||
|
public void testAddToSingleCardinalityFieldFailsWhenExists() {
|
||||||
|
FhirPatch svc = new FhirPatch(ourCtx);
|
||||||
|
Patient patient = new Patient();
|
||||||
|
patient.setActive(true);
|
||||||
|
|
||||||
|
//Given: We create a patch request to add a new active statuts
|
||||||
|
BooleanType theValue = new BooleanType(false);
|
||||||
|
Parameters patch = new Parameters();
|
||||||
|
patch.addParameter(createPatchAddOperation("Patient", "active", theValue));
|
||||||
|
|
||||||
|
//When: We apply the patch
|
||||||
|
svc.apply(patient, patch);
|
||||||
|
ourLog.info("Outcome:\n{}", ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(patient));
|
||||||
|
//TODO THIS SHOULD THROW AN EXCEPTION. you cannot `add` to a field that is already set.
|
||||||
|
}
|
||||||
|
|
||||||
|
private Parameters.ParametersParameterComponent createPatchAddOperation(String thePath, String theName, Type theValue) {
|
||||||
|
return createPatchOperation("add", thePath, theName, theValue, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Parameters.ParametersParameterComponent createPatchReplaceOperation(String thePath, Type theValue) {
|
||||||
|
return createPatchOperation("replace", thePath, null , theValue, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Parameters.ParametersParameterComponent createPatchInsertOperation(String thePath, Type theValue) {
|
||||||
|
return createPatchOperation("replace", thePath, null , theValue, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
private Parameters.ParametersParameterComponent createPatchOperation(String theType, String thePath, String theName, Type theValue, Integer theIndex) {
|
||||||
|
Parameters.ParametersParameterComponent operation = new Parameters.ParametersParameterComponent();
|
||||||
|
operation.setName("operation");
|
||||||
|
operation
|
||||||
|
.addPart()
|
||||||
|
.setName("type")
|
||||||
|
.setValue(new CodeType(theType));
|
||||||
|
operation
|
||||||
|
.addPart()
|
||||||
|
.setName("path")
|
||||||
|
.setValue(new StringType(thePath));
|
||||||
|
|
||||||
|
if (theName != null) {
|
||||||
|
operation
|
||||||
|
.addPart()
|
||||||
|
.setName("name")
|
||||||
|
.setValue(new StringType(theName));
|
||||||
|
}
|
||||||
|
if (theValue != null) {
|
||||||
|
operation.addPart()
|
||||||
|
.setName("value")
|
||||||
|
.setValue(theValue);
|
||||||
|
}
|
||||||
|
if (theIndex != null) {
|
||||||
|
operation.addPart()
|
||||||
|
.setName("index")
|
||||||
|
.setValue(new IntegerType(theIndex));
|
||||||
|
}
|
||||||
|
return operation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String extractPartValuePrimitive(Parameters theDiff, int theIndex, String theParameterName, String thePartName) {
|
public static String extractPartValuePrimitive(Parameters theDiff, int theIndex, String theParameterName, String thePartName) {
|
||||||
|
|
|
@ -235,10 +235,11 @@ public class FhirPatch {
|
||||||
childDef.getMutator().addValue(next, nextNewValue);
|
childDef.getMutator().addValue(next, nextNewValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else if ("add".equals(type)) {
|
||||||
|
childDef.getMutator().addValue(next, newValue);
|
||||||
|
} else if ("replace".equals(type)) {
|
||||||
childDef.getMutator().setValue(next, newValue);
|
childDef.getMutator().setValue(next, newValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue