Get history and tags working properly on JPA

This commit is contained in:
jamesagnew 2014-06-02 09:07:10 -04:00
parent be44a2bca2
commit dfa88a442c
41 changed files with 862 additions and 274 deletions

View File

@ -20,7 +20,7 @@ package ca.uhn.fhir.model.api;
* #L% * #L%
*/ */
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.*;
import java.util.Date; import java.util.Date;
import java.util.Map; import java.util.Map;
@ -46,6 +46,12 @@ public enum ResourceMetadataKeyEnum {
public InstantDt get(IResource theResource) { public InstantDt get(IResource theResource) {
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), DELETED_AT); return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), DELETED_AT);
} }
@Override
public void put(IResource theResource, Object theObject) {
InstantDt obj = (InstantDt) theObject;
theResource.getResourceMetadata().put(DELETED_AT, obj);
}
}, },
/** /**
@ -61,6 +67,12 @@ public enum ResourceMetadataKeyEnum {
public IdDt get(IResource theResource) { public IdDt get(IResource theResource) {
return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PREVIOUS_ID); return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PREVIOUS_ID);
} }
@Override
public void put(IResource theResource, Object theObject) {
IdDt obj = (IdDt) theObject;
theResource.getResourceMetadata().put(PREVIOUS_ID, obj);
}
}, },
/** /**
@ -83,6 +95,12 @@ public enum ResourceMetadataKeyEnum {
public InstantDt get(IResource theResource) { public InstantDt get(IResource theResource) {
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PUBLISHED); return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), PUBLISHED);
} }
@Override
public void put(IResource theResource, Object theObject) {
InstantDt obj = (InstantDt) theObject;
theResource.getResourceMetadata().put(PUBLISHED, obj);
}
}, },
@ -109,6 +127,12 @@ public enum ResourceMetadataKeyEnum {
} }
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + TAG_LIST.name() + " - Expected " + TagList.class.getCanonicalName()); throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + TAG_LIST.name() + " - Expected " + TagList.class.getCanonicalName());
} }
@Override
public void put(IResource theResource, Object theObject) {
TagList obj = (TagList) theObject;
theResource.getResourceMetadata().put(TAG_LIST, obj);
}
}, },
@ -128,6 +152,12 @@ public enum ResourceMetadataKeyEnum {
public InstantDt get(IResource theResource) { public InstantDt get(IResource theResource) {
return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), UPDATED); return getInstantFromMetadataOrNullIfNone(theResource.getResourceMetadata(), UPDATED);
} }
@Override
public void put(IResource theResource, Object theObject) {
InstantDt obj = (InstantDt) theObject;
theResource.getResourceMetadata().put(UPDATED, obj);
}
}, },
/** /**
@ -144,6 +174,12 @@ public enum ResourceMetadataKeyEnum {
public IdDt get(IResource theResource) { public IdDt get(IResource theResource) {
return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION_ID); return getIdFromMetadataOrNullIfNone(theResource.getResourceMetadata(), VERSION_ID);
} }
@Override
public void put(IResource theResource, Object theObject) {
IdDt obj = (IdDt) theObject;
theResource.getResourceMetadata().put(VERSION_ID, obj);
}
}; };
public abstract Object get(IResource theResource); public abstract Object get(IResource theResource);
@ -186,4 +222,6 @@ public enum ResourceMetadataKeyEnum {
throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName()); throw new InternalErrorException("Found an object of type '" + retValObj.getClass().getCanonicalName() + "' in resource metadata for key " + theKey.name() + " - Expected " + InstantDt.class.getCanonicalName());
} }
public abstract void put(IResource theResource, Object theObject);
} }

View File

@ -29,7 +29,7 @@ public class TagList extends ArrayList<Tag> {
public static final String ELEMENT_NAME = "TagList"; public static final String ELEMENT_NAME = "TagList";
public static final String ELEMENT_NAME_LC = ELEMENT_NAME.toLowerCase(); public static final String ELEMENT_NAME_LC = ELEMENT_NAME.toLowerCase();
public Tag addTag(String theTerm, String theLabel, String theScheme) { public Tag addTag(String theScheme, String theTerm, String theLabel) {
Tag retVal = new Tag(theTerm, theLabel, theScheme); Tag retVal = new Tag(theTerm, theLabel, theScheme);
add(retVal); add(retVal);
return retVal; return retVal;
@ -39,4 +39,13 @@ public class TagList extends ArrayList<Tag> {
return addTag(null, null, null); return addTag(null, null, null);
} }
public Tag getTag(String theScheme, String theTerm) {
for (Tag next : this) {
if (theScheme.equals(next.getScheme()) && theTerm.equals(next.getTerm())) {
return next;
}
}
return null;
}
} }

View File

@ -48,10 +48,10 @@ import ca.uhn.fhir.rest.server.Constants;
@DatatypeDef(name = "id") @DatatypeDef(name = "id")
public class IdDt extends BasePrimitive<String> { public class IdDt extends BasePrimitive<String> {
private String myUnqualifiedId;
private String myValue;
private String myUnqualifiedVersionId;
private String myResourceType; private String myResourceType;
private String myUnqualifiedId;
private String myUnqualifiedVersionId;
private String myValue;
/** /**
* Create a new empty ID * Create a new empty ID
@ -166,6 +166,14 @@ public class IdDt extends BasePrimitive<String> {
return myUnqualifiedVersionId; return myUnqualifiedVersionId;
} }
public Long getUnqualifiedVersionIdAsLong() {
if (!hasUnqualifiedVersionId()) {
return null;
}else {
return Long.parseLong(getUnqualifiedVersionId());
}
}
/** /**
* Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a * Returns the value of this ID. Note that this value may be a fully qualified URL, a relative/partial URL, or a
* simple ID. Use {@link #getUnqualifiedId()} to get just the ID portion. * simple ID. Use {@link #getUnqualifiedId()} to get just the ID portion.
@ -182,6 +190,31 @@ public class IdDt extends BasePrimitive<String> {
return myValue; return myValue;
} }
public boolean hasUnqualifiedId() {
return isNotBlank(getUnqualifiedId());
}
public boolean hasUnqualifiedVersionId() {
return isNotBlank(getUnqualifiedVersionId());
}
/**
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only
* of digits)
*/
public boolean isValidLong() {
String id = getUnqualifiedId();
if (StringUtils.isBlank(id)) {
return false;
}
for (int i = 0; i < id.length(); i++) {
if (Character.isDigit(id.charAt(i)) == false) {
return false;
}
}
return true;
}
/** /**
* Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method * Copies the value from the given IdDt to <code>this</code> IdDt. It is generally not neccesary to use this method
* but it is provided for consistency with the rest of the API. * but it is provided for consistency with the rest of the API.
@ -254,28 +287,6 @@ public class IdDt extends BasePrimitive<String> {
setValue(theValue); setValue(theValue);
} }
@Override
public String toString() {
return myValue;
}
/**
* Returns <code>true</code> if the unqualified ID is a valid {@link Long} value (in other words, it consists only
* of digits)
*/
public boolean isValidLong() {
String id = getUnqualifiedId();
if (StringUtils.isBlank(id)) {
return false;
}
for (int i = 0; i < id.length(); i++) {
if (Character.isDigit(id.charAt(i)) == false) {
return false;
}
}
return true;
}
/** /**
* Returns a view of this ID as a fully qualified URL, given a server base and resource name * Returns a view of this ID as a fully qualified URL, given a server base and resource name
* (which will only be used if the ID does not already contain those respective parts). Essentially, * (which will only be used if the ID does not already contain those respective parts). Essentially,
@ -305,4 +316,20 @@ public class IdDt extends BasePrimitive<String> {
return retVal.toString(); return retVal.toString();
} }
@Override
public String toString() {
return myValue;
}
public IdDt withoutVersion() {
int i = myValue.indexOf(Constants.PARAM_HISTORY);
if (i > 1) {
return new IdDt(myValue.substring(0, i-1));
}else {
return this;
}
}
} }

View File

@ -121,9 +121,11 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
if (myType != IResource.class) { if (myType != IResource.class) {
if (id != null) { if (id != null) {
if (versionId != null) { if (versionId != null) {
retVal = new HttpGetClientInvocation(getResourceName(), id.getValue(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS); retVal = new HttpGetClientInvocation(getResourceName(), id.getUnqualifiedId(), Constants.PARAM_HISTORY, versionId.getValue(), Constants.PARAM_TAGS);
} else if (id.hasUnqualifiedVersionId()){
retVal = new HttpGetClientInvocation(getResourceName(), id.getUnqualifiedId(), Constants.PARAM_HISTORY, id.getUnqualifiedVersionId(), Constants.PARAM_TAGS);
} else { } else {
retVal = new HttpGetClientInvocation(getResourceName(), id.getValue(), Constants.PARAM_TAGS); retVal = new HttpGetClientInvocation(getResourceName(), id.getUnqualifiedId(), Constants.PARAM_TAGS);
} }
} else { } else {
retVal = new HttpGetClientInvocation(getResourceName(), Constants.PARAM_TAGS); retVal = new HttpGetClientInvocation(getResourceName(), Constants.PARAM_TAGS);
@ -192,9 +194,9 @@ public class GetTagsMethodBinding extends BaseMethodBinding<TagList> {
if ((myIdParamIndex != null) != (theRequest.getId() != null)) { if ((myIdParamIndex != null) != (theRequest.getId() != null)) {
return false; return false;
} }
if ((myVersionIdParamIndex != null) != (theRequest.getVersionId() != null)) { // if ((myVersionIdParamIndex != null) != (theRequest.getVersionId() != null)) {
return false; // return false;
} // }
return true; return true;
} }

View File

@ -0,0 +1,16 @@
package ca.uhn.fhir.rest.server;
import java.util.List;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.InstantDt;
public interface IBundleProvider {
List<IResource> getResources(int theFromIndex, int theToIndex);
int size();
InstantDt getPublished();
}

View File

@ -0,0 +1,5 @@
package ca.uhn.fhir.rest.server;
public interface IPagingProvider {
}

View File

@ -23,7 +23,6 @@ package ca.uhn.fhir.rest.server;
import static org.apache.commons.lang3.StringUtils.*; import static org.apache.commons.lang3.StringUtils.*;
import java.io.IOException; import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.Arrays; import java.util.Arrays;
@ -567,7 +566,8 @@ public class RestfulServer extends HttpServlet {
if (id == null) { if (id == null) {
throw new InvalidRequestException("Don't know how to handle request path: " + requestPath); throw new InvalidRequestException("Don't know how to handle request path: " + requestPath);
} }
versionId = new IdDt(resourceName + "/" + id.getUnqualifiedId() + "/_history/" + versionString); id = new IdDt(resourceName + "/" + id.getUnqualifiedId() + "/_history/" + versionString);
versionId = id;
} else { } else {
operation = Constants.PARAM_HISTORY; operation = Constants.PARAM_HISTORY;
} }

View File

@ -156,6 +156,7 @@ public class RestfulServerTesterServlet extends HttpServlet {
ctx.setVariable("conf", conformance); ctx.setVariable("conf", conformance);
ctx.setVariable("base", myServerBase); ctx.setVariable("base", myServerBase);
ctx.setVariable("jsonEncodedConf", myCtx.newJsonParser().encodeResourceToString(conformance)); ctx.setVariable("jsonEncodedConf", myCtx.newJsonParser().encodeResourceToString(conformance));
addStandardVariables(ctx, theReq.getParameterMap());
theResp.setContentType("text/html"); theResp.setContentType("text/html");
theResp.setCharacterEncoding("UTF-8"); theResp.setCharacterEncoding("UTF-8");
@ -167,6 +168,17 @@ public class RestfulServerTesterServlet extends HttpServlet {
} }
} }
private void addStandardVariables(WebContext theCtx, Map<String, String[]> theParameterMap) {
addStandardVariable(theCtx, theParameterMap, "configEncoding");
addStandardVariable(theCtx, theParameterMap, "configPretty");
}
private void addStandardVariable(WebContext theCtx, Map<String, String[]> theParameterMap, String key) {
if (theParameterMap.containsKey(key) && theParameterMap.get(key).length > 0) {
theCtx.setVariable(key, theParameterMap.get(key)[0]);
}
}
@Override @Override
protected void doPost(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException { protected void doPost(HttpServletRequest theReq, HttpServletResponse theResp) throws ServletException, IOException {
if (DEBUGMODE) { if (DEBUGMODE) {

View File

@ -18,7 +18,7 @@ This file is a Thymeleaf template for the
<script type="text/javascript" src="json2.js"></script> <script type="text/javascript" src="json2.js"></script>
<script type="text/javascript" src="minify.json.js"></script> <script type="text/javascript" src="minify.json.js"></script>
</head> </head>
<body> <body onload="selectResourceType();">
<table border="0" width="100%"> <table border="0" width="100%">
<tr> <tr>
<td align="left"><img src="hapi_fhir_banner.png"/></td> <td align="left"><img src="hapi_fhir_banner.png"/></td>
@ -56,17 +56,30 @@ This file is a Thymeleaf template for the
<tr> <tr>
<td class="propertyKeyCell">Encoding</td> <td class="propertyKeyCell">Encoding</td>
<td> <td>
<select id="configEncoding"> <select id="configEncoding" th:switch="${configEncoding}">
<option value="" selected="true">(default)</option> <th:block th:case="'xml'">
<option value="">(default)</option>
<option value="xml" selected="selected">XML</option>
<option value="json">JSON</option>
</th:block>
<th:block th:case="'json'">
<option value="">(default)</option>
<option value="xml">XML</option>
<option value="json" selected="selected">JSON</option>
</th:block>
<th:block th:case="*">
<option value="" selected="selected">(default)</option>
<option value="xml">XML</option> <option value="xml">XML</option>
<option value="json">JSON</option> <option value="json">JSON</option>
</th:block>
</select> </select>
</td> </td>
</tr> </tr>
<tr> <tr>
<td class="propertyKeyCell">Pretty Printing</td> <td class="propertyKeyCell">Pretty Printing</td>
<td> <td th:switch="${configPretty}">
<input type="checkbox" id="configPretty" checked="checked"/> <input th:case="'false'" type="checkbox" id="configPretty"/>
<input th:case="*" type="checkbox" id="configPretty" checked="checked"/>
</td> </td>
</tr> </tr>
</table> </table>
@ -94,7 +107,24 @@ This file is a Thymeleaf template for the
</tr> </tr>
</table> </table>
<th:block th:each="resource, resIterStat : ${rest.resource}" th:with="expandoId='resExpando'+${resIterStat.count}"> <div class="bodyHeaderBlock">
Resources
</div>
<table border="0" th:id="systemExpando" class="propertyTable">
<tr>
<td valign="top" class="propertyKeyCell">Select Resource:</td>
<td valign="top">
<select id="configResource" onchange="selectResourceType();">
<th:block th:each="resource, resIterStat : ${rest.resource}">
<option th:value="${resource.type.valueAsString}" th:text="${resource.type.valueAsString}"/>
</th:block>
</select>
</td>
</tr>
</table>
<div th:each="resource, resIterStat : ${rest.resource}" th:with="expandoId='resExpando'+${resIterStat.count}" th:id="'res' + ${resource.type.valueAsString}" class="resourceTypeContainer" style="display:none;">
<div class="bodyHeaderBlock"> <div class="bodyHeaderBlock">
Resource: <th:block th:text="${resource.type.valueAsString}"/> Resource: <th:block th:text="${resource.type.valueAsString}"/>
</div> </div>
@ -118,7 +148,7 @@ This file is a Thymeleaf template for the
</td> </td>
</tr> </tr>
</table> </table>
</th:block> </div>
</th:block> </th:block>
</td> </td>
</tr> </tr>

View File

@ -341,6 +341,15 @@ function newUniqueId() {
return "uid" + uniqueIdSeed++; return "uid" + uniqueIdSeed++;
} }
function selectResourceType() {
$('.resourceTypeContainer').each(function() {
$(this).hide();
});
var cr = document.getElementById('configResource');
var selected = cr.options[cr.selectedIndex].value;
$('#res' + selected).show();
}
/** Show a newly created tester form */ /** Show a newly created tester form */
function showNewForm() { function showNewForm() {
var time = 0; var time = 0;

View File

@ -613,8 +613,8 @@ public Patient readPatient(@IdParam IdDt theId) {
// Create a TagList and place a complete list of the patient's tags inside // Create a TagList and place a complete list of the patient's tags inside
TagList tags = new TagList(); TagList tags = new TagList();
tags.addTag("Dog", "Canine Patient", "http://animals"); // TODO: more realistic example tags.addTag("http://animals", "Dog", "Canine Patient"); // TODO: more realistic example
tags.addTag("Friendly", "Friendly", "http://personality"); // TODO: more realistic example tags.addTag("http://personality", "Friendly", "Friendly"); // TODO: more realistic example
// The tags are then stored in the Patient resource instance // The tags are then stored in the Patient resource instance
retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tags); retVal.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tags);
@ -656,8 +656,8 @@ newPatient.addName().addFamily("Jones").addGiven("Frank");
// Populate tags // Populate tags
TagList tags = new TagList(); TagList tags = new TagList();
tags.addTag("Dog", "Canine Patient", "http://animals"); // TODO: more realistic example tags.addTag("http://animals", "Dog", "Canine Patient"); // TODO: more realistic example
tags.addTag("Friendly", "Friendly", "http://personality"); // TODO: more realistic example tags.addTag("http://personality", "Friendly", "Friendly"); // TODO: more realistic example
newPatient.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tags); newPatient.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tags);
// ...invoke the create method on the client... // ...invoke the create method on the client...

