Add a test

This commit is contained in:
James Agnew 2019-10-24 05:59:41 -05:00
parent 91ec0a6922
commit 74c03b5bb7
6 changed files with 934 additions and 161 deletions

View File

@ -1,100 +0,0 @@
package ca.uhn.fhir.context;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.instance.model.api.IBaseResource;
import ca.uhn.fhir.model.api.annotation.ProvidesResources;
/**
* Scans a class tagged with {@code ProvidesResources} and adds any resources listed to its FhirContext's resource
* definition list. This makes the profile generator find the classes.
*
* @see ca.uhn.fhir.model.api.annotation.ProvidesResources
*/
public class ProvidedResourceScanner {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ProvidedResourceScanner.class);
private FhirContext myContext;
/**
* Constructor
*
* @param theContext
* - context whose resource definition list is to be updated by the scanner
*/
public ProvidedResourceScanner(FhirContext theContext) {
myContext = theContext;
}
/**
* If {@code theProvider} is tagged with the {@code ProvidesResources} annotation, this method will add every
* resource listed by the {@code resources} method.
* <p>
* Notes:
* </p>
* <ul>
* <li>if {@code theProvider} isn't annotated with {@code resources} nothing is done; it's expected that most
* RestfulServers and ResourceProviders won't be annotated.</li>
* <li>any object listed in {@code resources} that doesn't implement {@code IResource} will generate a warning in the
* log.</li>
* </ul>
*
* @param theProvider
* - Normally, either a {@link ca.uhn.fhir.rest.server.RestfulServer} or a
* {@link ca.uhn.fhir.rest.server.IResourceProvider} that might be annotated with
* {@link ca.uhn.fhir.model.api.annotation.ProvidesResources}
*/
@SuppressWarnings("unchecked")
public void scanForProvidedResources(Object theProvider) {
ProvidesResources annotation = theProvider.getClass().getAnnotation(ProvidesResources.class);
if (annotation == null)
return;
for (Class<?> clazz : annotation.resources()) {
if (IBaseResource.class.isAssignableFrom(clazz)) {
myContext.getResourceDefinition((Class<? extends IBaseResource>) clazz);
} else {
ourLog.warn(clazz.getSimpleName() + "is not assignable from IResource");
}
}
}
/**
* Remove any metadata that was added by any {@code ProvidesResources} annotation
* present in {@code theProvider}. This method is callled from {@code RestfulService}
* when it is unregistering a Resource Provider.
*
* @param theProvider
* - Normally a {@link ca.uhn.fhir.rest.server.IResourceProvider} that might
* be annotated with {@link ca.uhn.fhir.model.api.annotation.ProvidesResources}
*/
public void removeProvidedResources(Object theProvider) {
ProvidesResources annotation = theProvider.getClass().getAnnotation(ProvidesResources.class);
if (annotation == null)
return;
for (Class<?> clazz : annotation.resources()) {
if (IBaseResource.class.isAssignableFrom(clazz)) {
// TODO -- not currently used but should be finished for completeness
} else {
ourLog.warn(clazz.getSimpleName() + "is not assignable from IResource");
}
}
}
}

View File

