Issue-1077: Make CapabilityStatementProvider use the closest common superclass of provided resources when generating rest.resource.profile, instead of always using the base definition.

This commit is contained in:
Stig Rohde Døssing 2018-10-02 13:12:43 +02:00 committed by James Agnew
parent e29462bcab
commit 80bfb9af37
13 changed files with 662 additions and 193 deletions

View File

@ -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<Class<? extends IResourceProvider>, IResourceProvider> provider : getProviders().entrySet()) {
ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> providers = getProviders();
for (Entry<Class<? extends IResourceProvider>, IResourceProvider> provider : providers.entrySet()) {
addProvider(provider.getValue(), provider.getKey());
}
List<BaseMethodBinding<?>> serverBindings = new ArrayList<BaseMethodBinding<?>>();
@ -128,6 +130,7 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
}
serverConfiguration.setServerBindings(serverBindings);
serverConfiguration.setResourceBindings(new LinkedList<ResourceBinding>(myResourceNameToBinding.values()));
serverConfiguration.computeSharedSupertypeForResourcePerName(providers.values());
HardcodedServerAddressStrategy hardcodedServerAddressStrategy = new HardcodedServerAddressStrategy();
hardcodedServerAddressStrategy.setValue(getBaseForServer());
serverConfiguration.setServerAddressStrategy(hardcodedServerAddressStrategy);

View File

@ -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<Class<? extends IResourceProvider>, IResourceProvider> providers;
private ResteasyHttpHeaders headers;
private MultivaluedHashMap<String, String> 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<Class<? extends IResourceProvider>, 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(" <type value=\"Patient\"/>"));
assertTrue(response.getEntity().toString().contains("\"someCustomOperation"));
System.out.println(response.getEntity());
}
private AbstractJaxRsConformanceProvider createConformanceProvider(final ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> providers)
throws Exception {
AbstractJaxRsConformanceProvider result = new AbstractJaxRsConformanceProvider(FhirContext.forDstu3(), null, null, null) {
@Override
protected ConcurrentHashMap<Class<? extends IResourceProvider>, 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<Class<? extends IResourceProvider>, IResourceProvider> providers;
private ResteasyHttpHeaders headers;
private MultivaluedHashMap<String, String> 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<Class<? extends IResourceProvider>, 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(" <type value=\"Patient\"/>"));
assertTrue(response.getEntity().toString().contains("\"someCustomOperation"));
System.out.println(response.getEntity());
}
private AbstractJaxRsConformanceProvider createConformanceProvider(final ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> providers)
throws Exception {
AbstractJaxRsConformanceProvider result = new AbstractJaxRsConformanceProvider(FhirContext.forDstu3(), null, null, null) {
@Override
protected ConcurrentHashMap<Class<? extends IResourceProvider>, 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;
}
}

View File