View File

@ -230,6 +230,28 @@ public class TagsClientTest {
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8")); when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ser), Charset.forName("UTF-8"))); when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ser), Charset.forName("UTF-8")));
IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
TagList response = client.getAllTagsPatientId(new IdDt("Patient", "111", "222"));
assertEquals(tagList, response);
assertEquals(HttpGet.class, capt.getValue().getClass());
HttpGet get = (HttpGet) capt.getValue();
assertEquals("http://foo/Patient/111/_history/222/_tags", get.getURI().toString());
}
@Test
public void testGetAllTagsPatientIdVersionOld() throws Exception {
TagList tagList = new TagList();
tagList.add(new Tag("AAA", "BBB", "CCC"));
String ser = ctx.newXmlParser().encodeTagListToString(tagList);
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
when(httpClient.execute(capt.capture())).thenReturn(httpResponse);
when(httpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
when(httpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_XML + "; charset=UTF-8"));
when(httpResponse.getEntity().getContent()).thenReturn(new ReaderInputStream(new StringReader(ser), Charset.forName("UTF-8")));
IClient client = ctx.newRestfulClient(IClient.class, "http://foo"); IClient client = ctx.newRestfulClient(IClient.class, "http://foo");
TagList response = client.getAllTagsPatientIdVersion(new IdDt("111"), new IdDt("222")); TagList response = client.getAllTagsPatientIdVersion(new IdDt("111"), new IdDt("222"));
assertEquals(tagList, response); assertEquals(tagList, response);

View File

@ -1516,8 +1516,11 @@ public class ResfulServerMethodTest {
@Read() @Read()
public Patient getResourceById(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId) { public Patient getResourceById(@IdParam IdDt theId, @VersionIdParam IdDt theVersionId) {
Patient retVal = getIdToPatient().get(theId.getValue()); Patient retVal = getIdToPatient().get(theId.getUnqualifiedId());
retVal.getName().get(0).setText(theVersionId.getValue()); List<HumanNameDt> name = retVal.getName();
HumanNameDt nameDt = name.get(0);
String value = theVersionId.getValue();
nameDt.setText(value);
return retVal; return retVal;
} }

View File

@ -1,5 +1,6 @@
package ca.uhn.fhir.rest.server; package ca.uhn.fhir.rest.server;
import static org.apache.commons.lang.StringUtils.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -25,6 +26,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Tag; import ca.uhn.fhir.model.api.Tag;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.api.annotation.TagListParam; import ca.uhn.fhir.model.api.annotation.TagListParam;
import ca.uhn.fhir.model.dstu.resource.Observation;
import ca.uhn.fhir.model.dstu.resource.Patient; import ca.uhn.fhir.model.dstu.resource.Patient;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.AddTags; import ca.uhn.fhir.rest.annotation.AddTags;
@ -58,7 +60,7 @@ public class TagsServerTest {
public void testAddTagsById() throws Exception { public void testAddTagsById() throws Exception {
TagList tagList = new TagList(); TagList tagList = new TagList();
tagList.addTag("term", "label", "scheme"); tagList.addTag("scheme", "term", "label");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/111/_tags"); HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/111/_tags");
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeTagListToString(tagList), ContentType.create(EncodingEnum.JSON.getResourceContentType(), "UTF-8"))); httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeTagListToString(tagList), ContentType.create(EncodingEnum.JSON.getResourceContentType(), "UTF-8")));
@ -76,7 +78,7 @@ public class TagsServerTest {
public void testAddTagsByIdAndVersion() throws Exception { public void testAddTagsByIdAndVersion() throws Exception {
TagList tagList = new TagList(); TagList tagList = new TagList();
tagList.addTag("term", "label", "scheme"); tagList.addTag("scheme", "term", "label");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/111/_history/222/_tags"); HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/111/_history/222/_tags");
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeTagListToString(tagList), ContentType.create(EncodingEnum.JSON.getResourceContentType(), "UTF-8"))); httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeTagListToString(tagList), ContentType.create(EncodingEnum.JSON.getResourceContentType(), "UTF-8")));
@ -167,11 +169,28 @@ public class TagsServerTest {
assertEquals(expected, actual); assertEquals(expected, actual);
} }
@Test
public void testGetAllTagsObservationIdVersion() throws Exception {
HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/111/_history/222/_tags");
HttpResponse status = ourClient.execute(httpGet);
String responseContent = IOUtils.toString(status.getEntity().getContent());
ourLog.info("Response was:\n{}", responseContent);
assertEquals(200, status.getStatusLine().getStatusCode());
TagList actual = ourCtx.newXmlParser().parseTagList(responseContent);
TagList expected = ourProvider.getAllTags();
expected.get(0).setTerm("Patient111222");
assertEquals(expected, actual);
}
@Test @Test
public void testRemoveTagsById() throws Exception { public void testRemoveTagsById() throws Exception {
TagList tagList = new TagList(); TagList tagList = new TagList();
tagList.addTag("term", "label", "scheme"); tagList.addTag("scheme", "term", "label");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/111/_tags/_delete"); HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/111/_tags/_delete");
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeTagListToString(tagList), ContentType.create(EncodingEnum.JSON.getResourceContentType(), "UTF-8"))); httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeTagListToString(tagList), ContentType.create(EncodingEnum.JSON.getResourceContentType(), "UTF-8")));
@ -189,7 +208,7 @@ public class TagsServerTest {
public void testRemoveTagsByIdAndVersion() throws Exception { public void testRemoveTagsByIdAndVersion() throws Exception {
TagList tagList = new TagList(); TagList tagList = new TagList();
tagList.addTag("term", "label", "scheme"); tagList.addTag("scheme", "term", "label");
HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/111/_history/222/_tags/_delete"); HttpPost httpPost = new HttpPost("http://localhost:" + ourPort + "/Patient/111/_history/222/_tags/_delete");
httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeTagListToString(tagList), ContentType.create(EncodingEnum.JSON.getResourceContentType(), "UTF-8"))); httpPost.setEntity(new StringEntity(ourCtx.newJsonParser().encodeTagListToString(tagList), ContentType.create(EncodingEnum.JSON.getResourceContentType(), "UTF-8")));
@ -264,7 +283,7 @@ public class TagsServerTest {
@GetTags(type = Patient.class) @GetTags(type = Patient.class)
public TagList getAllTagsPatientId(@IdParam IdDt theId) { public TagList getAllTagsPatientId(@IdParam IdDt theId) {
TagList tagList = new TagList(); TagList tagList = new TagList();
tagList.add(new Tag("Patient" + theId.getUnqualifiedId(), "DogLabel", (String) null)); tagList.add(new Tag("Patient" + theId.getUnqualifiedId() + defaultString(theId.getUnqualifiedVersionId()), "DogLabel", (String) null));
tagList.add(new Tag("AllCat", "CatLabel", "http://cats")); tagList.add(new Tag("AllCat", "CatLabel", "http://cats"));
return tagList; return tagList;
} }
@ -277,6 +296,14 @@ public class TagsServerTest {
return tagList; return tagList;
} }
@GetTags(type = Observation.class)
public TagList getAllTagsObservationIdVersion(@IdParam IdDt theId) {
TagList tagList = new TagList();
tagList.add(new Tag("Patient" + theId.getUnqualifiedId() + theId.getUnqualifiedVersionId(), "DogLabel", (String) null));
tagList.add(new Tag("AllCat", "CatLabel", "http://cats"));
return tagList;
}
@DeleteTags(type = Patient.class) @DeleteTags(type = Patient.class)
public void RemoveTagsPatient(@IdParam IdDt theId, @VersionIdParam IdDt theVersion, @TagListParam TagList theTagList) { public void RemoveTagsPatient(@IdParam IdDt theId, @VersionIdParam IdDt theVersion, @TagListParam TagList theTagList) {
ourLastOutcome = "Remove" + theId.getUnqualifiedId() + theVersion.getUnqualifiedVersionId(); ourLastOutcome = "Remove" + theId.getUnqualifiedId() + theVersion.getUnqualifiedVersionId();

View File

@ -14,6 +14,7 @@
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry excluding="**/*.java" including="**/*.java" kind="src" path="src/main/resources"/> <classpathentry excluding="**/*.java" including="**/*.java" kind="src" path="src/main/resources"/>
<classpathentry combineaccessrules="false" kind="src" path="/hapi-fhir-base"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>

View File

@ -9,8 +9,10 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.NoResultException; import javax.persistence.NoResultException;
@ -34,12 +36,14 @@ import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.BaseTag; import ca.uhn.fhir.jpa.entity.BaseTag;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTablePk; import ca.uhn.fhir.jpa.entity.ResourceHistoryTablePk;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTag;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.entity.ResourceLink; import ca.uhn.fhir.jpa.entity.ResourceLink;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.ResourceTag;
import ca.uhn.fhir.jpa.entity.TagDefinition; import ca.uhn.fhir.jpa.entity.TagDefinition;
import ca.uhn.fhir.model.api.IDatatype; import ca.uhn.fhir.model.api.IDatatype;
import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.model.api.IPrimitiveDatatype;
@ -85,7 +89,14 @@ public abstract class BaseFhirDao {
myContext = theContext; myContext = theContext;
} }
private void searchHistoryCurrentVersion(String theResourceName, Long theId, Date theSince, int theLimit, List<HistoryTuple> tuples) { protected DaoConfig getConfig() {
return myConfig;
}
@Autowired(required = true)
private DaoConfig myConfig;
private void searchHistoryCurrentVersion(String theResourceName, Long theId, Date theSince, Integer theLimit, List<HistoryTuple> tuples) {
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = builder.createTupleQuery(); CriteriaQuery<Tuple> cq = builder.createTupleQuery();
Root<?> from = cq.from(ResourceTable.class); Root<?> from = cq.from(ResourceTable.class);
@ -108,8 +119,10 @@ public abstract class BaseFhirDao {
cq.orderBy(builder.desc(from.get("myUpdated"))); cq.orderBy(builder.desc(from.get("myUpdated")));
TypedQuery<Tuple> q = myEntityManager.createQuery(cq); TypedQuery<Tuple> q = myEntityManager.createQuery(cq);
if (theLimit > 0) { if (theLimit != null && theLimit < myConfig.getHardSearchLimit()) {
q.setMaxResults(theLimit); q.setMaxResults(theLimit);
} else {
q.setMaxResults(myConfig.getHardSearchLimit());
} }
for (Tuple next : q.getResultList()) { for (Tuple next : q.getResultList()) {
long id = (Long) next.get(0); long id = (Long) next.get(0);
@ -147,7 +160,7 @@ public abstract class BaseFhirDao {
} }
} }
private void searchHistoryHistory(String theResourceName, Long theId, Date theSince, int theLimit, List<HistoryTuple> tuples) { private void searchHistoryHistory(String theResourceName, Long theId, Date theSince, Integer theLimit, List<HistoryTuple> tuples) {
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = builder.createTupleQuery(); CriteriaQuery<Tuple> cq = builder.createTupleQuery();
Root<?> from = cq.from(ResourceHistoryTable.class); Root<?> from = cq.from(ResourceHistoryTable.class);
@ -170,8 +183,10 @@ public abstract class BaseFhirDao {
cq.orderBy(builder.desc(from.get("myUpdated"))); cq.orderBy(builder.desc(from.get("myUpdated")));
TypedQuery<Tuple> q = myEntityManager.createQuery(cq); TypedQuery<Tuple> q = myEntityManager.createQuery(cq);
if (theLimit > 0) { if (theLimit != null && theLimit < myConfig.getHardSearchLimit()) {
q.setMaxResults(theLimit); q.setMaxResults(theLimit);
} else {
q.setMaxResults(myConfig.getHardSearchLimit());
} }
for (Tuple next : q.getResultList()) { for (Tuple next : q.getResultList()) {
ResourceHistoryTablePk id = (ResourceHistoryTablePk) next.get(0); ResourceHistoryTablePk id = (ResourceHistoryTablePk) next.get(0);
@ -534,13 +549,18 @@ public abstract class BaseFhirDao {
for (IFhirResourceDao<?> next : myResourceDaos) { for (IFhirResourceDao<?> next : myResourceDaos) {
myResourceTypeToDao.put(next.getResourceType(), next); myResourceTypeToDao.put(next.getResourceType(), next);
} }
if (this instanceof IFhirResourceDao<?>) {
IFhirResourceDao<?> thiz = (IFhirResourceDao<?>) this;
myResourceTypeToDao.put(thiz.getResourceType(), thiz);
} }
Map<Class<? extends IResource>, IFhirResourceDao<?>> resourceTypeToDao = myResourceTypeToDao;
return resourceTypeToDao.get(theType);
} }
protected ArrayList<IResource> history(String theResourceName, Long theId, Date theSince, int theLimit) { return myResourceTypeToDao.get(theType);
}
protected ArrayList<IResource> history(String theResourceName, Long theId, Date theSince, Integer theLimit) {
List<HistoryTuple> tuples = new ArrayList<HistoryTuple>(); List<HistoryTuple> tuples = new ArrayList<HistoryTuple>();
// Get list of IDs // Get list of IDs
@ -564,8 +584,15 @@ public abstract class BaseFhirDao {
return theO2.getUpdated().getValue().compareTo(theO1.getUpdated().getValue()); return theO2.getUpdated().getValue().compareTo(theO1.getUpdated().getValue());
} }
}); });
if (resEntities.size() > theLimit) {
resEntities = resEntities.subList(0, theLimit); int limit;
if (theLimit != null && theLimit < myConfig.getHardSearchLimit()) {
limit = theLimit;
} else {
limit = myConfig.getHardSearchLimit();
}
if (resEntities.size() > limit) {
resEntities = resEntities.subList(0, limit);
} }
ArrayList<IResource> retVal = new ArrayList<IResource>(); ArrayList<IResource> retVal = new ArrayList<IResource>();
@ -588,8 +615,7 @@ public abstract class BaseFhirDao {
return new String(out).toUpperCase(); return new String(out).toUpperCase();
} }
protected TagDefinition getTag(String theScheme, String theTerm, String theLabel) {
private TagDefinition getTag(String theScheme, String theTerm, String theLabel) {
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<TagDefinition> cq = builder.createQuery(TagDefinition.class); CriteriaQuery<TagDefinition> cq = builder.createQuery(TagDefinition.class);
Root<TagDefinition> from = cq.from(TagDefinition.class); Root<TagDefinition> from = cq.from(TagDefinition.class);
@ -659,6 +685,10 @@ public abstract class BaseFhirDao {
protected String toResourceName(IResource theResource) { protected String toResourceName(IResource theResource) {
return myContext.getResourceDefinition(theResource).getName(); return myContext.getResourceDefinition(theResource).getName();
} }
protected String toResourceName(Class<? extends IResource> theResourceType) {
return myContext.getResourceDefinition(theResourceType).getName();
}
protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory) { protected ResourceTable updateEntity(final IResource theResource, ResourceTable entity, boolean theUpdateHistory) {
if (entity.getPublished() == null) { if (entity.getPublished() == null) {
@ -739,4 +769,66 @@ public abstract class BaseFhirDao {
return entity; return entity;
} }
protected TagList getTags(Class<? extends IResource> theResourceType, IdDt theResourceId) {
String resourceName=null;
if (theResourceType != null) {
resourceName = toResourceName(theResourceType);
if (theResourceId != null && theResourceId.hasUnqualifiedVersionId()) {
IFhirResourceDao<? extends IResource> dao = getDao(theResourceType);
BaseHasResource entity = dao.readEntity(theResourceId);
TagList retVal = new TagList();
for (BaseTag next : entity.getTags()) {
retVal.add(next.getTag().toTag());
}
return retVal;
}
}
Set<Long> tagIds = new HashSet<Long>();
findMatchingTagIds(resourceName, theResourceId, tagIds, ResourceTag.class);
findMatchingTagIds(resourceName, theResourceId, tagIds, ResourceHistoryTag.class);
if (tagIds.isEmpty()) {
return new TagList();
}
{
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<TagDefinition> cq = builder.createQuery(TagDefinition.class);
Root<TagDefinition> from = cq.from(TagDefinition.class);
cq.where(from.get("myId").in(tagIds));
cq.orderBy(builder.asc(from.get("myScheme")), builder.asc(from.get("myTerm")));
TypedQuery<TagDefinition> q = myEntityManager.createQuery(cq);
q.setMaxResults(getConfig().getHardTagListLimit());
TagList retVal = new TagList();
for (TagDefinition next : q.getResultList()) {
retVal.add(next.toTag());
}
return retVal;
}
}
private void findMatchingTagIds(String theResourceName, IdDt theResourceId, Set<Long> tagIds, Class<? extends BaseTag> entityClass) {
{
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> cq = builder.createTupleQuery();
Root<? extends BaseTag> from = cq.from(entityClass);
cq.multiselect(from.get("myTagId").as(Long.class)).distinct(true);
if (theResourceName != null) {
Predicate typePredicate = builder.equal(from.get("myResourceType"), theResourceName);
if (theResourceId != null) {
cq.where(typePredicate, builder.equal(from.get("myResourceId"), theResourceId.asLong()));
} else {
cq.where(typePredicate);
}
}
TypedQuery<Tuple> query = myEntityManager.createQuery(cq);
for (Tuple next : query.getResultList()) {
tagIds.add(next.get(0, Long.class));
}
}
}
} }

View File

@ -0,0 +1,24 @@
package ca.uhn.fhir.jpa.dao;
public class DaoConfig {
private int myHardSearchLimit = 1000;
private int myHardTagListLimit = 1000;
public int getHardSearchLimit() {
return myHardSearchLimit;
}
public int getHardTagListLimit() {
return myHardTagListLimit;
}
public void setHardSearchLimit(int theHardSearchLimit) {
myHardSearchLimit = theHardSearchLimit;
}
public void setHardTagListLimit(int theHardTagListLimit) {
myHardTagListLimit = theHardTagListLimit;
}
}

View File

@ -33,16 +33,21 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.RuntimeChildResourceDefinition; import ca.uhn.fhir.context.RuntimeChildResourceDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.RuntimeSearchParam; import ca.uhn.fhir.context.RuntimeSearchParam;
import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.jpa.entity.BaseTag;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTable; import ca.uhn.fhir.jpa.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.entity.ResourceHistoryTablePk;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamDate;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamNumber;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken; import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamToken;
import ca.uhn.fhir.jpa.entity.ResourceLink; import ca.uhn.fhir.jpa.entity.ResourceLink;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TagDefinition;
import ca.uhn.fhir.model.api.IPrimitiveDatatype; import ca.uhn.fhir.model.api.IPrimitiveDatatype;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.CodingDt; import ca.uhn.fhir.model.dstu.composite.CodingDt;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.QuantityDt; import ca.uhn.fhir.model.dstu.composite.QuantityDt;
@ -57,6 +62,7 @@ import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.Constants; import ca.uhn.fhir.rest.server.Constants;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
@Transactional(propagation = Propagation.REQUIRED)
public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements IFhirResourceDao<T> { public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements IFhirResourceDao<T> {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDao.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDao.class);
@ -70,61 +76,61 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
private String myResourceName; private String myResourceName;
private Class<T> myResourceType; private Class<T> myResourceType;
@Transactional(propagation = Propagation.REQUIRED, readOnly = true) @Override
public void addTag(IdDt theId, String theScheme, String theTerm, String theLabel) {
BaseHasResource entity = readEntity(theId);
if (entity == null) {
throw new ResourceNotFoundException(theId);
}
for (BaseTag next : new ArrayList<BaseTag>(entity.getTags())) {
if (next.getTag().getScheme().equals(theScheme) && next.getTag().getTerm().equals(theTerm)) {
return;
}
}
TagDefinition def = getTag(theScheme, theTerm, theLabel);
BaseTag newEntity = entity.addTag(def);
myEntityManager.persist(newEntity);
myEntityManager.merge(entity);
}
@Override @Override
public MethodOutcome create(final T theResource) { public MethodOutcome create(final T theResource) {
// final ResourceTable entity = toEntity(theResource);
//
// entity.setPublished(new Date());
// entity.setUpdated(entity.getPublished());
ResourceTable entity = new ResourceTable(); ResourceTable entity = new ResourceTable();
entity.setResourceType(toResourceName(theResource)); entity.setResourceType(toResourceName(theResource));
// final List<ResourceIndexedSearchParamString> stringParams = extractSearchParamStrings(entity, theResource);
// final List<ResourceIndexedSearchParamToken> tokenParams = extractSearchParamTokens(entity, theResource);
// final List<ResourceIndexedSearchParamNumber> numberParams = extractSearchParamNumber(entity, theResource);
// final List<ResourceIndexedSearchParamDate> dateParams = extractSearchParamDates(entity, theResource);
// final List<ResourceLink> links = extractResourceLinks(entity, theResource);
// ourLog.info("Saving links: {}", links);
// TransactionTemplate template = new TransactionTemplate(myPlatformTransactionManager);
// template.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW);
// template.setReadOnly(false);
// template.execute(new TransactionCallback<ResourceTable>() {
// @Override
// public ResourceTable doInTransaction(TransactionStatus theStatus) {
// myEntityManager.persist(entity);
// for (ResourceIndexedSearchParamString next : stringParams) {
// myEntityManager.persist(next);
// }
// for (ResourceIndexedSearchParamToken next : tokenParams) {
// myEntityManager.persist(next);
// }
// for (ResourceIndexedSearchParamNumber next : numberParams) {
// myEntityManager.persist(next);
// }
// for (ResourceIndexedSearchParamDate next : dateParams) {
// myEntityManager.persist(next);
// }
// for (ResourceLink next : links) {
// myEntityManager.persist(next);
// }
updateEntity(theResource, entity, false); updateEntity(theResource, entity, false);
// return entity;
// }
// });
MethodOutcome outcome = toMethodOutcome(entity); MethodOutcome outcome = toMethodOutcome(entity);
return outcome; return outcome;
} }
@Override
public TagList getAllResourceTags() {
return super.getTags(myResourceType, null);
}
public Class<T> getResourceType() { public Class<T> getResourceType() {
return myResourceType; return myResourceType;
} }
@Transactional(propagation = Propagation.REQUIRED) @Override
public TagList getTags(IdDt theResourceId) {
return super.getTags(myResourceType, theResourceId);
}
@Override
public List<T> history() {
return null;
}
@Override
public List<IResource> history(Date theSince, Integer theLimit) {
return super.history(myResourceName, null, theSince, theLimit);
}
@Override @Override
public List<T> history(IdDt theId) { public List<T> history(IdDt theId) {
ArrayList<T> retVal = new ArrayList<T>(); ArrayList<T> retVal = new ArrayList<T>();
@ -142,7 +148,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
} }
try { try {
retVal.add(read(theId)); retVal.add(read(theId.withoutVersion()));
} catch (ResourceNotFoundException e) { } catch (ResourceNotFoundException e) {
// ignore // ignore
} }
@ -154,20 +160,61 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
return retVal; return retVal;
} }
@Override
public List<IResource> history(Long theId, Date theSince, Integer theLimit) {
return super.history(myResourceName, theId, theSince, theLimit);
}
@PostConstruct @PostConstruct
public void postConstruct() { public void postConstruct() {
myResourceName = getContext().getResourceDefinition(myResourceType).getName(); myResourceName = getContext().getResourceDefinition(myResourceType).getName();
} }
@Transactional(propagation = Propagation.REQUIRED)
@Override @Override
public T read(IdDt theId) { public T read(IdDt theId) {
ResourceTable entity = readEntity(theId); BaseHasResource entity = readEntity(theId);
T retVal = toResource(myResourceType, entity); T retVal = toResource(myResourceType, entity);
return retVal; return retVal;
} }
@Override
public BaseHasResource readEntity(IdDt theId) {
BaseHasResource entity = myEntityManager.find(ResourceTable.class, theId.asLong());
if (theId.hasUnqualifiedVersionId()) {
if (entity.getVersion() != theId.getUnqualifiedVersionIdAsLong()) {
entity = null;
}
}
if (entity == null) {
if (theId.hasUnqualifiedVersionId()) {
entity = myEntityManager.find(ResourceHistoryTable.class, new ResourceHistoryTablePk(myResourceName, theId.asLong(), theId.getUnqualifiedVersionIdAsLong()));
}
if (entity == null) {
throw new ResourceNotFoundException(theId);
}
}
return entity;
}
@Override
public void removeTag(IdDt theId, String theScheme, String theTerm) {
BaseHasResource entity = readEntity(theId);
if (entity == null) {
throw new ResourceNotFoundException(theId);
}
for (BaseTag next : new ArrayList<BaseTag>(entity.getTags())) {
if (next.getTag().getScheme().equals(theScheme) && next.getTag().getTerm().equals(theTerm)) {
myEntityManager.remove(next);
entity.getTags().remove(next);
}
}
myEntityManager.merge(entity);
}
@Override @Override
public List<T> search(Map<String, IQueryParameterType> theParams) { public List<T> search(Map<String, IQueryParameterType> theParams) {
SearchParameterMap map = new SearchParameterMap(); SearchParameterMap map = new SearchParameterMap();
@ -178,11 +225,6 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
return search(map); return search(map);
} }
@Override
public List<T> history() {
return null;
}
@Override @Override
public List<T> search(SearchParameterMap theParams) { public List<T> search(SearchParameterMap theParams) {
@ -216,16 +258,6 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
} }
} }
@Override
public List<IResource> history(Date theSince, int theLimit) {
return super.history(myResourceName, null, theSince, theLimit);
}
@Override
public List<IResource> history(Long theId, Date theSince, int theLimit) {
return super.history(myResourceName, theId, theSince, theLimit);
}
@Override @Override
public List<T> search(String theParameterName, IQueryParameterType theValue) { public List<T> search(String theParameterName, IQueryParameterType theValue) {
return search(Collections.singletonMap(theParameterName, theValue)); return search(Collections.singletonMap(theParameterName, theValue));
@ -311,7 +343,6 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
myResourceType = (Class<T>) theTableType; myResourceType = (Class<T>) theTableType;
} }
@Transactional(propagation = Propagation.REQUIRED)
@Override @Override
public MethodOutcome update(final T theResource, final IdDt theId) { public MethodOutcome update(final T theResource, final IdDt theId) {
@ -324,14 +355,12 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
// } // }
// }); // });
final ResourceTable entity = readEntity(theId); final ResourceTable entity = readEntityLatestVersion(theId);
ResourceTable savedEntity = updateEntity(theResource, entity, true); ResourceTable savedEntity = updateEntity(theResource, entity, true);
return toMethodOutcome(savedEntity); return toMethodOutcome(savedEntity);
} }
private Set<Long> addPredicateDate(Set<Long> thePids, List<IQueryParameterType> theOrParams) { private Set<Long> addPredicateDate(Set<Long> thePids, List<IQueryParameterType> theOrParams) {
if (theOrParams == null || theOrParams.isEmpty()) { if (theOrParams == null || theOrParams.isEmpty()) {
return thePids; return thePids;
@ -667,7 +696,7 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
return new HashSet<Long>(q.getResultList()); return new HashSet<Long>(q.getResultList());
} }
private ResourceTable readEntity(IdDt theId) { private ResourceTable readEntityLatestVersion(IdDt theId) {
ResourceTable entity = myEntityManager.find(ResourceTable.class, theId.asLong()); ResourceTable entity = myEntityManager.find(ResourceTable.class, theId.asLong());
if (entity == null) { if (entity == null) {
throw new ResourceNotFoundException(theId); throw new ResourceNotFoundException(theId);
@ -709,13 +738,4 @@ public class FhirResourceDao<T extends IResource> extends BaseFhirDao implements
} }
} }
@Override
protected IFhirResourceDao<? extends IResource> getDao(Class<? extends IResource> theType) {
if (theType.equals(myResourceType)) {
return this;
}
return super.getDao(theType);
}
} }

View File

@ -15,11 +15,13 @@ import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root; import javax.persistence.criteria.Root;
import org.hamcrest.generator.qdox.parser.structs.TagDef;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TagDefinition;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList; import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
@ -109,44 +111,13 @@ public class FhirSystemDao extends BaseFhirDao implements IFhirSystemDao {
} }
@Override @Override
public List<IResource> history(Date theSince, int theLimit) { public List<IResource> history(Date theSince, Integer theLimit) {
return super.history(null, null, theSince, theLimit); return super.history(null, null, theSince, theLimit);
} }
@Override @Override
public TagList getAllTags() { public TagList getAllTags() {
// CriteriaBuilder builder = myEntityManager.getCriteriaBuilder(); return super.getTags(null, null);
// CriteriaQuery<Tuple> cq = builder.createQuery(Tag)
// Root<?> from = cq.from(ResourceTable.class);
// cq.multiselect(from.get("myId").as(Long.class), from.get("myUpdated").as(Date.class));
//
// List<Predicate> predicates = new ArrayList<Predicate>();
// if (theSince != null) {
// Predicate low = builder.greaterThanOrEqualTo(from.<Date> get("myUpdated"), theSince);
// predicates.add(low);
// }
//
// if (theResourceName != null) {
// predicates.add(builder.equal(from.get("myResourceType"), theResourceName));
// }
// if (theId != null) {
// predicates.add(builder.equal(from.get("myId"), theId));
// }
//
// cq.where(builder.and(predicates.toArray(new Predicate[0])));
//
// cq.orderBy(builder.desc(from.get("myUpdated")));
// TypedQuery<Tuple> q = myEntityManager.createQuery(cq);
// if (theLimit > 0) {
// q.setMaxResults(theLimit);
// }
// for (Tuple next : q.getResultList()) {
// long id = (Long) next.get(0);
// Date updated = (Date) next.get(1);
// tuples.add(new HistoryTuple(ResourceTable.class, updated, id));
// }
return null;
} }
} }