@ -1,43 +0,0 @@
package ca.uhn.fhir.model.api.annotation;
/*
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2019 University Health Network
* %%
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* IResourceProvider and RestfulServer subclasses can use this annotation to designate which custom resources they can provide.
* These resources will automatically be added to the resource list used for profile generation.
* <pre>
* Examples:
* {@literal @}ProvidesResources(resource=CustomObservation.class)
* class CustomObservationResourceProvider implements IResourceProvider{...}
*
* {@literal @}ProvidesResources(resource={CustomPatient.class,CustomObservation.class}){...}
* class FhirServer extends RestfulServer
* }
* </pre>
* Note that you needn't annotate both the IResourceProvider and the RestfulServer for a given resource; either one will suffice.
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface ProvidesResources {
Class<?>[] resources();
}

View File

@ -53,7 +53,7 @@ public class BaseSearchParamRegistryTest {
}
@Test
public void testRefreshCacheIfNeccessary() {
public void testRefreshCacheIfNecessary() {
SearchParamRegistryR4 registry = new SearchParamRegistryR4();
when(mySearchParamProvider.search(any())).thenReturn(new SimpleBundleProvider());
@ -70,6 +70,9 @@ public class BaseSearchParamRegistryTest {
assertTrue(registry.refreshCacheIfNecessary());
assertFalse(registry.refreshCacheIfNecessary());
registry.requestRefresh();
assertTrue(registry.refreshCacheIfNecessary());
}
@Test

View File

@ -22,7 +22,6 @@ package ca.uhn.fhir.rest.server;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.ProvidedResourceScanner;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.context.api.AddProfileTagEnum;
import ca.uhn.fhir.context.api.BundleInclusionRule;
@ -106,7 +105,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
private static final ExceptionHandlingInterceptor DEFAULT_EXCEPTION_HANDLER = new ExceptionHandlingInterceptor();
private static final Logger ourLog = LoggerFactory.getLogger(RestfulServer.class);
private static final long serialVersionUID = 1L;
private static final Random RANDOM = new Random();
private final List<Object> myPlainProviders = new ArrayList<>();
private final List<IResourceProvider> myResourceProviders = new ArrayList<>();
private IInterceptorService myInterceptorService;
@ -667,10 +665,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
Validate.noNullElements(theProviders, "theProviders must not contain any null elements");
myPlainProviders.clear();
if (theProviders != null) {
myPlainProviders.addAll(theProviders);
}
}
/**
* Allows users of RestfulServer to override the getRequestPath method to let them build their custom request path
@ -714,10 +710,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
Validate.noNullElements(theProviders, "theProviders must not contain any null elements");
myResourceProviders.clear();
if (theProviders != null) {
myResourceProviders.addAll(theProviders);
}
}
/**
* Get the server address strategy, which is used to determine what base URL to provide clients to refer to this
@ -959,6 +953,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
for (String string : parts) {
if (string.equals("gzip")) {
respondGzip = true;
break;
}
}
}
@ -1199,9 +1194,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
try {
ourLog.info("Initializing HAPI FHIR restful server running in " + getFhirContext().getVersion().getVersion().name() + " mode");
ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext());
providedResourceScanner.scanForProvidedResources(this);
Collection<IResourceProvider> resourceProvider = getResourceProviders();
// 'true' tells registerProviders() that
// this call is part of initialization
@ -1552,7 +1544,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
List<IResourceProvider> newResourceProviders = new ArrayList<>();
List<Object> newPlainProviders = new ArrayList<>();
ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext());
if (theProviders != null) {
for (Object provider : theProviders) {
@ -1565,7 +1556,6 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
if (!inInit) {
myResourceProviders.add(rsrcProvider);
}
providedResourceScanner.scanForProvidedResources(rsrcProvider);
newResourceProviders.add(rsrcProvider);
} else {
if (!inInit) {
@ -1761,15 +1751,11 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* Unregister a {@code Collection} of providers
*/
public void unregisterProviders(Collection<?> providers) {
ProvidedResourceScanner providedResourceScanner = new ProvidedResourceScanner(getFhirContext());
if (providers != null) {
for (Object provider : providers) {
removeResourceMethods(provider);
if (provider instanceof IResourceProvider) {
myResourceProviders.remove(provider);
IResourceProvider rsrcProvider = (IResourceProvider) provider;
Class<? extends IBaseResource> resourceType = rsrcProvider.getResourceType();
providedResourceScanner.removeProvidedResources(rsrcProvider);
} else {
myPlainProviders.remove(provider);
}

View File

@ -0,0 +1,923 @@
package ca.uhn.fhir.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.rest.annotation.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.*;
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.IParameter;
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;
import org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider;
import org.hl7.fhir.r4.model.*;
import org.junit.AfterClass;
import org.junit.Test;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class ServerConformanceProviderR4Test {
private static FhirContext ourCtx;
private static FhirValidator ourValidator;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerConformanceProviderR4Test.class);
static {
ourCtx = FhirContext.forR4();
ourValidator = ourCtx.newValidator();
ourValidator.setValidateAgainstStandardSchema(true);
ourValidator.setValidateAgainstStandardSchematron(true);
}
private void validate(OperationDefinition theOpDef) {
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(theOpDef);
ourLog.info("Def: {}", conf);
ValidationResult result = ourValidator.validateWithResult(theOpDef);
String outcome = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result.toOperationOutcome());
ourLog.info("Outcome: {}", outcome);
assertTrue(outcome, result.isSuccessful());
}
private HttpServletRequest createHttpServletRequest() {
HttpServletRequest req = mock(HttpServletRequest.class);
when(req.getRequestURI()).thenReturn("/FhirStorm/fhir/Patient/_search");
when(req.getServletPath()).thenReturn("/fhir");
when(req.getRequestURL()).thenReturn(new StringBuffer().append("http://fhirstorm.dyndns.org:8080/FhirStorm/fhir/Patient/_search"));
when(req.getContextPath()).thenReturn("/FhirStorm");
return req;
}
private ServletConfig createServletConfig() {
ServletConfig sc = mock(ServletConfig.class);
when(sc.getServletContext()).thenReturn(null);
return sc;
}
private CapabilityStatement.CapabilityStatementRestResourceComponent findRestResource(CapabilityStatement capabilityStatement, String wantResource) throws Exception {
CapabilityStatement.CapabilityStatementRestResourceComponent resource = null;
for (CapabilityStatement.CapabilityStatementRestResourceComponent next : capabilityStatement.getRest().get(0).getResource()) {
if (next.getType().equals(wantResource)) {
resource = next;
}
}
if (resource == null) {
throw new Exception("Could not find resource: " + wantResource);
}
return resource;
}
@Test
public void testConditionalOperations() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new ConditionalProvider());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
CapabilityStatement.CapabilityStatementRestResourceComponent res = capabilityStatement.getRest().get(0).getResource().get(1);
assertEquals("Patient", res.getType());
assertTrue(res.getConditionalCreate());
assertEquals(CapabilityStatement.ConditionalDeleteStatus.MULTIPLE, res.getConditionalDeleteElement().getValue());
assertTrue(res.getConditionalUpdate());
}
@Test
public void testExtendedOperationReturningBundle() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new ProviderWithExtendedOperationReturningBundle());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
assertEquals(1, capabilityStatement.getRest().get(0).getOperation().size());
assertEquals("everything", capabilityStatement.getRest().get(0).getOperation().get(0).getName());
assertEquals("OperationDefinition/Patient-i-everything", capabilityStatement.getRest().get(0).getOperation().get(0).getDefinition());
}
@Test
public void testExtendedOperationReturningBundleOperation() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new ProviderWithExtendedOperationReturningBundle());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs) {
};
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-everything"), createRequestDetails(rs));
validate(opDef);
assertEquals("everything", opDef.getCode());
assertEquals(false, opDef.getAffectsState());
}
@Test
public void testInstanceHistorySupported() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new InstanceHistoryProvider());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(capabilityStatement);
assertThat(conf, containsString("<interaction><code value=\"" + CapabilityStatement.TypeRestfulInteraction.HISTORYINSTANCE.toCode() + "\"/></interaction>"));
}
@Test
public void testMultiOptionalDocumentation() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new MultiOptionalProvider());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
boolean found = false;
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().iterator().next();
assertEquals("The patient's identifier", param.getDescription());
found = true;
}
}
assertTrue(found);
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
assertThat(conf, containsString("<documentation value=\"The patient's identifier\"/>"));
assertThat(conf, containsString("<documentation value=\"The patient's name\"/>"));
assertThat(conf, containsString("<type value=\"token\"/>"));
}
@Test
public void testNonConditionalOperations() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new NonConditionalProvider());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
CapabilityStatement.CapabilityStatementRestResourceComponent res = capabilityStatement.getRest().get(0).getResource().get(1);
assertEquals("Patient", res.getType());
assertFalse(res.getConditionalCreate());
assertEquals(null, res.getConditionalDelete());
assertFalse(res.getConditionalUpdate());
}
/** See #379 */
@Test
public void testOperationAcrossMultipleTypes() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new MultiTypePatientProvider(), new MultiTypeEncounterProvider());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
assertEquals(4, capabilityStatement.getRest().get(0).getOperation().size());
List<String> operationNames = toOperationNames(capabilityStatement.getRest().get(0).getOperation());
assertThat(operationNames, containsInAnyOrder("someOp", "validate", "someOp", "validate"));
List<String> operationIdParts = toOperationIdParts(capabilityStatement.getRest().get(0).getOperation());
{
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-someOp"), createRequestDetails(rs));
validate(opDef);
assertEquals("someOp", opDef.getCode());
assertEquals(true, opDef.getInstance());
assertEquals(false, opDef.getSystem());
assertEquals(2, opDef.getParameter().size());
assertEquals("someOpParam1", opDef.getParameter().get(0).getName());
assertEquals("string", opDef.getParameter().get(0).getType());
assertEquals("someOpParam2", opDef.getParameter().get(1).getName());
assertEquals("Patient", opDef.getParameter().get(1).getType());
}
{
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Encounter-i-someOp"), createRequestDetails(rs));
validate(opDef);
assertEquals("someOp", opDef.getCode());
assertEquals(true, opDef.getInstance());
assertEquals(false, opDef.getSystem());
assertEquals(2, opDef.getParameter().size());
assertEquals("someOpParam1", opDef.getParameter().get(0).getName());
assertEquals("string", opDef.getParameter().get(0).getType());
assertEquals("someOpParam2", opDef.getParameter().get(1).getName());
assertEquals("Encounter", opDef.getParameter().get(1).getType());
}
{
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/Patient-i-validate"), createRequestDetails(rs));
validate(opDef);
assertEquals("validate", opDef.getCode());
assertEquals(true, opDef.getInstance());
assertEquals(false, opDef.getSystem());
assertEquals(1, opDef.getParameter().size());
assertEquals("resource", opDef.getParameter().get(0).getName());
assertEquals("Patient", opDef.getParameter().get(0).getType());
}
}
@Test
public void testOperationDocumentation() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setServerName("MY NAME");
rs.setServerVersion("MY VERSION");
rs.setProviders(new SearchProvider());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info("AAAAAA" + conf);
assertThat(conf, containsString("<documentation value=\"The patient's identifier (MRN or other card number)\"/>"));
assertThat(conf, containsString("<type value=\"token\"/>"));
assertEquals("MY NAME", capabilityStatement.getSoftware().getName());
assertEquals("MY VERSION", capabilityStatement.getSoftware().getVersion());
}
@Test
public void testOperationOnNoTypes() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new PlainProviderWithExtendedOperationOnNoType());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs) {
@Override
public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
return super.getServerConformance(theRequest, theRequestDetails);
}
};
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement sconf = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
OperationDefinition opDef = sc.readOperationDefinition(new IdType("OperationDefinition/-is-plain"), createRequestDetails(rs));
validate(opDef);
assertEquals("plain", opDef.getCode());
assertEquals(3, opDef.getParameter().size());
assertEquals("start", opDef.getParameter().get(0).getName());
assertEquals("in", opDef.getParameter().get(0).getUse().toCode());
assertEquals("0", opDef.getParameter().get(0).getMinElement().getValueAsString());
assertEquals("date", opDef.getParameter().get(0).getTypeElement().getValueAsString());
assertEquals("out1", opDef.getParameter().get(2).getName());
assertEquals("out", opDef.getParameter().get(2).getUse().toCode());
assertEquals("1", opDef.getParameter().get(2).getMinElement().getValueAsString());
assertEquals("2", opDef.getParameter().get(2).getMaxElement().getValueAsString());
assertEquals("string", opDef.getParameter().get(2).getTypeElement().getValueAsString());
}
@Test
public void testProviderForSmart() throws ServletException {
RestfulServer rs = new RestfulServer(ourCtx);
rs.createConfiguration();
rs.setProviders(new ProviderWithRequiredAndOptional());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider() {
@Override
public CapabilityStatement getServerConformance(HttpServletRequest theRequest, RequestDetails theRequestDetails) {
CapabilityStatement capabilityStatement = super.getServerConformance(theRequest, theRequestDetails);
Extension extension = new Extension();
Extension extensionDtToken = new Extension();
Extension extensionDtAuthorize = new Extension();
CapabilityStatement.CapabilityStatementRestComponent rest = capabilityStatement.getRestFirstRep();
CapabilityStatement.CapabilityStatementRestSecurityComponent restSecurity = rest.getSecurity();
extension.setUrl("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris");
extensionDtToken.setUrl("token");
extensionDtToken.setValue(new UriType("https://SERVERNAME/token"));
extensionDtAuthorize.setUrl("authorize");
extensionDtAuthorize.setValue(new UriType("https://SERVERNAME/authorize"));
extension.addExtension(extensionDtToken);
extension.addExtension(extensionDtAuthorize);
restSecurity.addExtension(extension);
return capabilityStatement;
}
};
rs.init(createServletConfig());
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
CapabilityStatement parsed = ourCtx.newJsonParser().parseResource(CapabilityStatement.class, conf);
}
@Test
public void testProviderWithRequiredAndOptional() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new ProviderWithRequiredAndOptional());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
CapabilityStatement.CapabilityStatementRestComponent rest = capabilityStatement.getRestFirstRep();
CapabilityStatement.CapabilityStatementRestResourceComponent res = rest.getResourceFirstRep();
assertEquals("DiagnosticReport", res.getType());
assertEquals(DiagnosticReport.SP_SUBJECT, res.getSearchParam().get(0).getName());
assertEquals(DiagnosticReport.SP_CODE, res.getSearchParam().get(1).getName());
assertEquals(DiagnosticReport.SP_DATE, res.getSearchParam().get(2).getName());
assertEquals(1, res.getSearchInclude().size());
}
@Test
public void testReadAndVReadSupported() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new VreadProvider());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(capabilityStatement);
assertThat(conf, containsString("<interaction><code value=\"vread\"/></interaction>"));
assertThat(conf, containsString("<interaction><code value=\"read\"/></interaction>"));
}
@Test
public void testReadSupported() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new ReadProvider());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(capabilityStatement);
assertThat(conf, not(containsString("<interaction><code value=\"vread\"/></interaction>")));
assertThat(conf, containsString("<interaction><code value=\"read\"/></interaction>"));
}
@Test
public void testSearchParameterDocumentation() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new SearchProvider());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
boolean found = false;
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
for (IParameter next : binding.getParameters()) {
SearchParameter param = (SearchParameter) next;
if (param.getDescription().contains("The patient's identifier (MRN or other card number")) {
found = true;
}
}
found = true;
}
}
assertTrue(found);
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
assertThat(conf, containsString("<documentation value=\"The patient's identifier (MRN or other card number)\"/>"));
assertThat(conf, containsString("<type value=\"token\"/>"));
}
/**
* See #286
*/
@Test
public void testSearchReferenceParameterDocumentation() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new PatientResourceProvider());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
boolean found = false;
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(25);
assertEquals("The organization at which this person is a patient", param.getDescription());
found = true;
}
}
assertTrue(found);
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
}
/**
* See #286
*/
@Test
public void testSearchReferenceParameterWithWhitelistDocumentation() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new SearchProviderWithWhitelist());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
boolean found = false;
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(0);
assertEquals("The organization at which this person is a patient", param.getDescription());
found = true;
}
}
assertTrue(found);
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
CapabilityStatement.CapabilityStatementRestResourceComponent resource = findRestResource(capabilityStatement, "Patient");
}
@Test
public void testSearchReferenceParameterWithExplicitChainsDocumentation() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new SearchProviderWithExplicitChains());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
boolean found = false;
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(0);
assertEquals("The organization at which this person is a patient", param.getDescription());
found = true;
}
}
assertTrue(found);
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
CapabilityStatement.CapabilityStatementRestResourceComponent resource = findRestResource(capabilityStatement, "Patient");
assertEquals(3, resource.getSearchParam().size());
CapabilityStatement.CapabilityStatementRestResourceSearchParamComponent param = resource.getSearchParam().get(0);
assertEquals("organization", param.getName());
}
@Test
public void testSystemHistorySupported() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new SystemHistoryProvider());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(capabilityStatement);
assertThat(conf, containsString("<interaction><code value=\"" + CapabilityStatement.SystemRestfulInteraction.HISTORYSYSTEM.toCode() + "\"/></interaction>"));
}
@Test
public void testTypeHistorySupported() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new TypeHistoryProvider());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
String conf = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement);
ourLog.info(conf);
conf = ourCtx.newXmlParser().setPrettyPrint(false).encodeResourceToString(capabilityStatement);
assertThat(conf, containsString("<interaction><code value=\"" + CapabilityStatement.TypeRestfulInteraction.HISTORYTYPE.toCode() + "\"/></interaction>"));
}
@Test
public void testValidateGeneratedStatement() throws Exception {
RestfulServer rs = new RestfulServer(ourCtx);
rs.setProviders(new MultiOptionalProvider());
ServerCapabilityStatementProvider sc = new ServerCapabilityStatementProvider(rs);
rs.setServerConformanceProvider(sc);
rs.init(createServletConfig());
CapabilityStatement capabilityStatement = sc.getServerConformance(createHttpServletRequest(), createRequestDetails(rs));
ourLog.info(ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(capabilityStatement));
ValidationResult result = ourCtx.newValidator().validateWithResult(capabilityStatement);
assertTrue(result.getMessages().toString(), result.isSuccessful());
}
private List<String> toOperationIdParts(List<CapabilityStatement.CapabilityStatementRestResourceOperationComponent> theOperation) {
ArrayList<String> retVal = Lists.newArrayList();
for (CapabilityStatement.CapabilityStatementRestResourceOperationComponent next : theOperation) {
retVal.add(next.getDefinitionElement().getValue());
}
return retVal;
}
private List<String> toOperationNames(List<CapabilityStatement.CapabilityStatementRestResourceOperationComponent> theOperation) {
ArrayList<String> retVal = Lists.newArrayList();
for (CapabilityStatement.CapabilityStatementRestResourceOperationComponent next : theOperation) {
retVal.add(next.getName());
}
return retVal;
}
private Set<String> toStrings(List<? extends CodeType> theType) {
HashSet<String> retVal = new HashSet<String>();
for (CodeType next : theType) {
retVal.add(next.getValueAsString());
}
return retVal;
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
public static class ConditionalProvider implements IResourceProvider {
@Create
public MethodOutcome create(@ResourceParam Patient thePatient, @ConditionalUrlParam String theConditionalUrl) {
return null;
}
@Delete
public MethodOutcome delete(@IdParam IdType theId, @ConditionalUrlParam(supportsMultiple = true) String theConditionalUrl) {
return null;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Update
public MethodOutcome update(@IdParam IdType theId, @ResourceParam Patient thePatient, @ConditionalUrlParam String theConditionalUrl) {
return null;
}
}
public static class InstanceHistoryProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@History
public List<IBaseResource> history(@IdParam IdType theId) {
return null;
}
}
public static class MultiOptionalProvider {
@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) {
return null;
}
}
public static class MultiTypeEncounterProvider implements IResourceProvider {
@Operation(name = "someOp")
public IBundleProvider everything(HttpServletRequest theServletRequest, @IdParam IdType theId,
@OperationParam(name = "someOpParam1") DateParam theStart, @OperationParam(name = "someOpParam2") Encounter theEnd) {
return null;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Encounter.class;
}
@Validate
public IBundleProvider validate(HttpServletRequest theServletRequest, @IdParam IdType theId, @ResourceParam Encounter thePatient) {
return null;
}
}
public static class MultiTypePatientProvider implements IResourceProvider {
@Operation(name = "someOp")
public IBundleProvider everything(HttpServletRequest theServletRequest, @IdParam IdType theId,
@OperationParam(name = "someOpParam1") DateParam theStart, @OperationParam(name = "someOpParam2") Patient theEnd) {
return null;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Validate
public IBundleProvider validate(HttpServletRequest theServletRequest, @IdParam IdType theId, @ResourceParam Patient thePatient) {
return null;
}
}
public static class NonConditionalProvider implements IResourceProvider {
@Create
public MethodOutcome create(@ResourceParam Patient thePatient) {
return null;
}
@Delete
public MethodOutcome delete(@IdParam IdType theId) {
return null;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@Update
public MethodOutcome update(@IdParam IdType theId, @ResourceParam Patient thePatient) {
return null;
}
}
public static class PlainProviderWithExtendedOperationOnNoType {
@Operation(name = "plain", idempotent = true, returnParameters = { @OperationParam(min = 1, max = 2, name = "out1", type = StringType.class) })
public IBundleProvider everything(HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) {
return null;
}
}
public static class ProviderWithExtendedOperationReturningBundle implements IResourceProvider {
@Operation(name = "everything", idempotent = true)
public IBundleProvider everything(HttpServletRequest theServletRequest, @IdParam IdType theId, @OperationParam(name = "start") DateType theStart, @OperationParam(name = "end") DateType theEnd) {
return null;
}
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
}
public static class ProviderWithRequiredAndOptional {
@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 {
return null;
}
}
public static class ReadProvider {
@Search(type = Patient.class)
public Patient findPatient(@Description(shortDefinition = "The patient's identifier (MRN or other card number)") @RequiredParam(name = Patient.SP_IDENTIFIER) TokenParam theIdentifier) {
return null;
}
@Read(version = false)
public Patient readPatient(@IdParam IdType theId) {
return null;
}
}
public static class SearchProvider {
@Search(type = Patient.class)
public Patient findPatient1(@Description(shortDefinition = "The patient's identifier (MRN or other card number)") @RequiredParam(name = Patient.SP_IDENTIFIER) TokenParam theIdentifier) {
return null;
}
@Search(type = Patient.class)
public Patient findPatient2(@Description(shortDefinition = "All patients linked to the given patient") @OptionalParam(name = "link", targetTypes = { Patient.class }) ReferenceAndListParam theLink) {
return null;
}
}
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) {
return null;
}
}
public static class SearchProviderWithExplicitChains {
@Search(type = Patient.class)
public Patient findPatient1(
@Description(shortDefinition = "The organization at which this person is a patient")
@RequiredParam(name = "organization.foo") ReferenceAndListParam theFoo,
@RequiredParam(name = "organization.bar") ReferenceAndListParam theBar,
@RequiredParam(name = "organization.baz.bob") ReferenceAndListParam theBazbob) {
return null;
}
}
public static class SystemHistoryProvider {
@History
public List<IBaseResource> history() {
return null;
}
}
public static class TypeHistoryProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Patient.class;
}
@History
public List<IBaseResource> history() {
return null;
}
}
/**
* Created by dsotnikov on 2/25/2014.
*/
public static class VreadProvider {
@Search(type = Patient.class)
public Patient findPatient(@Description(shortDefinition = "The patient's identifier (MRN or other card number)") @RequiredParam(name = Patient.SP_IDENTIFIER) TokenParam theIdentifier) {
return null;
}
@Read(version = true)
public Patient readPatient(@IdParam IdType theId) {
return null;
}
}
private RequestDetails createRequestDetails(RestfulServer theServer) {
ServletRequestDetails retVal = new ServletRequestDetails(null);
retVal.setServer(theServer);
return retVal;
}
}

View File

@ -458,6 +458,10 @@
Header and automatically place it in
<![CDATA[<code>Resource.meta.source</code>]]>
</action>
<action type="remove">
The @ProvidesResources annotation has been removed from HAPI FHIR, as it was not documented
and did not do anything useful. Please get in touch if this causes any issues.
</action>
</release>
<release version="4.0.3" date="2019-09-03" description="Igloo (Point Release)">
<action type="fix">