diff --git a/hapi-fhir-base/src/changes/changes.xml b/hapi-fhir-base/src/changes/changes.xml index 4861d6929e0..b746f3e3d80 100644 --- a/hapi-fhir-base/src/changes/changes.xml +++ b/hapi-fhir-base/src/changes/changes.xml @@ -72,6 +72,10 @@ is "yyyy-mm-dd" anyhow, and this is correctly handled). Thanks to Jeffrey Ting of Systems Made Simple for reporting! + + Server now adds a profile tag to returned results if the resource being returned + doesn't already have one + diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/TagList.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/TagList.java index ede83612067..9818c57a9c7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/TagList.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/model/api/TagList.java @@ -48,4 +48,14 @@ public class TagList extends ArrayList { return null; } + public ArrayList getTagsWithScheme(String theScheme) { + ArrayList retVal = new ArrayList(); + for (Tag next : this) { + if (theScheme.equals(next.getScheme())) { + retVal.add(next); + } + } + return retVal; + } + } diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java index 7a7a6f301c4..2e71ec2d5e7 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/Constants.java @@ -97,6 +97,7 @@ public class Constants { public static final int STATUS_HTTP_500_INTERNAL_ERROR = 500; public static final int STATUS_HTTP_501_NOT_IMPLEMENTED = 501; public static final String URL_TOKEN_HISTORY = "_history"; + public static final String TAG_SCHEME_PROFILE = "http://hl7.org/fhir/tag/profile "; static { diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index eb199fd7d91..706087a6522 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -55,6 +55,7 @@ import org.apache.commons.lang3.exception.ExceptionUtils; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.model.api.BundleEntry; import ca.uhn.fhir.model.api.IResource; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.model.api.Tag; @@ -846,17 +847,35 @@ public class RestfulServer extends HttpServlet { addedResources.addAll(addedResourcesThisPass); } while (references.isEmpty() == false); - bundle.addResource(next, theContext, theServerBase); + + BundleEntry entry = bundle.addResource(next, theContext, theServerBase); + addProfileToBundleEntry(theContext, next, entry); + } + /* + * Actually add the resources to the bundle + */ for (IResource next : addedResources) { - bundle.addResource(next, theContext, theServerBase); + BundleEntry entry = bundle.addResource(next, theContext, theServerBase); + addProfileToBundleEntry(theContext, next, entry); } bundle.getTotalResults().setValue(theTotalResults); return bundle; } + private static void addProfileToBundleEntry(FhirContext theContext, IResource next, BundleEntry entry) { + ArrayList profileTags = entry.getCategories().getTagsWithScheme(Constants.TAG_SCHEME_PROFILE); + if (profileTags.isEmpty()) { + RuntimeResourceDefinition nextDef = theContext.getResourceDefinition(next); + String profile = nextDef.getResourceProfile(); + if (isNotBlank(profile)) { + entry.addCategory(new Tag(Constants.TAG_SCHEME_PROFILE, profile, null)); + } + } + } + public static String createPagingLink(String theServerBase, String theSearchId, int theOffset, int theCount, EncodingEnum theResponseEncoding, boolean thePrettyPrint) { StringBuilder b = new StringBuilder(); b.append(theServerBase); diff --git a/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/CustomTypeTest.java b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/CustomTypeTest.java new file mode 100644 index 00000000000..63a450ff89e --- /dev/null +++ b/hapi-fhir-base/src/test/java/ca/uhn/fhir/rest/server/CustomTypeTest.java @@ -0,0 +1,140 @@ +package ca.uhn.fhir.rest.server; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import org.apache.commons.io.IOUtils; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicNameValuePair; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.model.api.Bundle; +import ca.uhn.fhir.model.api.BundleEntry; +import ca.uhn.fhir.model.api.IResource; +import ca.uhn.fhir.model.api.Tag; +import ca.uhn.fhir.model.api.annotation.ResourceDef; +import ca.uhn.fhir.model.dstu.composite.CodingDt; +import ca.uhn.fhir.model.dstu.resource.Observation; +import ca.uhn.fhir.model.dstu.resource.Patient; +import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; +import ca.uhn.fhir.rest.annotation.OptionalParam; +import ca.uhn.fhir.rest.annotation.RequiredParam; +import ca.uhn.fhir.rest.annotation.Search; +import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.param.TokenOrListParam; +import ca.uhn.fhir.testutil.RandomServerPortProvider; + +/** + * Created by dsotnikov on 2/25/2014. + */ +public class CustomTypeTest { + + private static CloseableHttpClient ourClient; + private static FhirContext ourCtx = new FhirContext(ExtendedPatient.class); + private static int ourPort; + private static Server ourServer; + + + @Test + public void testSearchReturnsProfile() throws Exception { + HttpGet httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient?_id=aaa"); + HttpResponse status = ourClient.execute(httpGet); + String responseContent = IOUtils.toString(status.getEntity().getContent()); + IOUtils.closeQuietly(status.getEntity().getContent()); + assertEquals(200, status.getStatusLine().getStatusCode()); + Bundle bundle = ourCtx.newXmlParser().parseBundle(responseContent); + assertEquals(1, bundle.getEntries().size()); + + BundleEntry entry = bundle.getEntries().get(0); + ArrayList profileTags = entry.getCategories().getTagsWithScheme(Constants.TAG_SCHEME_PROFILE); + assertEquals(1, profileTags.size()); + assertEquals("http://foo/profiles/Profile", profileTags.get(0).getTerm()); + + Patient p = (Patient) bundle.getEntries().get(0).getResource(); + assertEquals("idaaa", p.getNameFirstRep().getFamilyAsSingleString()); + + } + + + + @AfterClass + public static void afterClass() throws Exception { + ourServer.stop(); + } + + @BeforeClass + public static void beforeClass() throws Exception { + ourPort = RandomServerPortProvider.findFreePort(); + ourServer = new Server(ourPort); + + DummyPatientResourceProvider patientProvider = new DummyPatientResourceProvider(); + + ServletHandler proxyHandler = new ServletHandler(); + RestfulServer servlet = new RestfulServer(); + servlet.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); + + servlet.setResourceProviders(patientProvider); + ServletHolder servletHolder = new ServletHolder(servlet); + proxyHandler.addServletWithMapping(servletHolder, "/*"); + ourServer.setHandler(proxyHandler); + ourServer.start(); + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(5000, TimeUnit.MILLISECONDS); + HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setConnectionManager(connectionManager); + ourClient = builder.build(); + + } + + + @ResourceDef(name="Patient", profile="http://foo/profiles/Profile") + public static class ExtendedPatient extends Patient { + + + + } + + /** + * Created by dsotnikov on 2/25/2014. + */ + public static class DummyPatientResourceProvider implements IResourceProvider { + + @Search + public List findPatient(@OptionalParam(name = "_id") StringParam theParam) { + ArrayList retVal = new ArrayList(); + + ExtendedPatient patient = new ExtendedPatient(); + patient.setId("1"); + patient.addIdentifier("system", "identifier123"); + if (theParam != null) { + patient.addName().addFamily("id" + theParam.getValue()); + } + retVal.add(patient); + return retVal; + } + + @Override + public Class getResourceType() { + return Patient.class; + } + + } + +}