@ -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;
/**
* <pre>
* 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.
* </pre>
*
*/
public class CommonResourceSupertypeScanner {
private List<Class<? extends IBaseResource>> 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<? extends IBaseResource> resourceClass) {
List<Class<? extends IBaseResource>> resourceClassesInHierarchy = new LinkedList<>();
Class<?> currentClass = resourceClass;
while (IBaseResource.class.isAssignableFrom(currentClass)
&& currentClass.getAnnotation(ResourceDef.class) != null) {
resourceClassesInHierarchy.add((Class<? extends IBaseResource>)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<Class<? extends IBaseResource>> getLowestCommonSuperclass() {
if (!initialized || greatestSharedAncestorsDescending.isEmpty()) {
return Optional.empty();
}
return Optional.ofNullable(greatestSharedAncestorsDescending.get(greatestSharedAncestorsDescending.size() - 1));
}
}

View File

@ -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<Servlet
} catch (Exception e) {
// fall through
}
result.computeSharedSupertypeForResourcePerName(getResourceProviders());
return result;
}
@ -923,8 +924,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
preProcessedParams.add(HttpServletRequest.class, theRequest);
preProcessedParams.add(HttpServletResponse.class, theResponse);
if (!myInterceptorService.callHooks(Pointcut.SERVER_INCOMING_REQUEST_PRE_PROCESSED, preProcessedParams)) {
return;
}
return;
}
String requestPath = getRequestPath(requestFullPath, servletContextPath, servletPath);
@ -976,8 +977,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
postProcessedParams.add(HttpServletRequest.class, theRequest);
postProcessedParams.add(HttpServletResponse.class, theResponse);
if (!myInterceptorService.callHooks(Pointcut.SERVER_INCOMING_REQUEST_POST_PROCESSED, postProcessedParams)) {
return;
}
return;
}
/*
* Actually invoke the server method. This call is to a HAPI method binding, which
@ -996,7 +997,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
myInterceptorService.callHooks(Pointcut.SERVER_PROCESSING_COMPLETED_NORMALLY, hookParams);
ourLog.trace("Done writing to stream: {}", outputStreamOrWriter);
}
}
} catch (NotModifiedException | AuthenticationException e) {
@ -1007,8 +1008,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
handleExceptionParams.add(HttpServletResponse.class, theResponse);
handleExceptionParams.add(BaseServerResponseException.class, e);
if (!myInterceptorService.callHooks(Pointcut.SERVER_HANDLE_EXCEPTION, handleExceptionParams)) {
return;
}
return;
}
writeExceptionToResponse(theResponse, e);
@ -1063,8 +1064,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
handleExceptionParams.add(HttpServletResponse.class, theResponse);
handleExceptionParams.add(BaseServerResponseException.class, exception);
if (!myInterceptorService.callHooks(Pointcut.SERVER_HANDLE_EXCEPTION, handleExceptionParams)) {
return;
}
return;
}
/*
* If we're handling an exception, no summary mode should be applied

View File

@ -33,11 +33,16 @@ import java.util.*;
import static org.apache.commons.lang3.StringUtils.isBlank;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import java.util.stream.Collectors;
import org.hl7.fhir.instance.model.api.IBaseResource;
public class RestfulServerConfiguration {
private static final Logger ourLog = LoggerFactory.getLogger(RestfulServerConfiguration.class);
private Collection<ResourceBinding> resourceBindings;
private List<BaseMethodBinding<?>> serverBindings;
private Map<String, Class<? extends IBaseResource>> 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<String, Class<? extends IBaseResource>> getNameToSharedSupertype() {
return resourceNameToSharedSupertype;
}
public RestfulServerConfiguration setNameToSharedSupertype(Map<String, Class<? extends IBaseResource>> 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<IResourceProvider> providers) {
Map<String, CommonResourceSupertypeScanner> resourceNameToScanner = new HashMap<>();
List<Class<? extends IBaseResource>> 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) {

View File

@ -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<String> getFormatCommentsPre() {
return null;
}
@Override
public List<String> 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 {}
}

View File

@ -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<String, List<BaseMethodBinding<?>>> collectMethodBindings(RequestDetails theRequestDetails) {
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<String, List<BaseMethodBinding<?>>>();
Map<String, List<BaseMethodBinding<?>>> 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<BaseMethodBinding<?>>());
resourceToMethods.put(resourceName, new ArrayList<>());
}
resourceToMethods.get(resourceName).add(nextMethodBinding);
}
@ -231,13 +233,21 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
Set<String> operationNames = new HashSet<>();
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings(theRequestDetails);
Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype = serverConfiguration.getNameToSharedSupertype();
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
if (nextEntry.getKey().isEmpty() == false) {
Set<TypeRestfulInteraction> 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)));

View File

@ -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<CapabilityStatementRestResourceComponent> 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<String> toOperationIdParts(List<CapabilityStatementRestOperationComponent> theOperation) {
ArrayList<String> 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<DiagnosticReport> 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<Include> theIncludes) throws Exception {
@OptionalParam(name = DiagnosticReport.SP_CODE) TokenOrListParam theNames, @OptionalParam(name = DiagnosticReport.SP_DATE) DateRangeParam theDateRange,
@IncludeParam(allow = { "DiagnosticReport.result" }) Set<Include> 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<? extends IBaseResource> 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<? extends IBaseResource> getResourceType() {
@ -1063,7 +1084,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
}
@Search(type = Patient.class)
@Search(type=Patient.class)
public List<Patient> 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<? extends IBaseResource> getResourceType() {
return PatientSubSub2.class;
}
@Search
public List<PatientSubSub2> find() {
return null;
}
}
public static class MultipleProfilesPatientProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> 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);

View File

@ -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<Date> buildDate = getServerConfiguration(theRequestDetails).getConformanceDate();
if (buildDate != null && buildDate.getValue() != null) {
@ -180,13 +181,21 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
Set<String> operationNames = new HashSet<>();
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = configuration.collectMethodBindings();
Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype = configuration.getNameToSharedSupertype();
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
if (nextEntry.getKey().isEmpty() == false) {
Set<TypeRestfulInteraction> 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)));

View File

@ -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<CapabilityStatementRestResourceComponent> resources = conformance.getRestFirstRep().getResource();
CapabilityStatementRestResourceComponent patientResource = resources.stream()
.filter(resource -> "Patient".equals(resource.getType()))
.findFirst().get();
assertThat(patientResource.getProfile(), containsString(PATIENT_SUB));
}
private List<String> toOperationIdParts(List<CapabilityStatementRestResourceOperationComponent> theOperation) {
ArrayList<String> retVal = Lists.newArrayList();
@ -1107,5 +1129,49 @@ public class ServerCapabilityStatementProviderR4Test {
}
}
public static class ProfiledPatientProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return PatientSubSub2.class;
}
@Search
public List<PatientSubSub2> find() {
return null;
}
}
public static class MultipleProfilesPatientProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> 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 {}
}

View File

@ -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<String> operationNames = new HashSet<>();
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = configuration.collectMethodBindings();
Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype = configuration.getNameToSharedSupertype();
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
if (nextEntry.getKey().isEmpty() == false) {
Set<TypeRestfulInteraction> 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)));

View File

@ -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<CapabilityStatementRestResourceComponent> resources = conformance.getRestFirstRep().getResource();
CapabilityStatementRestResourceComponent patientResource = resources.stream()
.filter(resource -> "Patient".equals(resource.getType()))
.findFirst().get();
assertThat(patientResource.getProfile(), containsString(PATIENT_SUB));
}
private List<String> toOperationIdParts(List<CapabilityStatementRestResourceOperationComponent> theOperation) {
ArrayList<String> retVal = Lists.newArrayList();
for (CapabilityStatementRestResourceOperationComponent next : theOperation) {
@ -1100,4 +1121,48 @@ public class ServerCapabilityStatementProviderR5Test {
}
public static class ProfiledPatientProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return PatientSubSub2.class;
}
@Search
public List<PatientSubSub2> find() {
return null;
}
}
public static class MultipleProfilesPatientProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> 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 {}
}

View File

@ -1659,6 +1659,7 @@
<runOrder>random</runOrder>
<argLine>-Dfile.encoding=UTF-8 -Xmx1024m</argLine>
<forkCount>1.0C</forkCount>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
<plugin>