Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue-1077: Make CapabilityStatementProvider use the closest common s…
…uperclass of provided resources when generating rest.resource.profile, instead of always using the base definition.
- Loading branch information
Showing
13 changed files
with
665 additions
and
194 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
213 changes: 107 additions & 106 deletions
213
...ase/src/test/java/ca/uhn/fhir/jaxrs/server/AbstractJaxRsConformanceProviderDstu3Test.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
|
||
} |
65 changes: 65 additions & 0 deletions
65
hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/CommonResourceSupertypeScanner.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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)); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.