Correctly handle custom types in programatic access to JPA

This commit is contained in:
James Agnew 2016-09-27 14:22:48 -04:00
parent 090f079442
commit eba136d706
9 changed files with 159 additions and 10 deletions

View File

@ -694,13 +694,16 @@ public class FhirContext {
* The profile string, e.g. <code>"http://example.com/some_patient_profile"</code>. Must not be
* <code>null</code> or empty.
* @param theClass
* The resource type. Must not be <code>null</code> or empty.
* The resource type, or <code>null</code> to clear any existing type
*/
public void setDefaultTypeForProfile(String theProfile, Class<? extends IBaseResource> theClass) {
Validate.notBlank(theProfile, "theProfile must not be null or empty");
Validate.notNull(theClass, "theProfile must not be null");
if (theClass == null) {
myDefaultTypeForProfile.remove(theProfile);
} else {
myDefaultTypeForProfile.put(theProfile, theClass);
}
}
/**
* This feature is not yet in its final state and should be considered an internal part of HAPI for now - use with

View File

@ -733,6 +733,17 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
extractTagsRi((IAnyResource) theResource, theEntity, allDefs);
}
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
if (def.isStandardType() == false) {
String profile = def.getResourceProfile("");
if (isNotBlank(profile)) {
TagDefinition tag = getTag(TagTypeEnum.PROFILE, NS_JPA_PROFILE, profile, null);
allDefs.add(tag);
theEntity.addTag(tag);
theEntity.setHasTags(true);
}
}
ArrayList<ResourceTag> existingTags = new ArrayList<ResourceTag>();
if (theEntity.isHasTags()) {
existingTags.addAll(theEntity.getTags());
@ -1003,9 +1014,11 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
@Override
public IBaseResource toResource(BaseHasResource theEntity, boolean theForHistoryOperation) {
RuntimeResourceDefinition type = myContext.getResourceDefinition(theEntity.getResourceType());
return toResource(type.getImplementingClass(), theEntity, theForHistoryOperation);
Class<? extends IBaseResource> resourceType = type.getImplementingClass();
return toResource(resourceType, theEntity, theForHistoryOperation);
}
@SuppressWarnings("unchecked")
@Override
public <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasResource theEntity, boolean theForHistoryOperation) {
String resourceText = null;
@ -1022,14 +1035,34 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
break;
}
/*
* Use the appropriate custom type if one is specified in the context
*/
Class<R> resourceType = theResourceType;
if (myContext.hasDefaultTypeForProfile()) {
for (BaseTag nextTag : theEntity.getTags()) {
if (nextTag.getTag().getTagType() == TagTypeEnum.PROFILE) {
String profile = nextTag.getTag().getCode();
if (isNotBlank(profile)) {
Class<? extends IBaseResource> newType = myContext.getDefaultTypeForProfile(profile);
if (newType != null && theResourceType.isAssignableFrom(newType)) {
ourLog.debug("Using custom type {} for profile: {}", newType.getName(), profile);
resourceType = (Class<R>) newType;
break;
}
}
}
}
}
IParser parser = theEntity.getEncoding().newParser(getContext(theEntity.getFhirVersion()));
R retVal;
try {
retVal = parser.parseResource(theResourceType, resourceText);
retVal = parser.parseResource(resourceType, resourceText);
} catch (Exception e) {
StringBuilder b = new StringBuilder();
b.append("Failed to parse database resource[");
b.append(theResourceType);
b.append(resourceType);
b.append("/");
b.append(theEntity.getIdDt().getIdPart());
b.append(" (pid ");
@ -1045,10 +1078,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
if (retVal instanceof IResource) {
IResource res = (IResource) retVal;
retVal = populateResourceMetadataHapi(theResourceType, theEntity, theForHistoryOperation, res);
retVal = populateResourceMetadataHapi(resourceType, theEntity, theForHistoryOperation, res);
} else {
IAnyResource res = (IAnyResource) retVal;
retVal = populateResourceMetadataRi(theResourceType, theEntity, theForHistoryOperation, res);
retVal = populateResourceMetadataRi(resourceType, theEntity, theForHistoryOperation, res);
}
return retVal;
}

View File

@ -0,0 +1,29 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import org.hl7.fhir.dstu3.model.Observation;
import org.hl7.fhir.dstu3.model.StringType;
import ca.uhn.fhir.model.api.annotation.Child;
import ca.uhn.fhir.model.api.annotation.Extension;
import ca.uhn.fhir.model.api.annotation.ResourceDef;
@ResourceDef(name = "Observation", profile = CustomObservationDstu3.PROFILE)
public class CustomObservationDstu3 extends Observation {
public static final String PROFILE = "http://custom_ObservationDstu3";
private static final long serialVersionUID = 1L;
@Extension(definedLocally = false, isModifier = false, url = "http://eyeColour")
@Child(name = "eyeColour")
private StringType myEyeColour;
public StringType getEyeColour() {
return myEyeColour;
}
public void setEyeColour(StringType theEyeColour) {
myEyeColour = theEyeColour;
}
}

View File

@ -0,0 +1,45 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import static org.junit.Assert.assertEquals;
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.rest.server.IBundleProvider;
@SuppressWarnings({ })
public class FhirResourceDaoCustomTypeDstu3Test extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoCustomTypeDstu3Test.class);
@Before
public void before() {
myFhirCtx.setDefaultTypeForProfile(CustomObservationDstu3.PROFILE, CustomObservationDstu3.class);
}
@Test
public void testSaveAndRestore() {
CustomObservationDstu3 obs = new CustomObservationDstu3();
obs.setEyeColour(new StringType("blue"));
IIdType id = myObservationDao.create(obs).getId().toUnqualifiedVersionless();
CustomObservationDstu3 read = (CustomObservationDstu3) myObservationDao.read(id);
assertEquals("blue", read.getEyeColour().getValue());
IBundleProvider found = myObservationDao.search(new SearchParameterMap());
assertEquals(1, found.size());
CustomObservationDstu3 search = (CustomObservationDstu3) found.getResources(0, 1).get(0);
assertEquals("blue", search.getEyeColour().getValue());
}
@After
public void after() {
myFhirCtx.setDefaultTypeForProfile(CustomObservationDstu3.PROFILE, null);
}
}

View File

@ -1,5 +1,7 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.IOUtils;
import org.hl7.fhir.dstu3.model.Bundle;
import org.junit.AfterClass;
@ -8,7 +10,6 @@ import org.junit.Test;
import ca.uhn.fhir.jpa.dao.DaoMethodOutcome;
import ca.uhn.fhir.util.TestUtil;
@SuppressWarnings("unchecked")
public class FhirResourceDaoDocumentDstu3Test extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDocumentDstu3Test.class);
@ -21,7 +22,7 @@ public class FhirResourceDaoDocumentDstu3Test extends BaseJpaDstu3Test {
@Test
public void testPostDocument() throws Exception {
String input = IOUtils.toString(getClass().getResourceAsStream("/sample-document.xml"));
String input = IOUtils.toString(getClass().getResourceAsStream("/sample-document.xml"), StandardCharsets.UTF_8);
Bundle inputBundle = myFhirCtx.newXmlParser().parseResource(Bundle.class, input);
DaoMethodOutcome responseBundle = myBundleDao.create(inputBundle, mySrd);
}

View File

@ -13,11 +13,14 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.ContactPoint.ContactPointSystem;
@ -82,6 +85,18 @@ public class FhirResourceDaoDstu3SearchNoFtTest extends BaseJpaDstu3Test {
assertEquals(Condition.class, foundResources.get(1).getClass());
}
/**
* #454
*/
@Test
public void testIndexWithUtf8Chars() throws IOException {
String input = IOUtils.toString(getClass().getResourceAsStream("/bug454_utf8.json"), StandardCharsets.UTF_8);
CodeSystem cs = (CodeSystem) myFhirCtx.newJsonParser().parseResource(input);
myCodeSystemDao.create(cs);
}
@Test
public void testCodeSearch() {
Subscription subs = new Subscription();

View File

@ -0,0 +1,16 @@
{
"resourceType": "CodeSystem",
"url": "http://nestvision.com/fhir/myexample-yh",
"name": "NestVision Restful Interactions",
"status": "active",
"publisher": "杨浩",
"description": "this is a codesystem example.",
"caseSensitive": true,
"concept": [
{
"code": "mygod",
"display": "wodetian",
"definition": "this is a translate."
}
]
}

View File

@ -1,3 +1,5 @@
call SYSCS_UTIL.SYSCS_COMPRESS_TABLE('SA', 'HFJ_SEARCH_RESULT', 1);
CALL SYSCS_UTIL.SYSCS_INPLACE_COMPRESS_TABLE( 'SA', 'HFJ_SEARCH_RESULT', 0, 0, 1 );
dd if=/dev/urandom of=/opt/glassfish/tmp.tmp bs=1M count=500

View File

@ -102,6 +102,11 @@
JAX-RS server was not able to handle the new mime types defined
in STU3
</action>
<action type="fix">
JPA server did not handle custom types when being called
programatically (I.e. not through HTTP interface). Thanks to
Anthony Mei for pointing this out!
</action>
</release>
<release version="2.0" date="2016-08-30">
<action type="fix">