View File

@ -5,11 +5,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.BaseHasResource;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.api.MethodOutcome; import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
@ -17,6 +17,20 @@ public interface IFhirResourceDao<T extends IResource> {
MethodOutcome create(T theResource); MethodOutcome create(T theResource);
TagList getAllResourceTags();
Class<T> getResourceType();
TagList getTags(IdDt theResourceId);
List<T> history();
List<IResource> history(Date theDate, Integer theLimit);
List<T> history(IdDt theId);
List<IResource> history(Long theId, Date theSince, Integer theLimit);
/** /**
* *
* @param theId * @param theId
@ -25,28 +39,24 @@ public interface IFhirResourceDao<T extends IResource> {
*/ */
T read(IdDt theId); T read(IdDt theId);
MethodOutcome update(T theResource, IdDt theId); BaseHasResource readEntity(IdDt theId);
List<T> history(IdDt theId);
List<T> search(Map<String, IQueryParameterType> theParams); List<T> search(Map<String, IQueryParameterType> theParams);
List<T> search(String theParameterName, IQueryParameterType theValue);
List<T> search(SearchParameterMap theMap); List<T> search(SearchParameterMap theMap);
Class<T> getResourceType(); List<T> search(String theParameterName, IQueryParameterType theValue);
Set<Long> searchForIds(String theParameterName, IQueryParameterType theValue);
Set<Long> searchForIds(Map<String, IQueryParameterType> theParams); Set<Long> searchForIds(Map<String, IQueryParameterType> theParams);
Set<Long> searchForIds(String theParameterName, IQueryParameterType theValue);
Set<Long> searchForIdsWithAndOr(Map<String, List<List<IQueryParameterType>>> theMap); Set<Long> searchForIdsWithAndOr(Map<String, List<List<IQueryParameterType>>> theMap);
List<T> history(); MethodOutcome update(T theResource, IdDt theId);
List<IResource> history(Date theDate, Integer theLimit); void removeTag(IdDt theId, String theScheme, String theTerm);
List<IResource> history(Long theId, Date theSince, Integer theLimit); void addTag(IdDt theId, String theScheme, String theTerm, String theLabel);
} }

