diff --git a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java index 0e1b2d4e9f7..c63aeb6166a 100644 --- a/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java +++ b/hapi-fhir-jaxrsserver-base/src/main/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProvider.java @@ -46,6 +46,7 @@ import ca.uhn.fhir.rest.api.server.IRestfulResponse; import ca.uhn.fhir.rest.server.*; import ca.uhn.fhir.rest.server.method.BaseMethodBinding; import ca.uhn.fhir.util.ReflectionUtil; +import java.util.stream.Collectors; /** * This is the conformance provider for the jax rs servers. It requires all providers to be registered during startup because the conformance profile is generated during the postconstruct phase. @@ -119,7 +120,8 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv return; } - for (Entry, IResourceProvider> provider : getProviders().entrySet()) { + ConcurrentHashMap, IResourceProvider> providers = getProviders(); + for (Entry, IResourceProvider> provider : providers.entrySet()) { addProvider(provider.getValue(), provider.getKey()); } List> serverBindings = new ArrayList>(); @@ -128,6 +130,7 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv } serverConfiguration.setServerBindings(serverBindings); serverConfiguration.setResourceBindings(new LinkedList(myResourceNameToBinding.values())); + serverConfiguration.computeSharedSupertypeForResourcePerName(providers.values()); HardcodedServerAddressStrategy hardcodedServerAddressStrategy = new HardcodedServerAddressStrategy(); hardcodedServerAddressStrategy.setValue(getBaseForServer()); serverConfiguration.setServerAddressStrategy(hardcodedServerAddressStrategy); diff --git a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java index da4cae5a22b..553c196d8b0 100644 --- a/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java +++ b/hapi-fhir-jaxrsserver-base/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java @@ -1,106 +1,107 @@ -package ca.uhn.fhir.jaxrs.server; - -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProvider; -import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProviderDstu3; -import ca.uhn.fhir.rest.api.Constants; -import ca.uhn.fhir.rest.server.IResourceProvider; -import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; -import org.junit.Before; -import org.junit.Test; - -import javax.ws.rs.core.MultivaluedHashMap; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.UriInfo; -import java.net.URI; -import java.util.Arrays; -import java.util.concurrent.ConcurrentHashMap; - -import static org.junit.Assert.*; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class AbstractJaxRsConformanceProviderDstu3Test { - - private static final String BASEURI = "http://basiuri"; - private static final String REQUESTURI = BASEURI + "/metadata"; - AbstractJaxRsConformanceProvider provider; - private ConcurrentHashMap, IResourceProvider> providers; - private ResteasyHttpHeaders headers; - private MultivaluedHashMap queryParameters; - - @Before - public void setUp() throws Exception { - // uri info - queryParameters = new MultivaluedHashMap<>(); - // headers -// headers = new ContainerRequest(new URI(BASEURI), new URI(REQUESTURI), HttpMethod.GET, null, -// new MapPropertiesDelegate()); - headers = new ResteasyHttpHeaders(queryParameters); - - - providers = new ConcurrentHashMap, IResourceProvider>(); - provider = createConformanceProvider(providers); - } - - @Test - public void testConformance() throws Exception { - providers.put(AbstractJaxRsConformanceProvider.class, provider); - providers.put(TestJaxRsDummyPatientProvider.class, new TestJaxRsDummyPatientProvider()); - Response response = createConformanceProvider(providers).conformance(); - System.out.println(response); - } - - @Test - public void testConformanceUsingOptions() throws Exception { - providers.put(AbstractJaxRsConformanceProvider.class, provider); - providers.put(TestJaxRsDummyPatientProvider.class, new TestJaxRsDummyPatientProvider()); - Response response = createConformanceProvider(providers).conformanceUsingOptions(); - System.out.println(response); - } - - @Test - public void testConformanceWithMethods() throws Exception { - providers.put(AbstractJaxRsConformanceProvider.class, provider); - providers.put(TestJaxRsMockPatientRestProviderDstu3.class, new TestJaxRsMockPatientRestProviderDstu3()); - Response response = createConformanceProvider(providers).conformance(); - assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus()); - assertTrue(response.getEntity().toString().contains("\"type\": \"Patient\"")); - assertTrue(response.getEntity().toString().contains("\"someCustomOperation")); - System.out.println(response); - System.out.println(response.getEntity()); - } - - @Test - public void testConformanceInXml() throws Exception { - queryParameters.put(Constants.PARAM_FORMAT, Arrays.asList(Constants.CT_XML)); - providers.put(AbstractJaxRsConformanceProvider.class, provider); - providers.put(TestJaxRsMockPatientRestProviderDstu3.class, new TestJaxRsMockPatientRestProviderDstu3()); - Response response = createConformanceProvider(providers).conformance(); - assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus()); - System.out.println(response.getEntity()); - assertTrue(response.getEntity().toString().contains(" ")); - assertTrue(response.getEntity().toString().contains("\"someCustomOperation")); - System.out.println(response.getEntity()); - } - - private AbstractJaxRsConformanceProvider createConformanceProvider(final ConcurrentHashMap, IResourceProvider> providers) - throws Exception { - AbstractJaxRsConformanceProvider result = new AbstractJaxRsConformanceProvider(FhirContext.forDstu3(), null, null, null) { - @Override - protected ConcurrentHashMap, IResourceProvider> getProviders() { - return providers; - } - }; - // mocks - UriInfo uriInfo = mock(UriInfo.class); - when(uriInfo.getQueryParameters()).thenReturn(queryParameters); - when(uriInfo.getBaseUri()).thenReturn(new URI(BASEURI)); - when(uriInfo.getRequestUri()).thenReturn(new URI(BASEURI + "/foo")); - result.setUriInfo(uriInfo); - result.setHeaders(headers); - result.setUpPostConstruct(); - return result; - } - -} +package ca.uhn.fhir.jaxrs.server; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProviderDstu3; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.server.IResourceProvider; +import org.jboss.resteasy.specimpl.ResteasyHttpHeaders; +import org.junit.Before; +import org.junit.Test; + +import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriInfo; +import java.net.URI; +import java.util.Arrays; +import java.util.concurrent.ConcurrentHashMap; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProviderDstu3; + +public class AbstractJaxRsConformanceProviderDstu3Test { + + private static final String BASEURI = "http://basiuri"; + private static final String REQUESTURI = BASEURI + "/metadata"; + AbstractJaxRsConformanceProvider provider; + private ConcurrentHashMap, IResourceProvider> providers; + private ResteasyHttpHeaders headers; + private MultivaluedHashMap queryParameters; + + @Before + public void setUp() throws Exception { + // uri info + queryParameters = new MultivaluedHashMap<>(); + // headers +// headers = new ContainerRequest(new URI(BASEURI), new URI(REQUESTURI), HttpMethod.GET, null, +// new MapPropertiesDelegate()); + headers = new ResteasyHttpHeaders(queryParameters); + + + providers = new ConcurrentHashMap, IResourceProvider>(); + provider = createConformanceProvider(providers); + } + + @Test + public void testConformance() throws Exception { + providers.put(AbstractJaxRsConformanceProvider.class, provider); + providers.put(TestJaxRsDummyPatientProviderDstu3.class, new TestJaxRsDummyPatientProviderDstu3()); + Response response = createConformanceProvider(providers).conformance(); + System.out.println(response); + } + + @Test + public void testConformanceUsingOptions() throws Exception { + providers.put(AbstractJaxRsConformanceProvider.class, provider); + providers.put(TestJaxRsDummyPatientProviderDstu3.class, new TestJaxRsDummyPatientProviderDstu3()); + Response response = createConformanceProvider(providers).conformanceUsingOptions(); + System.out.println(response); + } + + @Test + public void testConformanceWithMethods() throws Exception { + providers.put(AbstractJaxRsConformanceProvider.class, provider); + providers.put(TestJaxRsMockPatientRestProviderDstu3.class, new TestJaxRsMockPatientRestProviderDstu3()); + Response response = createConformanceProvider(providers).conformance(); + assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus()); + assertTrue(response.getEntity().toString().contains("\"type\": \"Patient\"")); + assertTrue(response.getEntity().toString().contains("\"someCustomOperation")); + System.out.println(response); + System.out.println(response.getEntity()); + } + + @Test + public void testConformanceInXml() throws Exception { + queryParameters.put(Constants.PARAM_FORMAT, Arrays.asList(Constants.CT_XML)); + providers.put(AbstractJaxRsConformanceProvider.class, provider); + providers.put(TestJaxRsMockPatientRestProviderDstu3.class, new TestJaxRsMockPatientRestProviderDstu3()); + Response response = createConformanceProvider(providers).conformance(); + assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus()); + System.out.println(response.getEntity()); + assertTrue(response.getEntity().toString().contains(" ")); + assertTrue(response.getEntity().toString().contains("\"someCustomOperation")); + System.out.println(response.getEntity()); + } + + private AbstractJaxRsConformanceProvider createConformanceProvider(final ConcurrentHashMap, IResourceProvider> providers) + throws Exception { + AbstractJaxRsConformanceProvider result = new AbstractJaxRsConformanceProvider(FhirContext.forDstu3(), null, null, null) { + @Override + protected ConcurrentHashMap, IResourceProvider> getProviders() { + return providers; + } + }; + // mocks + UriInfo uriInfo = mock(UriInfo.class); + when(uriInfo.getQueryParameters()).thenReturn(queryParameters); + when(uriInfo.getBaseUri()).thenReturn(new URI(BASEURI)); + when(uriInfo.getRequestUri()).thenReturn(new URI(BASEURI + "/foo")); + result.setUriInfo(uriInfo); + result.setHeaders(headers); + result.setUpPostConstruct(); + return result; + } + +} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/CommonResourceSupertypeScanner.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/CommonResourceSupertypeScanner.java new file mode 100644 index 00000000000..edfbc244ede --- /dev/null +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/CommonResourceSupertypeScanner.java @@ -0,0 +1,65 @@ +package ca.uhn.fhir.rest.server; + +import ca.uhn.fhir.model.api.annotation.ResourceDef; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Optional; +import org.hl7.fhir.instance.model.api.IBaseResource; + +/** + *
+ * When populating the StructureDefinition links in a capability statement,
+ * it can be useful to know the lowest common superclass for the profiles in use for a given resource name.
+ * This class finds this superclass, by incrementally computing the greatest common sequence of ancestor classes in the class hierarchies of registered resources.
+ * For instance, given the following classes
+ * MyPatient extends Patient
+ * MyPatient2 extends MyPatient
+ * MyPatient3 extends MyPatient
+ * MyPatient4 extends MyPatient3
+ * this class will find the common ancestor sequence "IBaseResource -> Patient -> MyPatient". MyPatient is the lowest common superclass in this hierarchy.
+ * 
+ * + */ +public class CommonResourceSupertypeScanner { + + private List> greatestSharedAncestorsDescending; + private boolean initialized; + + /** + * Recomputes the lowest common superclass by adding a new resource definition to the hierarchy. + * @param resourceClass The resource class to add. + */ + public void register(Class resourceClass) { + List> resourceClassesInHierarchy = new LinkedList<>(); + Class currentClass = resourceClass; + while (IBaseResource.class.isAssignableFrom(currentClass) + && currentClass.getAnnotation(ResourceDef.class) != null) { + resourceClassesInHierarchy.add((Class)currentClass); + currentClass = currentClass.getSuperclass(); + } + Collections.reverse(resourceClassesInHierarchy); + if (initialized) { + for (int i = 0; i < Math.min(resourceClassesInHierarchy.size(), greatestSharedAncestorsDescending.size()); i++) { + if (greatestSharedAncestorsDescending.get(i) != resourceClassesInHierarchy.get(i)) { + greatestSharedAncestorsDescending = greatestSharedAncestorsDescending.subList(0, i); + break; + } + } + } else { + greatestSharedAncestorsDescending = resourceClassesInHierarchy; + initialized = true; + } + } + + /** + * @return The lowest common superclass of currently registered resources. + */ + public Optional> getLowestCommonSuperclass() { + if (!initialized || greatestSharedAncestorsDescending.isEmpty()) { + return Optional.empty(); + } + return Optional.ofNullable(greatestSharedAncestorsDescending.get(greatestSharedAncestorsDescending.size() - 1)); + } + +} diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 3a523b22a12..91876811163 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.rest.server; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -208,6 +208,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer resourceBindings; private List> serverBindings; + private Map> resourceNameToSharedSupertype; private String implementationDescription; private String serverVersion = VersionUtil.getVersion(); private String serverName = "HAPI FHIR"; @@ -87,6 +92,15 @@ public class RestfulServerConfiguration { this.serverBindings = theServerBindings; return this; } + + public Map> getNameToSharedSupertype() { + return resourceNameToSharedSupertype; + } + + public RestfulServerConfiguration setNameToSharedSupertype(Map> resourceNameToSharedSupertype) { + this.resourceNameToSharedSupertype = resourceNameToSharedSupertype; + return this; + } /** * Get the implementationDescription @@ -268,6 +282,34 @@ public class RestfulServerConfiguration { return resourceToMethods; } + /* + * Populates {@link #resourceNameToSharedSupertype} by scanning the given resource providers. Only resource provider getResourceType values + * are taken into account. {@link ProvidesResources} and method return types are deliberately ignored. + * + * Given a resource name, the common superclass for all getResourceType return values for that name's providers is the common superclass + * for all returned/received resources with that name. Since {@link ProvidesResources} resources and method return types must also be + * subclasses of this common supertype, they can't affect the result of this method. + */ + public void computeSharedSupertypeForResourcePerName(Collection providers) { + Map resourceNameToScanner = new HashMap<>(); + + List> providedResourceClasses = providers.stream() + .map(provider -> provider.getResourceType()) + .collect(Collectors.toList()); + providedResourceClasses.stream() + .forEach(resourceClass -> { + RuntimeResourceDefinition baseDefinition = getFhirContext().getResourceDefinition(resourceClass).getBaseDefinition(); + CommonResourceSupertypeScanner scanner = resourceNameToScanner.computeIfAbsent(baseDefinition.getName(), key -> new CommonResourceSupertypeScanner()); + scanner.register(resourceClass); + }); + + resourceNameToSharedSupertype = resourceNameToScanner.entrySet().stream() + .filter(entry -> entry.getValue().getLowestCommonSuperclass().isPresent()) + .collect(Collectors.toMap( + entry -> entry.getKey(), + entry -> entry.getValue().getLowestCommonSuperclass().get())); + } + private String createOperationName(OperationMethodBinding theMethodBinding) { StringBuilder retVal = new StringBuilder(); if (theMethodBinding.getResourceName() != null) { diff --git a/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/CommonResourceSupertypeScannerTest.java b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/CommonResourceSupertypeScannerTest.java new file mode 100644 index 00000000000..6e28a077c77 --- /dev/null +++ b/hapi-fhir-server/src/test/java/ca/uhn/fhir/rest/server/CommonResourceSupertypeScannerTest.java @@ -0,0 +1,132 @@ + +package ca.uhn.fhir.rest.server; + +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.model.api.annotation.ResourceDef; +import java.util.List; +import static org.hamcrest.CoreMatchers.is; +import org.hl7.fhir.instance.model.api.IBaseMetaType; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import static org.junit.Assert.assertThat; +import org.junit.Test; + +public class CommonResourceSupertypeScannerTest { + + private final CommonResourceSupertypeScanner scanner = new CommonResourceSupertypeScanner(); + + @Test + public void testBaseClass() { + scanner.register(DemoPatient.class); + + assertThat(scanner.getLowestCommonSuperclass().get(), is(DemoPatient.class)); + } + + @Test + public void testSubtype() { + scanner.register(DemoPatient.class); + scanner.register(DemoPatientTripleSub.class); + + assertThat(scanner.getLowestCommonSuperclass().get(), is(DemoPatient.class)); + } + + @Test + public void testHierarchyBranch() { + scanner.register(DemoPatientSub.class); + scanner.register(DemoPatientSubSub.class); + scanner.register(DemoPatientSubSubTwo.class); + scanner.register(DemoPatientTripleSub.class); + + assertThat(scanner.getLowestCommonSuperclass().get(), is(DemoPatientSub.class)); + } + + @Test + public void testSupertypeNotRegistered() { + scanner.register(DemoPatientTripleSub.class); + scanner.register(DemoPatientSubSubTwo.class); + + assertThat(scanner.getLowestCommonSuperclass().get(), is(DemoPatientSub.class)); + } + + @Test + public void testOnlySubtype() { + scanner.register(DemoPatientTripleSub.class); + + assertThat(scanner.getLowestCommonSuperclass().get(), is(DemoPatientTripleSub.class)); + } + + @Test + public void testEmpty() { + assertThat(scanner.getLowestCommonSuperclass().isPresent(), is(false)); + } + + @ResourceDef(name = "Patient") + private static class DemoPatient implements IBaseResource { + + @Override + public IBaseMetaType getMeta() { + return null; + } + + @Override + public IIdType getIdElement() { + return null; + } + + @Override + public IBaseResource setId(String theId) { + return null; + } + + @Override + public IBaseResource setId(IIdType theId) { + return null; + } + + @Override + public FhirVersionEnum getStructureFhirVersionEnum() { + return null; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public boolean hasFormatComment() { + return false; + } + + @Override + public List getFormatCommentsPre() { + return null; + } + + @Override + public List getFormatCommentsPost() { + return null; + } + + @Override + public Object getUserData(String theName) { + return null; + } + + @Override + public void setUserData(String theName, Object theValue) { + } + } + + @ResourceDef(id = "subOne") + private static class DemoPatientSub extends DemoPatient {} + + @ResourceDef(id = "subSubOne") + private static class DemoPatientSubSub extends DemoPatientSub {} + + @ResourceDef(id = "subSubTwo") + private static class DemoPatientSubSubTwo extends DemoPatientSub {} + + @ResourceDef(id = "tripleSub") + private static class DemoPatientTripleSub extends DemoPatientSubSub {} +} diff --git a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java index b61650fd57b..7860e22528a 100644 --- a/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java +++ b/hapi-fhir-structures-dstu3/src/main/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProvider.java @@ -34,6 +34,8 @@ import java.util.Map.Entry; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import ca.uhn.fhir.context.FhirContext; + /* * #%L * HAPI FHIR Structures - DSTU2 (FHIR v1.0.0) @@ -113,12 +115,12 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState } private Map>> collectMethodBindings(RequestDetails theRequestDetails) { - Map>> resourceToMethods = new TreeMap>>(); + Map>> resourceToMethods = new TreeMap<>(); for (ResourceBinding next : getServerConfiguration(theRequestDetails).getResourceBindings()) { String resourceName = next.getResourceName(); for (BaseMethodBinding nextMethodBinding : next.getMethodBindings()) { if (resourceToMethods.containsKey(resourceName) == false) { - resourceToMethods.put(resourceName, new ArrayList>()); + resourceToMethods.put(resourceName, new ArrayList<>()); } resourceToMethods.get(resourceName).add(nextMethodBinding); } @@ -231,13 +233,21 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState Set operationNames = new HashSet<>(); Map>> resourceToMethods = collectMethodBindings(theRequestDetails); + Map> resourceNameToSharedSupertype = serverConfiguration.getNameToSharedSupertype(); for (Entry>> nextEntry : resourceToMethods.entrySet()) { if (nextEntry.getKey().isEmpty() == false) { Set resourceOps = new HashSet<>(); CapabilityStatementRestResourceComponent resource = rest.addResource(); String resourceName = nextEntry.getKey(); - RuntimeResourceDefinition def = serverConfiguration.getFhirContext().getResourceDefinition(resourceName); + + RuntimeResourceDefinition def; + FhirContext context = serverConfiguration.getFhirContext(); + if (resourceNameToSharedSupertype.containsKey(resourceName)) { + def = context.getResourceDefinition(resourceNameToSharedSupertype.get(resourceName)); + } else { + def = context.getResourceDefinition(resourceName); + } resource.getTypeElement().setValue(def.getName()); resource.getProfile().setReference((def.getResourceProfile(serverBase))); diff --git a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java index 22c0ea18278..841f14efdc5 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java +++ b/hapi-fhir-structures-dstu3/src/test/java/org/hl7/fhir/dstu3/hapi/rest/server/ServerCapabilityStatementProviderDstu3Test.java @@ -3,6 +3,7 @@ package org.hl7.fhir.dstu3.hapi.rest.server; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.model.api.Include; import ca.uhn.fhir.model.api.annotation.Description; +import ca.uhn.fhir.model.api.annotation.ResourceDef; import ca.uhn.fhir.model.primitive.InstantDt; import ca.uhn.fhir.rest.annotation.*; import ca.uhn.fhir.rest.api.MethodOutcome; @@ -91,7 +92,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new SearchProviderWithExplicitChains()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -131,7 +132,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new ConditionalProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -154,7 +155,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new ProviderWithExtendedOperationReturningBundle()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -181,7 +182,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new ProviderWithExtendedOperationReturningBundle()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs) { + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider() { }; rs.setServerConformanceProvider(sc); @@ -203,7 +204,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new InstanceHistoryProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -222,7 +223,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new MultiOptionalProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -255,7 +256,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new NonConditionalProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -280,7 +281,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new MultiTypePatientProvider(), new MultiTypeEncounterProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -341,14 +342,14 @@ public class ServerCapabilityStatementProviderDstu3Test { assertEquals("Patient", opDef.getParameter().get(0).getType()); } } - + @Test public void testOperationDocumentation() throws Exception { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new SearchProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -367,7 +368,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new PlainProviderWithExtendedOperationOnNoType()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs) { + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider() { @Override public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) { return super.getServerConformance(theRequest, createRequestDetails(rs)); @@ -407,7 +408,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new ProviderWithRequiredAndOptional()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -437,7 +438,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new VreadProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -457,7 +458,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new ReadProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -477,7 +478,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new SearchProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -517,7 +518,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new PatientResourceProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -550,7 +551,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new SearchProviderWithWhitelist()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -583,7 +584,7 @@ public class ServerCapabilityStatementProviderDstu3Test { @Test public void testSearchReferenceParameterWithList() throws Exception { - RestfulServer rsNoType = new RestfulServer(ourCtx) { + RestfulServer rsNoType = new RestfulServer(ourCtx){ @Override public RestfulServerConfiguration createConfiguration() { RestfulServerConfiguration retVal = super.createConfiguration(); @@ -592,7 +593,7 @@ public class ServerCapabilityStatementProviderDstu3Test { } }; rsNoType.registerProvider(new SearchProviderWithListNoType()); - ServerCapabilityStatementProvider scNoType = new ServerCapabilityStatementProvider(rsNoType); + ServerCapabilityStatementProvider scNoType = new ServerCapabilityStatementProvider(); rsNoType.setServerConformanceProvider(scNoType); rsNoType.init(createServletConfig()); @@ -600,7 +601,7 @@ public class ServerCapabilityStatementProviderDstu3Test { String confNoType = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance); ourLog.info(confNoType); - RestfulServer rsWithType = new RestfulServer(ourCtx) { + RestfulServer rsWithType = new RestfulServer(ourCtx){ @Override public RestfulServerConfiguration createConfiguration() { RestfulServerConfiguration retVal = super.createConfiguration(); @@ -609,7 +610,7 @@ public class ServerCapabilityStatementProviderDstu3Test { } }; rsWithType.registerProvider(new SearchProviderWithListWithType()); - ServerCapabilityStatementProvider scWithType = new ServerCapabilityStatementProvider(rsWithType); + ServerCapabilityStatementProvider scWithType = new ServerCapabilityStatementProvider(); rsWithType.setServerConformanceProvider(scWithType); rsWithType.init(createServletConfig()); @@ -627,7 +628,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new SystemHistoryProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -646,7 +647,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new TypeHistoryProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -665,7 +666,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new MultiOptionalProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -682,7 +683,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new NamedQueryPlainProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -726,7 +727,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new NamedQueryResourceProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -759,8 +760,8 @@ public class ServerCapabilityStatementProviderDstu3Test { assertThat(param.getUse(), is(OperationParameterUse.IN)); CapabilityStatementRestResourceComponent patientResource = restComponent.getResource().stream() - .filter(r -> patientResourceName.equals(r.getType())) - .findAny().get(); + .filter(r -> patientResourceName.equals(r.getType())) + .findAny().get(); assertThat("Named query parameters should not appear in the resource search params", patientResource.getSearchParam(), is(empty())); } @@ -769,7 +770,7 @@ public class ServerCapabilityStatementProviderDstu3Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new TypeLevelOperationProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -791,6 +792,26 @@ public class ServerCapabilityStatementProviderDstu3Test { assertThat(opDef.getInstance(), is(false)); } + @Test + public void testProfiledResourceStructureDefinitionLinks() throws Exception { + RestfulServer rs = new RestfulServer(ourCtx); + rs.setResourceProviders(new ProfiledPatientProvider(), new MultipleProfilesPatientProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance)); + + List resources = conformance.getRestFirstRep().getResource(); + CapabilityStatementRestResourceComponent patientResource = resources.stream() + .filter(resource -> "Patient".equals(resource.getType())) + .findFirst().get(); + assertThat(patientResource.getProfile().getReference(), containsString(PATIENT_SUB)); + } + private List toOperationIdParts(List theOperation) { ArrayList retVal = Lists.newArrayList(); for (CapabilityStatementRestOperationComponent next : theOperation) { @@ -822,7 +843,7 @@ public class ServerCapabilityStatementProviderDstu3Test { ValidationResult result = ourValidator.validateWithResult(theOpDef); String outcome = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome()); ourLog.info("Outcome: {}", outcome); - + assertTrue(outcome, result.isSuccessful()); } @@ -883,7 +904,7 @@ public class ServerCapabilityStatementProviderDstu3Test { @Search(type = Patient.class) public Patient findPatient(@Description(shortDefinition = "The patient's identifier") @OptionalParam(name = Patient.SP_IDENTIFIER) TokenParam theIdentifier, - @Description(shortDefinition = "The patient's name") @OptionalParam(name = Patient.SP_NAME) StringParam theName) { + @Description(shortDefinition = "The patient's name") @OptionalParam(name = Patient.SP_NAME) StringParam theName) { return null; } @@ -894,7 +915,7 @@ public class ServerCapabilityStatementProviderDstu3Test { @Operation(name = "someOp") public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, - @OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Encounter theEnd) { + @OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Encounter theEnd) { return null; } @@ -915,7 +936,7 @@ public class ServerCapabilityStatementProviderDstu3Test { @Operation(name = "someOp") public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, - @OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Patient theEnd) { + @OperationParam(name = "someOpParam1") DateType theStart, @OperationParam(name = "someOpParam2") Patient theEnd) { return null; } @@ -959,9 +980,9 @@ public class ServerCapabilityStatementProviderDstu3Test { @SuppressWarnings("unused") public static class PlainProviderWithExtendedOperationOnNoType { - @Operation(name = "plain", idempotent = true, returnParameters = {@OperationParam(min = 1, max = 2, name = "out1", type = StringType.class)}) + @Operation(name = "plain", idempotent = true, returnParameters = { @OperationParam(min = 1, max = 2, name = "out1", type = StringType.class) }) public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, - @OperationParam(name = "end") DateType theEnd) { + @OperationParam(name = "end") DateType theEnd) { return null; } @@ -972,7 +993,7 @@ public class ServerCapabilityStatementProviderDstu3Test { @Operation(name = "everything", idempotent = true) public IBundleProvider everything(javax.servlet.http.HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, - @OperationParam(name = "end") DateType theEnd) { + @OperationParam(name = "end") DateType theEnd) { return null; } @@ -989,8 +1010,8 @@ public class ServerCapabilityStatementProviderDstu3Test { @Description(shortDefinition = "This is a search for stuff!") @Search public List findDiagnosticReportsByPatient(@RequiredParam(name = DiagnosticReport.SP_SUBJECT + '.' + Patient.SP_IDENTIFIER) TokenParam thePatientId, - @OptionalParam(name = DiagnosticReport.SP_CODE) TokenOrListParam theNames, @OptionalParam(name = DiagnosticReport.SP_DATE) DateRangeParam theDateRange, - @IncludeParam(allow = {"DiagnosticReport.result"}) Set theIncludes) throws Exception { + @OptionalParam(name = DiagnosticReport.SP_CODE) TokenOrListParam theNames, @OptionalParam(name = DiagnosticReport.SP_DATE) DateRangeParam theDateRange, + @IncludeParam(allow = { "DiagnosticReport.result" }) Set theIncludes) throws Exception { return null; } @@ -1021,7 +1042,7 @@ public class ServerCapabilityStatementProviderDstu3Test { @Search(type = Patient.class) public Patient findPatient2( - @Description(shortDefinition = "All patients linked to the given patient") @OptionalParam(name = "link", targetTypes = {Patient.class}) ReferenceAndListParam theLink) { + @Description(shortDefinition = "All patients linked to the given patient") @OptionalParam(name = "link", targetTypes = { Patient.class }) ReferenceAndListParam theLink) { return null; } @@ -1031,15 +1052,15 @@ public class ServerCapabilityStatementProviderDstu3Test { public static class SearchProviderWithWhitelist { @Search(type = Patient.class) - public Patient findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = Patient.SP_ORGANIZATION, chainWhitelist = {"foo", - "bar"}) ReferenceAndListParam theIdentifier) { + public Patient findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = Patient.SP_ORGANIZATION, chainWhitelist = { "foo", + "bar" }) ReferenceAndListParam theIdentifier) { return null; } } @SuppressWarnings("unused") - public static class SearchProviderWithListNoType implements IResourceProvider { + public static class SearchProviderWithListNoType implements IResourceProvider { @Override public Class getResourceType() { @@ -1055,7 +1076,7 @@ public class ServerCapabilityStatementProviderDstu3Test { } @SuppressWarnings("unused") - public static class SearchProviderWithListWithType implements IResourceProvider { + public static class SearchProviderWithListWithType implements IResourceProvider { @Override public Class getResourceType() { @@ -1063,7 +1084,7 @@ public class ServerCapabilityStatementProviderDstu3Test { } - @Search(type = Patient.class) + @Search(type=Patient.class) public List findPatient1(@Description(shortDefinition = "The organization at which this person is a patient") @RequiredParam(name = Patient.SP_ORGANIZATION) ReferenceAndListParam theIdentifier) { return null; } @@ -1107,7 +1128,7 @@ public class ServerCapabilityStatementProviderDstu3Test { } } - + public static class TypeLevelOperationProvider implements IResourceProvider { public static final String OPERATION_NAME = "op"; @@ -1158,6 +1179,50 @@ public class ServerCapabilityStatementProviderDstu3Test { public static void afterClassClearContext() { TestUtil.clearAllStaticFieldsForUnitTest(); } + + public static class ProfiledPatientProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return PatientSubSub2.class; + } + + @Search + public List find() { + return null; + } + } + + public static class MultipleProfilesPatientProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return PatientSubSub.class; + } + + @Read(type = PatientTripleSub.class) + public PatientTripleSub read(@IdParam IdType theId) { + return null; + } + + } + + public static final String PATIENT_SUB = "PatientSub"; + public static final String PATIENT_SUB_SUB = "PatientSubSub"; + public static final String PATIENT_SUB_SUB_2 = "PatientSubSub2"; + public static final String PATIENT_TRIPLE_SUB = "PatientTripleSub"; + + @ResourceDef(id = PATIENT_SUB) + public static class PatientSub extends Patient {} + + @ResourceDef(id = PATIENT_SUB_SUB) + public static class PatientSubSub extends PatientSub {} + + @ResourceDef(id = PATIENT_SUB_SUB_2) + public static class PatientSubSub2 extends PatientSub {} + + @ResourceDef(id = PATIENT_TRIPLE_SUB) + public static class PatientTripleSub extends PatientSubSub {} private RequestDetails createRequestDetails(RestfulServer theServer) { ServletRequestDetails retVal = new ServletRequestDetails(null); diff --git a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/ServerCapabilityStatementProvider.java b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/ServerCapabilityStatementProvider.java index 73d83e9f963..d78ec0a3424 100644 --- a/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/ServerCapabilityStatementProvider.java +++ b/hapi-fhir-structures-r4/src/main/java/org/hl7/fhir/r4/hapi/rest/server/ServerCapabilityStatementProvider.java @@ -37,6 +37,8 @@ import java.util.Map.Entry; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import ca.uhn.fhir.context.FhirContext; + /* * #%L * HAPI FHIR Structures - DSTU2 (FHIR v1.0.0) @@ -115,7 +117,6 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState } } - private DateTimeType conformanceDate(RequestDetails theRequestDetails) { IPrimitiveType buildDate = getServerConfiguration(theRequestDetails).getConformanceDate(); if (buildDate != null && buildDate.getValue() != null) { @@ -180,13 +181,21 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState Set operationNames = new HashSet<>(); Map>> resourceToMethods = configuration.collectMethodBindings(); + Map> resourceNameToSharedSupertype = configuration.getNameToSharedSupertype(); for (Entry>> nextEntry : resourceToMethods.entrySet()) { if (nextEntry.getKey().isEmpty() == false) { Set resourceOps = new HashSet<>(); CapabilityStatementRestResourceComponent resource = rest.addResource(); String resourceName = nextEntry.getKey(); - RuntimeResourceDefinition def = configuration.getFhirContext().getResourceDefinition(resourceName); + + RuntimeResourceDefinition def; + FhirContext context = configuration.getFhirContext(); + if (resourceNameToSharedSupertype.containsKey(resourceName)) { + def = context.getResourceDefinition(resourceNameToSharedSupertype.get(resourceName)); + } else { + def = context.getResourceDefinition(resourceName); + } resource.getTypeElement().setValue(def.getName()); resource.getProfileElement().setValue((def.getResourceProfile(serverBase))); diff --git a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4Test.java b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4Test.java index a2fdc92cf4b..c9bdbda1069 100644 --- a/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4Test.java +++ b/hapi-fhir-structures-r4/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR4Test.java @@ -39,6 +39,8 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import ca.uhn.fhir.model.api.annotation.ResourceDef; + public class ServerCapabilityStatementProviderR4Test { private static FhirContext ourCtx; @@ -86,7 +88,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new ConditionalProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -115,7 +117,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new ProviderWithExtendedOperationReturningBundle()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -142,7 +144,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new ProviderWithExtendedOperationReturningBundle()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs) { + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider() { }; rs.setServerConformanceProvider(sc); @@ -164,7 +166,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new InstanceHistoryProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -183,7 +185,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new MultiOptionalProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -216,7 +218,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new NonConditionalProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -239,7 +241,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new MultiTypePatientProvider(), new MultiTypeEncounterProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -307,7 +309,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new SearchProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -326,7 +328,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new PlainProviderWithExtendedOperationOnNoType()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs) { + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider() { @Override public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) { return super.getServerConformance(theRequest, createRequestDetails(rs)); @@ -366,7 +368,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new ProviderWithRequiredAndOptional()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -396,7 +398,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new VreadProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -416,7 +418,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new ReadProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -436,7 +438,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new SearchProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -476,7 +478,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new PatientResourceProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -509,7 +511,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new SearchProviderWithWhitelist()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -551,7 +553,7 @@ public class ServerCapabilityStatementProviderR4Test { } }; rsNoType.registerProvider(new SearchProviderWithListNoType()); - ServerCapabilityStatementProvider scNoType = new ServerCapabilityStatementProvider(rsNoType); + ServerCapabilityStatementProvider scNoType = new ServerCapabilityStatementProvider(); rsNoType.setServerConformanceProvider(scNoType); rsNoType.init(createServletConfig()); @@ -568,7 +570,7 @@ public class ServerCapabilityStatementProviderR4Test { } }; rsWithType.registerProvider(new SearchProviderWithListWithType()); - ServerCapabilityStatementProvider scWithType = new ServerCapabilityStatementProvider(rsWithType); + ServerCapabilityStatementProvider scWithType = new ServerCapabilityStatementProvider(); rsWithType.setServerConformanceProvider(scWithType); rsWithType.init(createServletConfig()); @@ -586,7 +588,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new SystemHistoryProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -605,7 +607,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new TypeHistoryProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -624,7 +626,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new MultiOptionalProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -641,7 +643,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new NamedQueryPlainProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -685,7 +687,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new NamedQueryResourceProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -728,7 +730,7 @@ public class ServerCapabilityStatementProviderR4Test { RestfulServer rs = new RestfulServer(ourCtx); rs.setProviders(new TypeLevelOperationProvider()); - ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs); + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); rs.setServerConformanceProvider(sc); rs.init(createServletConfig()); @@ -749,6 +751,26 @@ public class ServerCapabilityStatementProviderR4Test { assertThat(opDef.getType(), is(true)); assertThat(opDef.getInstance(), is(false)); } + + @Test + public void testProfiledResourceStructureDefinitionLinks() throws Exception { + RestfulServer rs = new RestfulServer(ourCtx); + rs.setResourceProviders(new ProfiledPatientProvider(), new MultipleProfilesPatientProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance)); + + List resources = conformance.getRestFirstRep().getResource(); + CapabilityStatementRestResourceComponent patientResource = resources.stream() + .filter(resource -> "Patient".equals(resource.getType())) + .findFirst().get(); + assertThat(patientResource.getProfile(), containsString(PATIENT_SUB)); + } private List toOperationIdParts(List theOperation) { ArrayList retVal = Lists.newArrayList(); @@ -1107,5 +1129,49 @@ public class ServerCapabilityStatementProviderR4Test { } } + + public static class ProfiledPatientProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return PatientSubSub2.class; + } + + @Search + public List find() { + return null; + } + } + + public static class MultipleProfilesPatientProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return PatientSubSub.class; + } + + @Read(type = PatientTripleSub.class) + public PatientTripleSub read(@IdParam IdType theId) { + return null; + } + + } + + public static final String PATIENT_SUB = "PatientSub"; + public static final String PATIENT_SUB_SUB = "PatientSubSub"; + public static final String PATIENT_SUB_SUB_2 = "PatientSubSub2"; + public static final String PATIENT_TRIPLE_SUB = "PatientTripleSub"; + + @ResourceDef(id = PATIENT_SUB) + public static class PatientSub extends Patient {} + + @ResourceDef(id = PATIENT_SUB_SUB) + public static class PatientSubSub extends PatientSub {} + + @ResourceDef(id = PATIENT_SUB_SUB_2) + public static class PatientSubSub2 extends PatientSub {} + + @ResourceDef(id = PATIENT_TRIPLE_SUB) + public static class PatientTripleSub extends PatientSubSub {} } diff --git a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/ServerCapabilityStatementProvider.java b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/ServerCapabilityStatementProvider.java index 672c11878ee..4cdbb9268e4 100644 --- a/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/ServerCapabilityStatementProvider.java +++ b/hapi-fhir-structures-r5/src/main/java/org/hl7/fhir/r5/hapi/rest/server/ServerCapabilityStatementProvider.java @@ -37,6 +37,8 @@ import java.util.Map.Entry; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; +import ca.uhn.fhir.context.FhirContext; + /* * #%L * HAPI FHIR Structures - DSTU2 (FHIR v1.0.0) @@ -172,13 +174,20 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState Set operationNames = new HashSet<>(); Map>> resourceToMethods = configuration.collectMethodBindings(); + Map> resourceNameToSharedSupertype = configuration.getNameToSharedSupertype(); for (Entry>> nextEntry : resourceToMethods.entrySet()) { if (nextEntry.getKey().isEmpty() == false) { Set resourceOps = new HashSet<>(); CapabilityStatementRestResourceComponent resource = rest.addResource(); String resourceName = nextEntry.getKey(); - RuntimeResourceDefinition def = configuration.getFhirContext().getResourceDefinition(resourceName); + RuntimeResourceDefinition def; + FhirContext context = configuration.getFhirContext(); + if (resourceNameToSharedSupertype.containsKey(resourceName)) { + def = context.getResourceDefinition(resourceNameToSharedSupertype.get(resourceName)); + } else { + def = context.getResourceDefinition(resourceName); + } resource.getTypeElement().setValue(def.getName()); resource.getProfileElement().setValue((def.getResourceProfile(serverBase))); diff --git a/hapi-fhir-structures-r5/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR5Test.java b/hapi-fhir-structures-r5/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR5Test.java index 72fad2ef0b8..2f814a229a8 100644 --- a/hapi-fhir-structures-r5/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR5Test.java +++ b/hapi-fhir-structures-r5/src/test/java/ca/uhn/fhir/rest/server/ServerCapabilityStatementProviderR5Test.java @@ -16,7 +16,6 @@ import ca.uhn.fhir.rest.server.method.SearchMethodBinding; import ca.uhn.fhir.rest.server.method.SearchParameter; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.TestUtil; -import ca.uhn.fhir.validation.FhirValidator; import ca.uhn.fhir.validation.ValidationResult; import com.google.common.collect.Lists; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -40,6 +39,8 @@ import static org.junit.Assert.*; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import ca.uhn.fhir.model.api.annotation.ResourceDef; + public class ServerCapabilityStatementProviderR5Test { private static FhirContext ourCtx; @@ -748,6 +749,26 @@ public class ServerCapabilityStatementProviderR5Test { assertThat(opDef.getInstance(), is(false)); } + @Test + public void testProfiledResourceStructureDefinitionLinks() throws Exception { + RestfulServer rs = new RestfulServer(ourCtx); + rs.setResourceProviders(new ProfiledPatientProvider(), new MultipleProfilesPatientProvider()); + + ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(); + rs.setServerConformanceProvider(sc); + + rs.init(createServletConfig()); + + CapabilityStatement conformance = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs)); + ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(conformance)); + + List resources = conformance.getRestFirstRep().getResource(); + CapabilityStatementRestResourceComponent patientResource = resources.stream() + .filter(resource -> "Patient".equals(resource.getType())) + .findFirst().get(); + assertThat(patientResource.getProfile(), containsString(PATIENT_SUB)); + } + private List toOperationIdParts(List theOperation) { ArrayList retVal = Lists.newArrayList(); for (CapabilityStatementRestResourceOperationComponent next : theOperation) { @@ -1100,4 +1121,48 @@ public class ServerCapabilityStatementProviderR5Test { } + public static class ProfiledPatientProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return PatientSubSub2.class; + } + + @Search + public List find() { + return null; + } + } + + public static class MultipleProfilesPatientProvider implements IResourceProvider { + + @Override + public Class getResourceType() { + return PatientSubSub.class; + } + + @Read(type = PatientTripleSub.class) + public PatientTripleSub read(@IdParam IdType theId) { + return null; + } + + } + + public static final String PATIENT_SUB = "PatientSub"; + public static final String PATIENT_SUB_SUB = "PatientSubSub"; + public static final String PATIENT_SUB_SUB_2 = "PatientSubSub2"; + public static final String PATIENT_TRIPLE_SUB = "PatientTripleSub"; + + @ResourceDef(id = PATIENT_SUB) + public static class PatientSub extends Patient {} + + @ResourceDef(id = PATIENT_SUB_SUB) + public static class PatientSubSub extends PatientSub {} + + @ResourceDef(id = PATIENT_SUB_SUB_2) + public static class PatientSubSub2 extends PatientSub {} + + @ResourceDef(id = PATIENT_TRIPLE_SUB) + public static class PatientTripleSub extends PatientSubSub {} + } diff --git a/pom.xml b/pom.xml index 3bf920a6a74..3d6e20bbcc7 100755 --- a/pom.xml +++ b/pom.xml @@ -1659,6 +1659,7 @@ random -Dfile.encoding=UTF-8 -Xmx1024m 1.0C + false