This commit is contained in:
Tadgh 2024-09-26 02:08:10 +00:00 committed by GitHub
commit 35ddcbda78
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 35 additions and 89 deletions

View File

@ -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());
}
}
}

View File

@ -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
*/ */