View File

@ -11,7 +11,6 @@ import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal; import javax.persistence.Temporal;
import javax.persistence.TemporalType; import javax.persistence.TemporalType;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.rest.server.EncodingEnum; import ca.uhn.fhir.rest.server.EncodingEnum;
@ -83,4 +82,6 @@ public abstract class BaseHasResource {
myUpdated = theUpdated.getValue(); myUpdated = theUpdated.getValue();
} }
public abstract BaseTag addTag(TagDefinition theDef);
} }

View File

@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.entity;
import java.io.Serializable; import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.JoinColumn; import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne; import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass; import javax.persistence.MappedSuperclass;
@ -15,6 +16,9 @@ public class BaseTag implements Serializable {
@JoinColumn(name="TAG_ID", nullable=false) @JoinColumn(name="TAG_ID", nullable=false)
private TagDefinition myTag; private TagDefinition myTag;
@Column(name="TAG_ID", insertable=false,updatable=false)
private Long myTagId;
public TagDefinition getTag() { public TagDefinition getTag() {
return myTag; return myTag;
} }

View File

@ -92,7 +92,16 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
} }
public void addTag(ResourceTag theTag) { public void addTag(ResourceTag theTag) {
getTags().add(new ResourceHistoryTag(this, theTag.getTag())); ResourceHistoryTag tag = new ResourceHistoryTag(this, theTag.getTag());
tag.setResourceType(theTag.getResourceType());
getTags().add(tag);
}
@Override
public BaseTag addTag(TagDefinition theDef) {
ResourceHistoryTag historyTag = new ResourceHistoryTag(this, theDef);
getTags().add(historyTag);
return historyTag;
} }
} }

