Merge 88b3ea079b
into 3f6d1eb29b
This commit is contained in:
commit
35ddcbda78
|
@ -1,89 +0,0 @@
|
||||||
/*
|
|
||||||
* #%L
|
|
||||||
* HAPI FHIR JPA Server
|
|
||||||
* %%
|
|
||||||
* Copyright (C) 2014 - 2024 Smile CDR, Inc.
|
|
||||||
* %%
|
|
||||||
* 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.
|
|
||||||
* #L%
|
|
||||||
*/
|
|
||||||
package ca.uhn.fhir.jpa.util.jsonpatch;
|
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
|
||||||
import ca.uhn.fhir.parser.DataFormatException;
|
|
||||||
import ca.uhn.fhir.parser.IParser;
|
|
||||||
import ca.uhn.fhir.parser.StrictErrorHandler;
|
|
||||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
|
||||||
import com.fasterxml.jackson.core.JsonFactory;
|
|
||||||
import com.fasterxml.jackson.core.JsonParser;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
import com.github.fge.jsonpatch.JsonPatch;
|
|
||||||
import com.github.fge.jsonpatch.JsonPatchException;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.intellij.lang.annotations.Language;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
|
||||||
|
|
||||||
public class JsonPatchUtils {
|
|
||||||
|
|
||||||
public static <T extends IBaseResource> T apply(
|
|
||||||
FhirContext theCtx, T theResourceToUpdate, @Language("JSON") String thePatchBody) {
|
|
||||||
// Parse the patch
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
|
||||||
mapper.configure(JsonParser.Feature.INCLUDE_SOURCE_IN_LOCATION, false);
|
|
||||||
|
|
||||||
JsonFactory factory = mapper.getFactory();
|
|
||||||
|
|
||||||
final JsonPatch patch;
|
|
||||||
try {
|
|
||||||
com.fasterxml.jackson.core.JsonParser parser = factory.createParser(thePatchBody);
|
|
||||||
JsonNode jsonPatchNode = mapper.readTree(parser);
|
|
||||||
patch = JsonPatch.fromJson(jsonPatchNode);
|
|
||||||
|
|
||||||
JsonNode originalJsonDocument =
|
|
||||||
mapper.readTree(theCtx.newJsonParser().encodeResourceToString(theResourceToUpdate));
|
|
||||||
JsonNode after = patch.apply(originalJsonDocument);
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
Class<T> clazz = (Class<T>) theResourceToUpdate.getClass();
|
|
||||||
|
|
||||||
String postPatchedContent = mapper.writeValueAsString(after);
|
|
||||||
|
|
||||||
IParser fhirJsonParser = theCtx.newJsonParser();
|
|
||||||
fhirJsonParser.setParserErrorHandler(new StrictErrorHandler());
|
|
||||||
|
|
||||||
T retVal;
|
|
||||||
try {
|
|
||||||
retVal = fhirJsonParser.parseResource(clazz, postPatchedContent);
|
|
||||||
} catch (DataFormatException e) {
|
|
||||||
String resourceId = theResourceToUpdate
|
|
||||||
.getIdElement()
|
|
||||||
.toUnqualifiedVersionless()
|
|
||||||
.getValue();
|
|
||||||
String resourceType = theCtx.getResourceType(theResourceToUpdate);
|
|
||||||
resourceId = defaultString(resourceId, resourceType);
|
|
||||||
String msg = theCtx.getLocalizer()
|
|
||||||
.getMessage(JsonPatchUtils.class, "failedToApplyPatch", resourceId, e.getMessage());
|
|
||||||
throw new InvalidRequestException(Msg.code(818) + msg);
|
|
||||||
}
|
|
||||||
return retVal;
|
|
||||||
|
|
||||||
} catch (IOException | JsonPatchException theE) {
|
|
||||||
throw new InvalidRequestException(Msg.code(819) + theE.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,7 +4,10 @@ import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
|
import ca.uhn.fhir.jpa.provider.BaseResourceProviderR4Test;
|
||||||
import ca.uhn.fhir.jpa.test.config.TestR4Config;
|
import ca.uhn.fhir.jpa.test.config.TestR4Config;
|
||||||
|
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||||
|
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException;
|
||||||
|
import ca.uhn.fhir.util.BundleBuilder;
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
@ -15,6 +18,7 @@ import org.hl7.fhir.r4.model.Bundle.BundleType;
|
||||||
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
|
import org.hl7.fhir.r4.model.Bundle.HTTPVerb;
|
||||||
import org.hl7.fhir.r4.model.CarePlan;
|
import org.hl7.fhir.r4.model.CarePlan;
|
||||||
import org.hl7.fhir.r4.model.Condition;
|
import org.hl7.fhir.r4.model.Condition;
|
||||||
|
import org.hl7.fhir.r4.model.ContactPoint;
|
||||||
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
|
import org.hl7.fhir.r4.model.Enumerations.AdministrativeGender;
|
||||||
import org.hl7.fhir.r4.model.OperationOutcome;
|
import org.hl7.fhir.r4.model.OperationOutcome;
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
|
@ -26,6 +30,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.concurrent.CompletionService;
|
import java.util.concurrent.CompletionService;
|
||||||
import java.util.concurrent.ExecutorCompletionService;
|
import java.util.concurrent.ExecutorCompletionService;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
@ -68,6 +73,36 @@ public class ResourceProviderR4BundleTest extends BaseResourceProviderR4Test {
|
||||||
myStorageSettings.setBundleBatchMaxPoolSize(JpaStorageSettings.DEFAULT_BUNDLE_BATCH_MAX_POOL_SIZE);
|
myStorageSettings.setBundleBatchMaxPoolSize(JpaStorageSettings.DEFAULT_BUNDLE_BATCH_MAX_POOL_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPatchInTransactionBundleSupportsIfMatchTag() {
|
||||||
|
Patient newPatient = new Patient();
|
||||||
|
newPatient.setId("my-patient");
|
||||||
|
newPatient.getTelecomFirstRep().setSystem(ContactPoint.ContactPointSystem.PHONE).setValue("123-456-7890");
|
||||||
|
myClient.update().resource(newPatient).execute();
|
||||||
|
String bundleString = "{\n" +
|
||||||
|
"\t\"resourceType\": \"Bundle\",\n" +
|
||||||
|
"\t\"type\": \"transaction\",\n" +
|
||||||
|
"\t\"entry\": [{\n" +
|
||||||
|
"\t\t\"fullUrl\": \"Patient/my-patient\",\n" +
|
||||||
|
"\t\t\"resource\": {\n" +
|
||||||
|
"\t\t\t\"resourceType\": \"Binary\",\n" +
|
||||||
|
"\t\t\t\"contentType\": \"application/json-patch+json\",\n" +
|
||||||
|
"\t\t\t\"data\": \"W3sib3AiOiJyZXBsYWNlIiwicGF0aCI6IlwvdGVsZWNvbVwvMFwvdmFsdWUiLCJ2YWx1ZSI6IjQwMTEyMzQ1NiJ9XQ==\"\n" +
|
||||||
|
"\t\t},\n" +
|
||||||
|
"\t\t\"request\": {\n" +
|
||||||
|
"\t\t\t\"method\": \"PATCH\",\n" +
|
||||||
|
"\t\t\t\"url\": \"Patient/my-patient\",\n" +
|
||||||
|
"\t\t\t\"ifMatch\": \"W/\\\"1\\\"\"\n" +
|
||||||
|
"\t\t}\n" +
|
||||||
|
"\t}]\n" +
|
||||||
|
"}";
|
||||||
|
Bundle bundle = myFhirContext.newJsonParser().parseResource(Bundle.class, bundleString);
|
||||||
|
myClient.transaction().withBundle(bundle).execute();
|
||||||
|
Patient execute1 = myClient.read().resource(Patient.class).withId("my-patient").execute();
|
||||||
|
assertThat(execute1).extracting(p -> p.getTelecomFirstRep().getValue()).isEqualTo("401123456");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* See #401
|
* See #401
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue