Skip to content

Commit

Permalink
Correctly handle custom types in programatic access to JPA
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesagnew committed Sep 27, 2016
1 parent 090f079 commit eba136d
Show file tree
Hide file tree
Showing 9 changed files with 159 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -694,12 +694,15 @@ public void setAddProfileTagWhenEncoding(AddProfileTagEnum theAddProfileTagWhenE
* 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");
myDefaultTypeForProfile.put(theProfile, theClass);
if (theClass == null) {
myDefaultTypeForProfile.remove(theProfile);
} else {
myDefaultTypeForProfile.put(theProfile, theClass);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -733,6 +733,17 @@ protected void populateResourceIntoEntity(IBaseResource theResource, ResourceTab
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());
Expand Down Expand Up @@ -1003,9 +1014,11 @@ protected ResourceTable toEntity(IResource theResource) {
@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;
Expand All @@ -1022,14 +1035,34 @@ public <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasR
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 ");
Expand All @@ -1045,10 +1078,10 @@ public <R extends IBaseResource> R toResource(Class<R> theResourceType, BaseHasR

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;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -8,7 +10,6 @@
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);
Expand All @@ -21,7 +22,7 @@ public static void afterClassClearContext() {

@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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
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;
Expand Down Expand Up @@ -81,6 +84,18 @@ public IIdType doInTransaction(TransactionStatus theStatus) {
assertEquals(Patient.class, foundResources.get(0).getClass());
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() {
Expand Down
16 changes: 16 additions & 0 deletions hapi-fhir-jpaserver-base/src/test/resources/bug454_utf8.json
Original file line number Diff line number Diff line change
@@ -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."
}
]
}
2 changes: 2 additions & 0 deletions hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down

0 comments on commit eba136d

Please sign in to comment.