View File

@ -19,6 +19,17 @@ public class ResourceHistoryTablePk implements Serializable {
@Column(name = "VERSION", nullable = false) @Column(name = "VERSION", nullable = false)
private Long myVersion; private Long myVersion;
public ResourceHistoryTablePk() {
// nothing
}
public ResourceHistoryTablePk(String theResourceType, Long theResourceId, Long theVersion) {
super();
myResourceType = theResourceType;
myId = theResourceId;
myVersion = theVersion;
}
public Long getId() { public Long getId() {
return myId; return myId;
} }
@ -43,5 +54,4 @@ public class ResourceHistoryTablePk implements Serializable {
myVersion = theVersion; myVersion = theVersion;
} }
} }

View File

@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.entity;
import java.io.Serializable; import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType; import javax.persistence.GenerationType;
@ -29,6 +30,32 @@ public class ResourceHistoryTag extends BaseTag implements Serializable {
}/*, foreignKey=@ForeignKey(name="FK_HT_RT")*/) }/*, foreignKey=@ForeignKey(name="FK_HT_RT")*/)
private ResourceHistoryTable myResourceHistory; private ResourceHistoryTable myResourceHistory;
@Column(name = "RES_TYPE", length = ResourceTable.RESTYPE_LEN,nullable=false, insertable=false, updatable=false)
private String myResourceType;
@Column(name="PID", insertable=false,updatable=false)
private Long myResourceId;
public String getResourceType() {
return myResourceType;
}
public void setResourceType(String theResourceType) {
myResourceType = theResourceType;
}
public Long getResourceId() {
return myResourceId;
}
public void setResourceId(Long theResourceId) {
myResourceId = theResourceId;
}
public ResourceHistoryTag() { public ResourceHistoryTag() {
} }

View File

@ -28,6 +28,8 @@ import ca.uhn.fhir.rest.server.Constants;
@Inheritance(strategy = InheritanceType.JOINED) @Inheritance(strategy = InheritanceType.JOINED)
@org.hibernate.annotations.Table(appliesTo="HFJ_RESOURCE", indexes= {@Index(name="IDX_RES_DATE", columnNames= {"RES_UPDATED"})}) @org.hibernate.annotations.Table(appliesTo="HFJ_RESOURCE", indexes= {@Index(name="IDX_RES_DATE", columnNames= {"RES_UPDATED"})})
public class ResourceTable extends BaseHasResource implements Serializable { public class ResourceTable extends BaseHasResource implements Serializable {
static final int RESTYPE_LEN = 30;
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@Column(name = "SP_HAS_LINKS") @Column(name = "SP_HAS_LINKS")
@ -68,7 +70,7 @@ public class ResourceTable extends BaseHasResource implements Serializable {
@OneToMany(mappedBy = "mySourceResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) @OneToMany(mappedBy = "mySourceResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
private Collection<ResourceLink> myResourceLinks; private Collection<ResourceLink> myResourceLinks;
@Column(name = "RES_TYPE", length = 30) @Column(name = "RES_TYPE", length = RESTYPE_LEN)
private String myResourceType; private String myResourceType;
@OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true) @OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
@ -230,7 +232,10 @@ public class ResourceTable extends BaseHasResource implements Serializable {
return retVal; return retVal;
} }
public void addTag(TagDefinition theTag) { public ResourceTag addTag(TagDefinition theTag) {
getTags().add(new ResourceTag(this, theTag)); ResourceTag tag = new ResourceTag(this, theTag);
tag.setResourceType(getResourceType());
getTags().add(tag);
return tag;
} }
} }

View File

@ -24,6 +24,20 @@ public class ResourceTag extends BaseTag {
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID") @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID")
private ResourceTable myResource; private ResourceTable myResource;
@Column(name = "RES_TYPE", length = ResourceTable.RESTYPE_LEN,nullable=false)
private String myResourceType;
@Column(name="RES_ID", insertable=false,updatable=false)
private Long myResourceId;
public Long getResourceId() {
return myResourceId;
}
public void setResourceId(Long theResourceId) {
myResourceId = theResourceId;
}
public ResourceTag() { public ResourceTag() {
} }
@ -36,8 +50,16 @@ public class ResourceTag extends BaseTag {
return myResource; return myResource;
} }
public String getResourceType() {
return myResourceType;
}
public void setResource(ResourceTable theResource) { public void setResource(ResourceTable theResource) {
myResource = theResource; myResource = theResource;
} }
public void setResourceType(String theResourceType) {
myResourceType = theResourceType;
}
} }

View File

@ -1,15 +1,20 @@
package ca.uhn.fhir.jpa.entity; package ca.uhn.fhir.jpa.entity;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collection;
import javax.persistence.Column; import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType; import javax.persistence.GenerationType;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table; import javax.persistence.Table;
import javax.persistence.UniqueConstraint; import javax.persistence.UniqueConstraint;
import ca.uhn.fhir.model.api.Tag;
@Entity @Entity
@Table(name = "HFJ_TAG_DEF", uniqueConstraints= {@UniqueConstraint(columnNames= {"TAG_SCHEME","TAG_TERM"})}) @Table(name = "HFJ_TAG_DEF", uniqueConstraints= {@UniqueConstraint(columnNames= {"TAG_SCHEME","TAG_TERM"})})
//@org.hibernate.annotations.Table(appliesTo="HFJ_TAG", indexes= {@Index(name)}) //@org.hibernate.annotations.Table(appliesTo="HFJ_TAG", indexes= {@Index(name)})
@ -17,6 +22,12 @@ public class TagDefinition implements Serializable {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@OneToMany(cascade= {}, fetch= FetchType.LAZY, mappedBy= "myTag")
private Collection<ResourceTag> myResources;
@OneToMany(cascade= {}, fetch= FetchType.LAZY, mappedBy= "myTag")
private Collection<ResourceHistoryTag> myResourceVersions;
@Id @Id
@GeneratedValue(strategy = GenerationType.AUTO) @GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "TAG_ID") @Column(name = "TAG_ID")
@ -64,4 +75,8 @@ public class TagDefinition implements Serializable {
myTerm = theTerm; myTerm = theTerm;
} }
public Tag toTag() {
return new Tag( getTerm(), getLabel(),getScheme());
}
} }

View File

@ -10,9 +10,11 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.model.api.IQueryParameterType; import ca.uhn.fhir.model.api.IQueryParameterType;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.annotation.Count; import ca.uhn.fhir.rest.annotation.Count;
import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.GetTags;
import ca.uhn.fhir.rest.annotation.History; import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.annotation.IdParam; import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read; import ca.uhn.fhir.rest.annotation.Read;
@ -49,6 +51,31 @@ public class JpaResourceProvider<T extends IResource> implements IResourceProvid
return myDao; return myDao;
} }
@History
public List<IResource> getHistoryForResourceType(@Since Date theDate, @Count Integer theCount) {
return myDao.history(theDate, theCount);
}
@History
public List<IResource> getHistoryForResourceInstance(@IdParam IdDt theId, @Since Date theDate, @Count Integer theCount) {
return myDao.history(theId.asLong(), theDate, theCount);
}
@Override
public Class<? extends IResource> getResourceType() {
return myDao.getResourceType();
}
@GetTags
public TagList getTagsForResourceType() {
return myDao.getAllResourceTags();
}
@GetTags
public TagList getTagsForResourceInstance(@IdParam IdDt theResourceId) {
return myDao.getTags(theResourceId);
}
@History @History
public List<T> history(@IdParam IdDt theId) { public List<T> history(@IdParam IdDt theId) {
return myDao.history(theId); return myDao.history(theId);
@ -74,19 +101,4 @@ public class JpaResourceProvider<T extends IResource> implements IResourceProvid
return myDao.update(theResource, theId); return myDao.update(theResource, theId);
} }
@Override
public Class<? extends IResource> getResourceType() {
return myDao.getResourceType();
}
@History
public List<IResource> getHistoryServerWithCriteria(@Since Date theDate, @Count Integer theCount) {
return myDao.history(theDate, theCount);
}
@History
public List<IResource> getHistoryServerWithCriteria(@IdParam IdDt theId, @Since Date theDate, @Count Integer theCount) {
return myDao.history(theId.asLong(), theDate, theCount);
}
} }

View File

@ -6,9 +6,10 @@ import java.util.List;
import org.springframework.beans.factory.annotation.Required; import org.springframework.beans.factory.annotation.Required;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.rest.annotation.Count; import ca.uhn.fhir.rest.annotation.Count;
import ca.uhn.fhir.rest.annotation.GetTags;
import ca.uhn.fhir.rest.annotation.History; import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.annotation.Since; import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.annotation.Transaction; import ca.uhn.fhir.rest.annotation.Transaction;
@ -38,7 +39,13 @@ public class JpaSystemProvider {
} }
@History @History
public List<IResource> getHistoryServerWithCriteria(@Since Date theDate, @Count Integer theCount) { public List<IResource> historyServer(@Since Date theDate, @Count Integer theCount) {
return myDao.history(theDate, theCount); return myDao.history(theDate, theCount);
} }
@GetTags
public TagList getAllTagsOnServer() {
return myDao.getAllTags();
}
} }

View File

@ -413,8 +413,8 @@ public class FhirResourceDaoTest {
patient.addIdentifier("urn:system", "testTagsWithCreateAndReadAndSearch"); patient.addIdentifier("urn:system", "testTagsWithCreateAndReadAndSearch");
patient.addName().addFamily("Tester").addGiven("Joe"); patient.addName().addFamily("Tester").addGiven("Joe");
TagList tagList = new TagList(); TagList tagList = new TagList();
tagList.addTag("Dog", "Puppies", null); tagList.addTag(null, "Dog", "Puppies");
tagList.addTag("Cat", "Kittens", "http://foo"); tagList.addTag("http://foo", "Cat", "Kittens");
patient.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList); patient.getResourceMetadata().put(ResourceMetadataKeyEnum.TAG_LIST, tagList);
MethodOutcome outcome = ourPatientDao.create(patient); MethodOutcome outcome = ourPatientDao.create(patient);
@ -475,7 +475,7 @@ public class FhirResourceDaoTest {
Date now2 = new Date(); Date now2 = new Date();
Patient retrieved2 = ourPatientDao.read(outcome.getId()); Patient retrieved2 = ourPatientDao.read(outcome.getId().withoutVersion());
assertEquals("2", retrieved2.getId().getUnqualifiedVersionId()); assertEquals("2", retrieved2.getId().getUnqualifiedVersionId());
assertEquals("002", retrieved2.getIdentifierFirstRep().getValue().getValue()); assertEquals("002", retrieved2.getIdentifierFirstRep().getValue().getValue());
@ -490,7 +490,11 @@ public class FhirResourceDaoTest {
*/ */
List<Patient> history = ourPatientDao.history(outcome.getId()); List<Patient> history = ourPatientDao.history(outcome.getId());
assertEquals(2, history.size()); assertEquals(2, history.size());
assertEquals("1", history.get(0).getId().getUnqualifiedVersionId());
assertEquals("2", history.get(1).getId().getUnqualifiedVersionId());
assertEquals(published, history.get(0).getResourceMetadata().get(ResourceMetadataKeyEnum.PUBLISHED));
assertEquals(published, history.get(0).getResourceMetadata().get(ResourceMetadataKeyEnum.PUBLISHED)); assertEquals(published, history.get(0).getResourceMetadata().get(ResourceMetadataKeyEnum.PUBLISHED));
assertEquals(updated, history.get(0).getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED)); assertEquals(updated, history.get(0).getResourceMetadata().get(ResourceMetadataKeyEnum.UPDATED));
assertEquals("001", history.get(0).getIdentifierFirstRep().getValue().getValue()); assertEquals("001", history.get(0).getIdentifierFirstRep().getValue().getValue());
@ -518,7 +522,7 @@ public class FhirResourceDaoTest {
// Update the name // Update the name
p1.getNameFirstRep().getGivenFirstRep().setValue("testUpdateMaintainsSearchParamsBBB"); p1.getNameFirstRep().getGivenFirstRep().setValue("testUpdateMaintainsSearchParamsBBB");
ourPatientDao.update(p1, p1id); IdDt p1id2 = ourPatientDao.update(p1, p1id).getId();
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsAAA")); ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsAAA"));
assertEquals(0, ids.size()); assertEquals(0, ids.size());
@ -526,6 +530,13 @@ public class FhirResourceDaoTest {
ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsBBB")); ids = ourPatientDao.searchForIds(Patient.SP_GIVEN, new StringDt("testUpdateMaintainsSearchParamsBBB"));
assertEquals(2, ids.size()); assertEquals(2, ids.size());
// Make sure vreads work
p1 = ourPatientDao.read(p1id);
assertEquals("testUpdateMaintainsSearchParamsAAA", p1.getNameFirstRep().getGivenAsSingleString());
p1 = ourPatientDao.read(p1id2);
assertEquals("testUpdateMaintainsSearchParamsBBB", p1.getNameFirstRep().getGivenAsSingleString());
} }
@AfterClass @AfterClass

View File

@ -18,6 +18,8 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.model.dstu.composite.IdentifierDt; import ca.uhn.fhir.model.dstu.composite.IdentifierDt;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Device; import ca.uhn.fhir.model.dstu.resource.Device;
@ -88,6 +90,80 @@ public class FhirSystemDaoTest {
} }
@Test
public void testTagOperationss() throws Exception {
TagList preSystemTl = ourSystemDao.getAllTags();
TagList tl1 = new TagList();
tl1.addTag("testGetAllTagsScheme1", "testGetAllTagsTerm1", "testGetAllTagsLabel1");
Patient p1 = new Patient();
p1.addIdentifier("foo", "testGetAllTags01");
ResourceMetadataKeyEnum.TAG_LIST.put(p1, tl1);
ourPatientDao.create(p1);
TagList tl2 = new TagList();
tl2.addTag("testGetAllTagsScheme2", "testGetAllTagsTerm2", "testGetAllTagsLabel2");
Observation o1 = new Observation();
o1.getName().setText("testGetAllTags02");
ResourceMetadataKeyEnum.TAG_LIST.put(o1, tl2);
IdDt o1id = ourObservationDao.create(o1).getId();
assertTrue(o1id.getUnqualifiedVersionId() != null);
TagList postSystemTl = ourSystemDao.getAllTags();
assertEquals(preSystemTl.size() + 2, postSystemTl.size());
assertEquals("testGetAllTagsLabel1", postSystemTl.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1").getLabel());
TagList tags = ourPatientDao.getAllResourceTags();
assertEquals("testGetAllTagsLabel1", tags.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1").getLabel());
assertNull(tags.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
TagList tags2 = ourObservationDao.getTags(o1id);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertEquals("testGetAllTagsLabel2", tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2").getLabel());
o1.getResourceMetadata().remove(ResourceMetadataKeyEnum.TAG_LIST);
IdDt o1id2 = ourObservationDao.update(o1, o1id).getId();
assertTrue(o1id2.getUnqualifiedVersionId() != null);
tags2 = ourObservationDao.getTags(o1id);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertEquals("testGetAllTagsLabel2", tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2").getLabel());
tags2 = ourObservationDao.getTags(o1id2);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNotNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
/*
* Remove a tag from a version
*/
ourObservationDao.removeTag(o1id2, "testGetAllTagsScheme2", "testGetAllTagsTerm2");
tags2 = ourObservationDao.getTags(o1id2);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
tags2 = ourObservationDao.getTags(o1id);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNotNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
/*
* Add a tag
*/
ourObservationDao.addTag(o1id2, "testGetAllTagsScheme3", "testGetAllTagsTerm3", "testGetAllTagsLabel3");
tags2 = ourObservationDao.getTags(o1id2);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
assertNotNull(tags2.getTag("testGetAllTagsScheme3", "testGetAllTagsTerm3"));
assertEquals("testGetAllTagsLabel3", tags2.getTag("testGetAllTagsScheme3", "testGetAllTagsTerm3").getLabel());
tags2 = ourObservationDao.getTags(o1id);
assertNull(tags2.getTag("testGetAllTagsScheme1", "testGetAllTagsTerm1"));
assertNotNull(tags2.getTag("testGetAllTagsScheme2", "testGetAllTagsTerm2"));
}
@Test @Test
public void testTransactionFromBundle() throws Exception { public void testTransactionFromBundle() throws Exception {

View File

@ -13,6 +13,9 @@
<context:annotation-config /> <context:annotation-config />
<context:mbean-server /> <context:mbean-server />
<bean id="myDaoConfig" class="ca.uhn.fhir.jpa.dao.DaoConfig">
</bean>
<bean id="mySystemDao" class="ca.uhn.fhir.jpa.dao.FhirSystemDao"> <bean id="mySystemDao" class="ca.uhn.fhir.jpa.dao.FhirSystemDao">
</bean> </bean>

View File

@ -0,0 +1,21 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<logger name="org.eclipse" additivity="false">
</logger>
<logger name="ca.uhn.fhir.rest.client" additivity="false" level="trace">
<appender-ref ref="STDOUT" />
</logger>
<root level="info">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -19,6 +19,8 @@
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>
</attributes> </attributes>
</classpathentry> </classpathentry>
<classpathentry combineaccessrules="false" kind="src" path="/hapi-fhir-base"/>
<classpathentry combineaccessrules="false" kind="src" path="/hapi-fhir-jpaserver-base"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
<attributes> <attributes>
<attribute name="maven.pomderived" value="true"/> <attribute name="maven.pomderived" value="true"/>

View File

@ -17,18 +17,18 @@ public class JpaTestApp {
@SuppressWarnings({ "unused", "unchecked" }) @SuppressWarnings({ "unused", "unchecked" })
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext appCtx = new ClassPathXmlApplicationContext("fhir-spring-test-config.xml"); // ClassPathXmlApplicationContext appCtx = new ClassPathXmlApplicationContext("fhir-spring-test-config.xml");
//
IFhirResourceDao<Patient> patientDao = appCtx.getBean("myPatientDao", IFhirResourceDao.class); // IFhirResourceDao<Patient> patientDao = appCtx.getBean("myPatientDao", IFhirResourceDao.class);
PatientResourceProvider patientRp = new PatientResourceProvider(); // PatientResourceProvider patientRp = new PatientResourceProvider();
patientRp.setDao(patientDao); // patientRp.setDao(patientDao);
//
IFhirResourceDao<Questionnaire> questionnaireDao = appCtx.getBean("myQuestionnaireDao", IFhirResourceDao.class); // IFhirResourceDao<Questionnaire> questionnaireDao = appCtx.getBean("myQuestionnaireDao", IFhirResourceDao.class);
QuestionnaireResourceProvider questionnaireRp = new QuestionnaireResourceProvider(); // QuestionnaireResourceProvider questionnaireRp = new QuestionnaireResourceProvider();
questionnaireRp.setDao(questionnaireDao); // questionnaireRp.setDao(questionnaireDao);
//
RestfulServer restServer = new RestfulServer(); // RestfulServer restServer = new RestfulServer();
restServer.setResourceProviders(patientRp, questionnaireRp); // restServer.setResourceProviders(patientRp, questionnaireRp);
int myPort = 8888; int myPort = 8888;
Server server = new Server(myPort); Server server = new Server(myPort);
@ -37,15 +37,16 @@ public class JpaTestApp {
proxyHandler.setContextPath("/"); proxyHandler.setContextPath("/");
RestfulServerTesterServlet testerServlet = new RestfulServerTesterServlet(); RestfulServerTesterServlet testerServlet = new RestfulServerTesterServlet();
testerServlet.setServerBase("http://localhost:" + myPort + "/fhir/context"); // testerServlet.setServerBase("http://localhost:" + myPort + "/fhir/context");
// testerServlet.setServerBase("http://fhir.healthintersections.com.au/open"); testerServlet.setServerBase("http://fhir.healthintersections.com.au/open");
ServletHolder handler = new ServletHolder(); ServletHolder handler = new ServletHolder();
handler.setName("Tester");
handler.setServlet(testerServlet); handler.setServlet(testerServlet);
proxyHandler.addServlet(handler, "/fhir/tester/*"); proxyHandler.addServlet(handler, "/fhir/tester/*");
ServletHolder servletHolder = new ServletHolder(); // ServletHolder servletHolder = new ServletHolder();
servletHolder.setServlet(restServer); // servletHolder.setServlet(restServer);
proxyHandler.addServlet(servletHolder, "/fhir/context/*"); // proxyHandler.addServlet(servletHolder, "/fhir/context/*");
server.setHandler(proxyHandler); server.setHandler(proxyHandler);
server.start(); server.start();

View File

@ -13,6 +13,7 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt; import ca.uhn.fhir.model.dstu.composite.ResourceReferenceDt;
import ca.uhn.fhir.model.dstu.resource.Observation; import ca.uhn.fhir.model.dstu.resource.Observation;
@ -157,7 +158,7 @@ public class CompleteResourceProviderTest {
RestfulServer restServer = new RestfulServer(); RestfulServer restServer = new RestfulServer();
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp); restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
int myPort = 8888; int myPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(myPort); ourServer = new Server(myPort);
ServletContextHandler proxyHandler = new ServletContextHandler(); ServletContextHandler proxyHandler = new ServletContextHandler();

View File

@ -16,6 +16,7 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.dao.IFhirSystemDao; import ca.uhn.fhir.jpa.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.provider.JpaSystemProvider; import ca.uhn.fhir.jpa.provider.JpaSystemProvider;
import ca.uhn.fhir.jpa.testutil.RandomServerPortProvider;
import ca.uhn.fhir.model.api.Bundle; import ca.uhn.fhir.model.api.Bundle;
import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.dstu.resource.Observation; import ca.uhn.fhir.model.dstu.resource.Observation;
@ -82,7 +83,7 @@ public class SystemTest {
restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp); restServer.setResourceProviders(patientRp, questionnaireRp, observationRp, organizationRp);
restServer.setPlainProviders(new JpaSystemProvider(systemDao)); restServer.setPlainProviders(new JpaSystemProvider(systemDao));
int myPort = 8888; int myPort = RandomServerPortProvider.findFreePort();
ourServer = new Server(myPort); ourServer = new Server(myPort);
ServletContextHandler proxyHandler = new ServletContextHandler(); ServletContextHandler proxyHandler = new ServletContextHandler();

View File

@ -0,0 +1,36 @@
package ca.uhn.fhir.jpa.testutil;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList;
import java.util.List;
/**
* Provides server ports
*/
public class RandomServerPortProvider {
private static List<Integer> ourPorts = new ArrayList<Integer>();
public static int findFreePort() {
ServerSocket server;
try {
server = new ServerSocket(0);
int port = server.getLocalPort();
ourPorts.add(port);
server.close();
Thread.sleep(500);
return port;
} catch (IOException e) {
throw new Error(e);
} catch (InterruptedException e) {
throw new Error(e);
}
}
public static List<Integer> list() {
return ourPorts;
}
}

View File

@ -13,6 +13,12 @@
<context:annotation-config /> <context:annotation-config />
<context:mbean-server /> <context:mbean-server />
<bean id="myDaoConfig" class="ca.uhn.fhir.jpa.dao.DaoConfig">
</bean>
<bean id="mySystemDao" class="ca.uhn.fhir.jpa.dao.FhirSystemDao">
</bean>
<bean id="myPatientDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao"> <bean id="myPatientDao" class="ca.uhn.fhir.jpa.dao.FhirResourceDao">
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Patient"/> <property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Patient"/>
</bean> </bean>
@ -29,9 +35,6 @@
<property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Questionnaire"/> <property name="resourceType" value="ca.uhn.fhir.model.dstu.resource.Questionnaire"/>
</bean> </bean>
<bean id="mySystemDao" class="ca.uhn.fhir.jpa.dao.FhirSystemDao">
</bean>
<bean id="myPersistenceDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true"> <bean id="myPersistenceDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" lazy-init="true">
<property name="url" value="jdbc:derby:memory:myUnitTestDB;create=true" /> <property name="url" value="jdbc:derby:memory:myUnitTestDB;create=true" />
<!-- <property name="url" value="jdbc:derby:directory:myUnitTestDB;create=true" /> --> <!-- <property name="url" value="jdbc:derby:directory:myUnitTestDB;create=true" /> -->

View File

@ -15,6 +15,9 @@
<!-- <import resource="classpath:hapi-jpaserver-springbeans.xml" /> --> <!-- <import resource="classpath:hapi-jpaserver-springbeans.xml" /> -->
<bean id="myDaoConfig" class="ca.uhn.fhir.jpa.dao.DaoConfig">
</bean>
<bean id="mySystemDao" class="ca.uhn.fhir.jpa.dao.FhirSystemDao"> <bean id="mySystemDao" class="ca.uhn.fhir.jpa.dao.FhirSystemDao">
</bean> </bean>