Add operation blocking interceptor (#4057)

* Add operation blocking interceptor

* Work on interceptor

* Work on interceptor

* FIx up docs

* Address CVE

* Tweak changelog

* Address review comments
This commit is contained in:
James Agnew 2022-09-25 14:19:17 -04:00 committed by GitHub
parent 2770295638
commit 3935b78083
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
67 changed files with 835 additions and 237 deletions

View File

@ -364,6 +364,26 @@ public enum Pointcut implements IPointcut {
"ca.uhn.fhir.rest.server.interceptor.IServerInterceptor$ActionRequestDetails" "ca.uhn.fhir.rest.server.interceptor.IServerInterceptor$ActionRequestDetails"
), ),
/**
* <b>Server Hook:</b>
* This method is called when a resource provider method is registered and being bound
* by the HAPI FHIR Plain Server / RestfulServer.
* <p>
* Hooks may accept the following parameters:
* <ul>
* <li>
* ca.uhn.fhir.rest.server.method.BaseMethodBinding - The method binding.
* </li>
* </ul>
* <p>
* Hook methods may modify the method binding, replace it, or return <code>null</code> to cancel the binding.
* </p>
*/
SERVER_PROVIDER_METHOD_BOUND("ca.uhn.fhir.rest.server.method.BaseMethodBinding",
"ca.uhn.fhir.rest.server.method.BaseMethodBinding"),
/** /**
* <b>Server Hook:</b> * <b>Server Hook:</b>
* This method is called upon any exception being thrown within the server's request processing code. This includes * This method is called upon any exception being thrown within the server's request processing code. This includes

View File

@ -191,7 +191,7 @@ public enum RestOperationTypeEnum {
/** /**
* Returns the enumerated value associated with this code * Returns the enumerated value associated with this code
*/ */
public RestOperationTypeEnum forCode(@Nonnull String theCode) { public static RestOperationTypeEnum forCode(@Nonnull String theCode) {
Validate.notNull(theCode, "theCode must not be null"); Validate.notNull(theCode, "theCode must not be null");
return CODE_TO_ENUM.get(theCode); return CODE_TO_ENUM.get(theCode);
} }

View File

@ -0,0 +1,5 @@
---
type: add
issue: 4057
title: "A new built-in server interceptor called the InteractionBlockingInterceptor has been added. This interceptor
allows individual operations to be included/excluded from a RestfulServer's exported capabilities."

View File

@ -0,0 +1,5 @@
---
type: add
issue: 4057
title: "The OpenApi generator now allows additional CSS customization for the Swagger UI page, as well as the
option to disable resource type pages."

View File

@ -0,0 +1,10 @@
---
- item:
type: "add"
title: "The version of a few dependencies have been bumped to the latest versions
(dependent HAPI modules listed in brackets):
<ul>
<li>Woodstox (Base): 6.2.5 -> 6.3.1</li>
</ul>
"

View File

@ -201,6 +201,15 @@ Some security audit tools require that servers return an HTTP 405 if an unsuppor
An interceptor can be registered against your server that enables support for OpenAPI (aka Swagger) automatically. See [OpenAPI](/docs/server_plain/openapi.html) for more information. An interceptor can be registered against your server that enables support for OpenAPI (aka Swagger) automatically. See [OpenAPI](/docs/server_plain/openapi.html) for more information.
# Server: Interaction Blocking
The interaction blocking interceptor selectively disables specific interactions that are provided in registered resource providers.
* [InteractionBlockingInterceptor JavaDoc](/apidocs/hapi-fhir-server/ca/uhn/fhir/rest/server/interceptor/InteractionBlockingInterceptor.html)
* [InteractionBlockingInterceptor Source](https://github.com/hapifhir/hapi-fhir/blob/master/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/interceptor/InteractionBlockingInterceptor.java)
# Subscription: Subscription Debug Log Interceptor # Subscription: Subscription Debug Log Interceptor
When using Subscriptions, the debug log interceptor can be used to add a number of additional lines to the server logs showing the internals of the subscription processing pipeline. When using Subscriptions, the debug log interceptor can be used to add a number of additional lines to the server logs showing the internals of the subscription processing pipeline.

View File

@ -45,9 +45,9 @@ The server will produce a response resembling the following:
} }
``` ```
# Diff Instance # Diff Server
When the $diff operation is invoked at the instance level (meaning it is invoked on a specific resource ID), it will compare two versions of the given resource. When the $diff operation is invoked at the server level (meaning it is invoked against the server base URL), it will compare two arbitrary resources of any type.
## Parameters ## Parameters

View File

@ -124,7 +124,7 @@ public abstract class AbstractJaxRsBundleProvider extends AbstractJaxRsProvider
private Response execute(final Builder theRequestBuilder, final String methodKey) private Response execute(final Builder theRequestBuilder, final String methodKey)
throws IOException { throws IOException {
final JaxRsRequest theRequest = theRequestBuilder.build(); final JaxRsRequest theRequest = theRequestBuilder.build();
final BaseMethodBinding<?> method = getBinding(theRequest.getRestOperationType(), methodKey); final BaseMethodBinding method = getBinding(theRequest.getRestOperationType(), methodKey);
try { try {
return (Response) method.invokeServer(this, theRequest); return (Response) method.invokeServer(this, theRequest);
} }
@ -151,7 +151,7 @@ public abstract class AbstractJaxRsBundleProvider extends AbstractJaxRsProvider
* @param theBindingKey the key determining the method to be executed (needed for e.g. custom operation) * @param theBindingKey the key determining the method to be executed (needed for e.g. custom operation)
* @return * @return
*/ */
protected BaseMethodBinding<?> getBinding(final RestOperationTypeEnum restOperation, final String theBindingKey) { protected BaseMethodBinding getBinding(final RestOperationTypeEnum restOperation, final String theBindingKey) {
return getBindings().getBinding(restOperation, theBindingKey); return getBindings().getBinding(restOperation, theBindingKey);
} }

View File

@ -25,12 +25,10 @@ import java.io.IOException;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -58,24 +56,15 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.CapabilityStatement; import org.hl7.fhir.r4.model.CapabilityStatement;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.PostConstruct;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.OPTIONS; import javax.ws.rs.OPTIONS;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* 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. * 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.
@ -156,7 +145,7 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
for (Entry<Class<? extends IResourceProvider>, IResourceProvider> provider : providers.entrySet()) { for (Entry<Class<? extends IResourceProvider>, IResourceProvider> provider : providers.entrySet()) {
addProvider(provider.getValue(), provider.getKey()); addProvider(provider.getValue(), provider.getKey());
} }
List<BaseMethodBinding<?>> serverBindings = new ArrayList<BaseMethodBinding<?>>(); List<BaseMethodBinding> serverBindings = new ArrayList<BaseMethodBinding>();
for (ResourceBinding baseMethodBinding : myResourceNameToBinding.values()) { for (ResourceBinding baseMethodBinding : myResourceNameToBinding.values()) {
serverBindings.addAll(baseMethodBinding.getMethodBindings()); serverBindings.addAll(baseMethodBinding.getMethodBindings());
} }
@ -269,7 +258,7 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
int count = 0; int count = 0;
for (Method m : ReflectionUtil.getDeclaredMethods(theProviderInterface)) { for (Method m : ReflectionUtil.getDeclaredMethods(theProviderInterface)) {
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, getFhirContext(), theProvider); BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(m, getFhirContext(), theProvider);
if (foundMethodBinding == null) { if (foundMethodBinding == null) {
continue; continue;
} }

View File

@ -322,7 +322,7 @@ implements IRestfulServer<JaxRsRequest>, IResourceProvider {
private Response execute(final Builder theRequestBuilder, final String methodKey) private Response execute(final Builder theRequestBuilder, final String methodKey)
throws IOException { throws IOException {
final JaxRsRequest theRequest = theRequestBuilder.build(); final JaxRsRequest theRequest = theRequestBuilder.build();
final BaseMethodBinding<?> method = getBinding(theRequest.getRestOperationType(), methodKey); final BaseMethodBinding method = getBinding(theRequest.getRestOperationType(), methodKey);
try { try {
return (Response) method.invokeServer(this, theRequest); return (Response) method.invokeServer(this, theRequest);
} }
@ -349,7 +349,7 @@ implements IRestfulServer<JaxRsRequest>, IResourceProvider {
* @param theBindingKey the key determining the method to be executed (needed for e.g. custom operation) * @param theBindingKey the key determining the method to be executed (needed for e.g. custom operation)
* @return * @return
*/ */
protected BaseMethodBinding<?> getBinding(final RestOperationTypeEnum restOperation, final String theBindingKey) { protected BaseMethodBinding getBinding(final RestOperationTypeEnum restOperation, final String theBindingKey) {
return getBindings().getBinding(restOperation, theBindingKey); return getBindings().getBinding(restOperation, theBindingKey);
} }

View File

@ -32,7 +32,6 @@ import ca.uhn.fhir.util.ReflectionUtil;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -48,7 +47,7 @@ public class JaxRsMethodBindings {
/** Static collection of bindings mapped to a class*/ /** Static collection of bindings mapped to a class*/
private static final ConcurrentHashMap<Class<?>, JaxRsMethodBindings> classBindings = new ConcurrentHashMap<Class<?>, JaxRsMethodBindings>(); private static final ConcurrentHashMap<Class<?>, JaxRsMethodBindings> classBindings = new ConcurrentHashMap<Class<?>, JaxRsMethodBindings>();
/** Static collection of operationBindings mapped to a class */ /** Static collection of operationBindings mapped to a class */
private ConcurrentHashMap<RestOperationTypeEnum, ConcurrentHashMap<String, BaseMethodBinding<?>>> operationBindings = new ConcurrentHashMap<RestOperationTypeEnum, ConcurrentHashMap<String,BaseMethodBinding<?>>>(); private ConcurrentHashMap<RestOperationTypeEnum, ConcurrentHashMap<String, BaseMethodBinding>> operationBindings = new ConcurrentHashMap<RestOperationTypeEnum, ConcurrentHashMap<String,BaseMethodBinding>>();
/** /**
* The constructor * The constructor
@ -59,7 +58,7 @@ public class JaxRsMethodBindings {
List<Method> declaredMethodsForCurrentProvider = ReflectionUtil.getDeclaredMethods(theProviderClass); List<Method> declaredMethodsForCurrentProvider = ReflectionUtil.getDeclaredMethods(theProviderClass);
declaredMethodsForCurrentProvider.addAll(ReflectionUtil.getDeclaredMethods(theProviderClass.getSuperclass())); declaredMethodsForCurrentProvider.addAll(ReflectionUtil.getDeclaredMethods(theProviderClass.getSuperclass()));
for (final Method m : declaredMethodsForCurrentProvider) { for (final Method m : declaredMethodsForCurrentProvider) {
final BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, theProvider.getFhirContext(), theProvider); final BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(m, theProvider.getFhirContext(), theProvider);
if (foundMethodBinding == null) { if (foundMethodBinding == null) {
continue; continue;
} }
@ -78,7 +77,7 @@ public class JaxRsMethodBindings {
* @param theBinding the methodbinding * @param theBinding the methodbinding
* @return the key for the methodbinding. * @return the key for the methodbinding.
*/ */
private String getBindingKey(final BaseMethodBinding<?> theBinding) { private String getBindingKey(final BaseMethodBinding theBinding) {
if (theBinding instanceof OperationMethodBinding) { if (theBinding instanceof OperationMethodBinding) {
return ((OperationMethodBinding) theBinding).getName(); return ((OperationMethodBinding) theBinding).getName();
} else if (theBinding instanceof SearchMethodBinding) { } else if (theBinding instanceof SearchMethodBinding) {
@ -89,8 +88,8 @@ public class JaxRsMethodBindings {
} }
} }
private void addMethodBinding(String key, BaseMethodBinding<?> binding) { private void addMethodBinding(String key, BaseMethodBinding binding) {
ConcurrentHashMap<String, BaseMethodBinding<?>> mapByOperation = getMapForOperation(binding.getRestOperationType()); ConcurrentHashMap<String, BaseMethodBinding> mapByOperation = getMapForOperation(binding.getRestOperationType());
if (mapByOperation.containsKey(key)) { if (mapByOperation.containsKey(key)) {
throw new IllegalArgumentException(Msg.code(597) + "Multiple Search Method Bindings Found : " + mapByOperation.get(key) + " -- " + binding.getMethod()); throw new IllegalArgumentException(Msg.code(597) + "Multiple Search Method Bindings Found : " + mapByOperation.get(key) + " -- " + binding.getMethod());
} }
@ -104,10 +103,10 @@ public class JaxRsMethodBindings {
* @param operationType the operation type. * @param operationType the operation type.
* @return the map defined in the operation bindings * @return the map defined in the operation bindings
*/ */
private ConcurrentHashMap<String, BaseMethodBinding<?>> getMapForOperation(RestOperationTypeEnum operationType) { private ConcurrentHashMap<String, BaseMethodBinding> getMapForOperation(RestOperationTypeEnum operationType) {
ConcurrentHashMap<String, BaseMethodBinding<?>> result = operationBindings.get(operationType); ConcurrentHashMap<String, BaseMethodBinding> result = operationBindings.get(operationType);
if(result == null) { if(result == null) {
operationBindings.putIfAbsent(operationType, new ConcurrentHashMap<String, BaseMethodBinding<?>>()); operationBindings.putIfAbsent(operationType, new ConcurrentHashMap<String, BaseMethodBinding>());
return getMapForOperation(operationType); return getMapForOperation(operationType);
} else { } else {
return result; return result;
@ -122,9 +121,9 @@ public class JaxRsMethodBindings {
* @return the binding defined * @return the binding defined
* @throws NotImplementedOperationException cannot be found * @throws NotImplementedOperationException cannot be found
*/ */
public BaseMethodBinding<?> getBinding(RestOperationTypeEnum operationType, String theBindingKey) { public BaseMethodBinding getBinding(RestOperationTypeEnum operationType, String theBindingKey) {
String bindingKey = StringUtils.defaultIfBlank(theBindingKey, DEFAULT_METHOD_KEY); String bindingKey = StringUtils.defaultIfBlank(theBindingKey, DEFAULT_METHOD_KEY);
ConcurrentHashMap<String, BaseMethodBinding<?>> map = getMapForOperation(operationType); ConcurrentHashMap<String, BaseMethodBinding> map = getMapForOperation(operationType);
if(map == null || !map.containsKey(bindingKey)) { if(map == null || !map.containsKey(bindingKey)) {
throw new NotImplementedOperationException(Msg.code(598) + "Operation not implemented"); throw new NotImplementedOperationException(Msg.code(598) + "Operation not implemented");
} else { } else {

View File

@ -20,10 +20,10 @@ package ca.uhn.fhir.rest.openapi;
* #L% * #L%
*/ */
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.interceptor.api.Hook; import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Pointcut; import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
@ -58,6 +58,7 @@ import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.servers.Server;
import io.swagger.v3.oas.models.tags.Tag; import io.swagger.v3.oas.models.tags.Tag;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_40;
import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50;
import org.hl7.fhir.instance.model.api.IBaseConformance; import org.hl7.fhir.instance.model.api.IBaseConformance;
@ -127,6 +128,8 @@ public class OpenApiInterceptor {
private final Map<String, String> myResourcePathToClasspath = new HashMap<>(); private final Map<String, String> myResourcePathToClasspath = new HashMap<>();
private final Map<String, String> myExtensionToContentType = new HashMap<>(); private final Map<String, String> myExtensionToContentType = new HashMap<>();
private String myBannerImage; private String myBannerImage;
private String myCssText;
private boolean myUseResourcePages;
/** /**
* Constructor * Constructor
@ -150,6 +153,7 @@ public class OpenApiInterceptor {
private void initResources() { private void initResources() {
setBannerImage(RACCOON_PNG); setBannerImage(RACCOON_PNG);
setUseResourcePages(true);
addResourcePathToClasspath("/swagger-ui/index.html", "/ca/uhn/fhir/rest/openapi/index.html"); addResourcePathToClasspath("/swagger-ui/index.html", "/ca/uhn/fhir/rest/openapi/index.html");
addResourcePathToClasspath("/swagger-ui/" + RACCOON_PNG, "/ca/uhn/fhir/rest/openapi/raccoon.png"); addResourcePathToClasspath("/swagger-ui/" + RACCOON_PNG, "/ca/uhn/fhir/rest/openapi/raccoon.png");
@ -245,6 +249,15 @@ public class OpenApiInterceptor {
String resourcePath = requestPath.substring("/swagger-ui/".length()); String resourcePath = requestPath.substring("/swagger-ui/".length());
if (resourcePath.equals("swagger-ui-custom.css") && isNotBlank(myCssText)) {
theResponse.setContentType("text/css");
theResponse.setStatus(200);
theResponse.getWriter().println(myCssText);
theResponse.getWriter().close();
return true;
}
try (InputStream resource = ClasspathUtil.loadResourceAsStream("/META-INF/resources/webjars/swagger-ui/" + mySwaggerUiVersion + "/" + resourcePath)) { try (InputStream resource = ClasspathUtil.loadResourceAsStream("/META-INF/resources/webjars/swagger-ui/" + mySwaggerUiVersion + "/" + resourcePath)) {
if (resourcePath.endsWith(".js") || resourcePath.endsWith(".map")) { if (resourcePath.endsWith(".js") || resourcePath.endsWith(".map")) {
@ -275,12 +288,34 @@ public class OpenApiInterceptor {
} }
public String removeTrailingSlash(String theUrl) { public String removeTrailingSlash(String theUrl) {
while(theUrl != null && theUrl.endsWith("/")) { while (theUrl != null && theUrl.endsWith("/")) {
theUrl = theUrl.substring(0, theUrl.length() - 1); theUrl = theUrl.substring(0, theUrl.length() - 1);
} }
return theUrl; return theUrl;
} }
/**
* If supplied, this field can be used to provide additional CSS text that should
* be loaded by the swagger-ui page. The contents should be raw CSS text, e.g.
* <code>
* BODY { font-size: 1.1em; }
* </code>
*/
public String getCssText() {
return myCssText;
}
/**
* If supplied, this field can be used to provide additional CSS text that should
* be loaded by the swagger-ui page. The contents should be raw CSS text, e.g.
* <code>
* BODY { font-size: 1.1em; }
* </code>
*/
public void setCssText(String theCssText) {
myCssText = theCssText;
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private void serveSwaggerUiHtml(ServletRequestDetails theRequestDetails, HttpServletResponse theResponse) throws IOException { private void serveSwaggerUiHtml(ServletRequestDetails theRequestDetails, HttpServletResponse theResponse) throws IOException {
CapabilityStatement cs = getCapabilityStatement(theRequestDetails); CapabilityStatement cs = getCapabilityStatement(theRequestDetails);
@ -299,6 +334,8 @@ public class OpenApiInterceptor {
context.setVariable("BANNER_IMAGE_URL", getBannerImage()); context.setVariable("BANNER_IMAGE_URL", getBannerImage());
context.setVariable("OPENAPI_DOCS", baseUrl + "/api-docs"); context.setVariable("OPENAPI_DOCS", baseUrl + "/api-docs");
context.setVariable("FHIR_VERSION", cs.getFhirVersion().toCode()); context.setVariable("FHIR_VERSION", cs.getFhirVersion().toCode());
context.setVariable("ADDITIONAL_CSS_TEXT", getCssText());
context.setVariable("USE_RESOURCE_PAGES", isUseResourcePages());
context.setVariable("FHIR_VERSION_CODENAME", FhirVersionEnum.forVersionString(cs.getFhirVersion().toCode()).name()); context.setVariable("FHIR_VERSION_CODENAME", FhirVersionEnum.forVersionString(cs.getFhirVersion().toCode()).name());
String copyright = cs.getCopyright(); String copyright = cs.getCopyright();
@ -341,7 +378,12 @@ public class OpenApiInterceptor {
context.setVariable("PAGE_NAMES", pageNames); context.setVariable("PAGE_NAMES", pageNames);
context.setVariable("PAGE_NAME_TO_COUNT", resourceToCount); context.setVariable("PAGE_NAME_TO_COUNT", resourceToCount);
String page = extractPageName(theRequestDetails, PAGE_SYSTEM); String page;
if (isUseResourcePages()) {
page = extractPageName(theRequestDetails, PAGE_SYSTEM);
} else {
page = PAGE_ALL;
}
context.setVariable("PAGE", page); context.setVariable("PAGE", page);
populateOIDCVariables(theRequestDetails, context); populateOIDCVariables(theRequestDetails, context);
@ -828,7 +870,6 @@ public class OpenApiInterceptor {
}; };
} }
private Content provideContentFhirResource(OpenAPI theOpenApi, FhirContext theExampleFhirContext, Supplier<IBaseResource> theExampleSupplier) { private Content provideContentFhirResource(OpenAPI theOpenApi, FhirContext theExampleFhirContext, Supplier<IBaseResource> theExampleSupplier) {
addSchemaFhirResource(theOpenApi); addSchemaFhirResource(theOpenApi);
Content retVal = new Content(); Content retVal = new Content();
@ -862,14 +903,36 @@ public class OpenApiInterceptor {
return new ClassLoaderTemplateResource(myResourcePathToClasspath.get("/swagger-ui/index.html"), StandardCharsets.UTF_8.name()); return new ClassLoaderTemplateResource(myResourcePathToClasspath.get("/swagger-ui/index.html"), StandardCharsets.UTF_8.name());
} }
public void setBannerImage(String theBannerImage) {
myBannerImage = theBannerImage;
}
public String getBannerImage() { public String getBannerImage() {
return myBannerImage; return myBannerImage;
} }
public OpenApiInterceptor setBannerImage(String theBannerImage) {
myBannerImage = StringUtils.defaultIfBlank(theBannerImage, null);
return this;
}
public boolean isUseResourcePages() {
return myUseResourcePages;
}
public void setUseResourcePages(boolean theUseResourcePages) {
myUseResourcePages = theUseResourcePages;
}
@SuppressWarnings("unchecked")
private static <T extends Resource> T toCanonicalVersion(IBaseResource theNonCanonical) {
IBaseResource canonical;
if (theNonCanonical instanceof org.hl7.fhir.dstu3.model.Resource) {
canonical = VersionConvertorFactory_30_40.convertResource((org.hl7.fhir.dstu3.model.Resource) theNonCanonical);
} else if (theNonCanonical instanceof org.hl7.fhir.r5.model.Resource) {
canonical = VersionConvertorFactory_40_50.convertResource((org.hl7.fhir.r5.model.Resource) theNonCanonical);
} else {
canonical = theNonCanonical;
}
return (T) canonical;
}
private class SwaggerUiTemplateResolver implements ITemplateResolver { private class SwaggerUiTemplateResolver implements ITemplateResolver {
@Override @Override
public String getName() { public String getName() {
@ -919,18 +982,5 @@ public class OpenApiInterceptor {
} }
} }
@SuppressWarnings("unchecked")
private static <T extends Resource> T toCanonicalVersion(IBaseResource theNonCanonical) {
IBaseResource canonical;
if (theNonCanonical instanceof org.hl7.fhir.dstu3.model.Resource) {
canonical = VersionConvertorFactory_30_40.convertResource((org.hl7.fhir.dstu3.model.Resource) theNonCanonical);
} else if (theNonCanonical instanceof org.hl7.fhir.r5.model.Resource) {
canonical = VersionConvertorFactory_40_50.convertResource((org.hl7.fhir.r5.model.Resource) theNonCanonical);
} else {
canonical = theNonCanonical;
}
return (T) canonical;
}
} }

View File

@ -139,3 +139,8 @@ body
border-radius: 6px; border-radius: 6px;
color: #000; color: #000;
} }
/* Disable the servers dropdown, which is useless here */
.swagger-ui .scheme-container {
display: none;
}

View File

@ -4,16 +4,15 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Swagger UI</title> <title>Swagger UI</title>
<link rel="stylesheet" type="text/css" href="./swagger-ui.css" /> <link rel="stylesheet" type="text/css" href="./swagger-ui.css" />
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
<link rel="stylesheet" type="text/css" href="./index.css" /> <link rel="stylesheet" type="text/css" href="./index.css" />
<link rel="stylesheet" type="text/css" href="./swagger-ui-custom.css" th:if="${ADDITIONAL_CSS_TEXT} != null"/>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
</div> </div>
<div class="banner"> <div class="banner">
<img th:src="${BANNER_IMAGE_URL}" style="height: 100px;"/> <img id="banner_img" th:src="${BANNER_IMAGE_URL}" style="height: 100px;" th:if="${BANNER_IMAGE_URL} != null"/>
<h1>[[${DESCRIPTION}]]<br/><span class="version" th:text="${SERVER_NAME} + ' ' + ${SERVER_VERSION}"></span></h1> <h1>[[${DESCRIPTION}]]<br/><span class="version" th:text="${SERVER_NAME} + ' ' + ${SERVER_VERSION}"></span></h1>
</div> </div>
<div class="banner2"> <div class="banner2">
@ -29,7 +28,7 @@
</div> </div>
<div class="bannerCopyright" th:if="${COPYRIGHT_HTML} != null" th:utext="${COPYRIGHT_HTML}"> <div class="bannerCopyright" th:if="${COPYRIGHT_HTML} != null" th:utext="${COPYRIGHT_HTML}">
</div> </div>
<div class="banner3"> <div class="banner3" th:if="${USE_RESOURCE_PAGES}">
<div class="pageButtons" id="pageButtons"> <div class="pageButtons" id="pageButtons">
<a class="pageButton" th:each="pageName : ${PAGE_NAMES}" th:classappend="${pageName} == ${PAGE} ? 'pageButtonSelected' : ''" th:href="@{/swagger-ui/(page=${pageName})}"> <a class="pageButton" th:each="pageName : ${PAGE_NAMES}" th:classappend="${pageName} == ${PAGE} ? 'pageButtonSelected' : ''" th:href="@{/swagger-ui/(page=${pageName})}">
[[${pageName}]] [[${pageName}]]

View File

@ -57,11 +57,14 @@ import java.net.URL;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
@ -181,6 +184,56 @@ public class OpenApiInterceptorTest {
String url = "http://localhost:" + myServer.getPort() + "/fhir/swagger-ui/"; String url = "http://localhost:" + myServer.getPort() + "/fhir/swagger-ui/";
String resp = fetchSwaggerUi(url); String resp = fetchSwaggerUi(url);
assertThat(resp, resp, containsString("<p>This server is copyright <strong>Example Org</strong> 2021</p>")); assertThat(resp, resp, containsString("<p>This server is copyright <strong>Example Org</strong> 2021</p>"));
assertThat(resp, resp, not(containsString("swagger-ui-custom.css")));
}
@Test
public void testSwaggerUiWithNoBannerUrl() throws IOException {
myServer.getRestfulServer().registerInterceptor(new AddResourceCountsInterceptor());
myServer.getRestfulServer().registerInterceptor(new OpenApiInterceptor().setBannerImage(""));
String url = "http://localhost:" + myServer.getPort() + "/fhir/swagger-ui/";
String resp = fetchSwaggerUi(url);
assertThat(resp, resp, not(containsString("img id=\"banner_img\"")));
}
@Test
public void testSwaggerUiWithCustomStylesheet() throws IOException {
myServer.getRestfulServer().registerInterceptor(new AddResourceCountsInterceptor());
OpenApiInterceptor interceptor = new OpenApiInterceptor();
interceptor.setCssText("BODY {\nfont-size: 1.1em;\n}");
myServer.getRestfulServer().registerInterceptor(interceptor);
// Fetch Swagger UI HTML
String url = "http://localhost:" + myServer.getPort() + "/fhir/swagger-ui/";
String resp = fetchSwaggerUi(url);
assertThat(resp, resp, containsString("<link rel=\"stylesheet\" type=\"text/css\" href=\"./swagger-ui-custom.css\"/>"));
// Fetch Custom CSS
url = "http://localhost:" + myServer.getPort() + "/fhir/swagger-ui/swagger-ui-custom.css";
resp = fetchSwaggerUi(url);
String expected = """
BODY {
font-size: 1.1em;
}
""";
assertEquals(expected, resp);
}
@Test
public void testSwaggerUiNotPaged() throws IOException {
myServer.getRestfulServer().registerInterceptor(new AddResourceCountsInterceptor());
OpenApiInterceptor interceptor = new OpenApiInterceptor();
interceptor.setUseResourcePages(false);
myServer.getRestfulServer().registerInterceptor(interceptor);
// Fetch Swagger UI HTML
String url = "http://localhost:" + myServer.getPort() + "/fhir/swagger-ui/";
String resp = fetchSwaggerUi(url);
List<String> buttonTexts = parsePageButtonTexts(resp, url);
assertThat(buttonTexts.toString(), buttonTexts, empty());
} }
@Test @Test
@ -238,6 +291,10 @@ public class OpenApiInterceptorTest {
private List<String> parsePageButtonTexts(String resp, String url) throws IOException { private List<String> parsePageButtonTexts(String resp, String url) throws IOException {
HtmlPage html = HtmlUtil.parseAsHtml(resp, new URL(url)); HtmlPage html = HtmlUtil.parseAsHtml(resp, new URL(url));
HtmlDivision pageButtons = (HtmlDivision) html.getElementById("pageButtons"); HtmlDivision pageButtons = (HtmlDivision) html.getElementById("pageButtons");
if (pageButtons == null) {
return Collections.emptyList();
}
List<String> buttonTexts = new ArrayList<>(); List<String> buttonTexts = new ArrayList<>();
for (DomElement next : pageButtons.getChildElements()) { for (DomElement next : pageButtons.getChildElements()) {
buttonTexts.add(next.asNormalizedText()); buttonTexts.add(next.asNormalizedText());

View File

@ -32,13 +32,13 @@ public interface IRestfulServerUtil {
Object getResourceParameter( Object getResourceParameter(
RequestDetails requestDetails, RequestDetails requestDetails,
Mode myMode, Mode myMode,
BaseMethodBinding<?> theMethodBinding, BaseMethodBinding theMethodBinding,
Class<? extends IBaseResource> myResourceType); Class<? extends IBaseResource> myResourceType);
Object getRequestResource(RequestDetails theRequest, ParamStyle myParamStyle, Class<? extends IBaseResource> myResourceBundleType); Object getRequestResource(RequestDetails theRequest, ParamStyle myParamStyle, Class<? extends IBaseResource> myResourceBundleType);
<T extends IBaseResource> T loadResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<T> theResourceType); <T extends IBaseResource> T loadResourceFromRequest(RequestDetails theRequest, BaseMethodBinding theMethodBinding, Class<T> theResourceType);
IBaseResource parseResourceFromRequest(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding, Class<? extends IBaseResource> theResourceType); IBaseResource parseResourceFromRequest(RequestDetails theRequest, BaseMethodBinding theMethodBinding, Class<? extends IBaseResource> theResourceType);
} }

View File

@ -22,6 +22,7 @@ package ca.uhn.fhir.rest.server;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.method.BaseMethodBinding; import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.MethodMatchEnum; import ca.uhn.fhir.rest.server.method.MethodMatchEnum;
import java.util.LinkedList; import java.util.LinkedList;
@ -35,7 +36,7 @@ public class ResourceBinding {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceBinding.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceBinding.class);
private String resourceName; private String resourceName;
private LinkedList<BaseMethodBinding<?>> myMethodBindings = new LinkedList<>(); private LinkedList<BaseMethodBinding> myMethodBindings = new LinkedList<>();
/** /**
* Constructor * Constructor
@ -44,7 +45,7 @@ public class ResourceBinding {
super(); super();
} }
public BaseMethodBinding<?> getMethod(RequestDetails theRequest) { public BaseMethodBinding getMethod(RequestDetails theRequest) {
if (null == myMethodBindings) { if (null == myMethodBindings) {
ourLog.warn("No methods exist for resource: {}", resourceName); ourLog.warn("No methods exist for resource: {}", resourceName);
return null; return null;
@ -56,10 +57,10 @@ public class ResourceBinding {
* Look for the method with the highest match strength * Look for the method with the highest match strength
*/ */
BaseMethodBinding<?> matchedMethod = null; BaseMethodBinding matchedMethod = null;
MethodMatchEnum matchedMethodStrength = null; MethodMatchEnum matchedMethodStrength = null;
for (BaseMethodBinding<?> rm : myMethodBindings) { for (BaseMethodBinding rm : myMethodBindings) {
MethodMatchEnum nextMethodMatch = rm.incomingServerRequestMatchesMethod(theRequest); MethodMatchEnum nextMethodMatch = rm.incomingServerRequestMatchesMethod(theRequest);
if (nextMethodMatch != MethodMatchEnum.NONE) { if (nextMethodMatch != MethodMatchEnum.NONE) {
if (matchedMethodStrength == null || matchedMethodStrength.ordinal() < nextMethodMatch.ordinal()) { if (matchedMethodStrength == null || matchedMethodStrength.ordinal() < nextMethodMatch.ordinal()) {
@ -83,11 +84,11 @@ public class ResourceBinding {
this.resourceName = resourceName; this.resourceName = resourceName;
} }
public List<BaseMethodBinding<?>> getMethodBindings() { public List<BaseMethodBinding> getMethodBindings() {
return myMethodBindings; return myMethodBindings;
} }
public void addMethod(BaseMethodBinding<?> method) { public void addMethod(BaseMethodBinding method) {
this.myMethodBindings.push(method); this.myMethodBindings.push(method);
} }

View File

@ -56,6 +56,7 @@ import ca.uhn.fhir.rest.server.interceptor.ExceptionHandlingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.method.BaseMethodBinding; import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.ConformanceMethodBinding; import ca.uhn.fhir.rest.server.method.ConformanceMethodBinding;
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.MethodMatchEnum; import ca.uhn.fhir.rest.server.method.MethodMatchEnum;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.rest.server.tenant.ITenantIdentificationStrategy; import ca.uhn.fhir.rest.server.tenant.ITenantIdentificationStrategy;
@ -77,6 +78,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.UnavailableException; import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
@ -264,7 +266,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
return result; return result;
} }
private List<BaseMethodBinding<?>> getGlobalBindings() { private List<BaseMethodBinding> getGlobalBindings() {
return myGlobalBinding.getMethodBindings(); return myGlobalBinding.getMethodBindings();
} }
@ -370,11 +372,11 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* Figure out and return whichever method binding is appropriate for * Figure out and return whichever method binding is appropriate for
* the given request * the given request
*/ */
public BaseMethodBinding<?> determineResourceMethod(RequestDetails requestDetails, String requestPath) { public BaseMethodBinding determineResourceMethod(RequestDetails requestDetails, String requestPath) {
RequestTypeEnum requestType = requestDetails.getRequestType(); RequestTypeEnum requestType = requestDetails.getRequestType();
ResourceBinding resourceBinding = null; ResourceBinding resourceBinding = null;
BaseMethodBinding<?> resourceMethod = null; BaseMethodBinding resourceMethod = null;
String resourceName = requestDetails.getResourceName(); String resourceName = requestDetails.getResourceName();
if (myServerConformanceMethod.incomingServerRequestMatchesMethod(requestDetails) != MethodMatchEnum.NONE) { if (myServerConformanceMethod.incomingServerRequestMatchesMethod(requestDetails) != MethodMatchEnum.NONE) {
resourceMethod = myServerConformanceMethod; resourceMethod = myServerConformanceMethod;
@ -466,7 +468,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
int count = 0; int count = 0;
for (Method m : ReflectionUtil.getDeclaredMethods(clazz)) { for (Method m : ReflectionUtil.getDeclaredMethods(clazz)) {
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, getFhirContext(), theProvider); BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(m, getFhirContext(), theProvider);
if (foundMethodBinding == null) { if (foundMethodBinding == null) {
continue; continue;
} }
@ -489,6 +491,18 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
} }
ourLog.trace("Scanning public method: {}#{}", theProvider.getClass(), m.getName()); ourLog.trace("Scanning public method: {}#{}", theProvider.getClass(), m.getName());
// Interceptor call: SERVER_PROVIDER_METHOD_BOUND
if (myInterceptorService.hasHooks(Pointcut.SERVER_PROVIDER_METHOD_BOUND)) {
HookParams params = new HookParams()
.add(BaseMethodBinding.class, foundMethodBinding);
BaseMethodBinding newMethodBinding = (BaseMethodBinding) myInterceptorService.callHooksAndReturnObject(Pointcut.SERVER_PROVIDER_METHOD_BOUND, params);
if (newMethodBinding == null) {
ourLog.info("Method binding {} was discarded by interceptor and will not be registered", foundMethodBinding);
continue;
}
foundMethodBinding = newMethodBinding;
}
String resourceName = foundMethodBinding.getResourceName(); String resourceName = foundMethodBinding.getResourceName();
ResourceBinding resourceBinding; ResourceBinding resourceBinding;
if (resourceName == null) { if (resourceName == null) {
@ -515,7 +529,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
Package pack = annotation.annotationType().getPackage(); Package pack = annotation.annotationType().getPackage();
if (pack.equals(IdParam.class.getPackage())) { if (pack.equals(IdParam.class.getPackage())) {
if (!allowableParams.contains(annotation.annotationType())) { if (!allowableParams.contains(annotation.annotationType())) {
throw new ConfigurationException(Msg.code(292) + "Method[" + m.toString() + "] is not allowed to have a parameter annotated with " + annotation); throw new ConfigurationException(Msg.code(292) + "Method[" + m + "] is not allowed to have a parameter annotated with " + annotation);
} }
} }
} }
@ -823,10 +837,10 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
return myResourceNameToBinding.values(); return myResourceNameToBinding.values();
} }
public Collection<BaseMethodBinding<?>> getProviderMethodBindings(Object theProvider) { public Collection<BaseMethodBinding> getProviderMethodBindings(Object theProvider) {
Set<BaseMethodBinding<?>> retVal = new HashSet<>(); Set<BaseMethodBinding> retVal = new HashSet<>();
for (ResourceBinding resourceBinding : getResourceBindings()) { for (ResourceBinding resourceBinding : getResourceBindings()) {
for (BaseMethodBinding<?> methodBinding : resourceBinding.getMethodBindings()) { for (BaseMethodBinding methodBinding : resourceBinding.getMethodBindings()) {
if (theProvider.equals(methodBinding.getProvider())) { if (theProvider.equals(methodBinding.getProvider())) {
retVal.add(methodBinding); retVal.add(methodBinding);
} }
@ -904,7 +918,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
* internal to HAPI and developers generally do not need to interact with it. Use * internal to HAPI and developers generally do not need to interact with it. Use
* with caution, as it may change. * with caution, as it may change.
*/ */
public List<BaseMethodBinding<?>> getServerBindings() { public List<BaseMethodBinding> getServerBindings() {
return myServerBinding.getMethodBindings(); return myServerBinding.getMethodBindings();
} }
@ -1125,7 +1139,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
validateRequest(requestDetails); validateRequest(requestDetails);
BaseMethodBinding<?> resourceMethod = determineResourceMethod(requestDetails, requestPath); BaseMethodBinding resourceMethod = determineResourceMethod(requestDetails, requestPath);
RestOperationTypeEnum operation = resourceMethod.getRestOperationType(requestDetails); RestOperationTypeEnum operation = resourceMethod.getRestOperationType(requestDetails);
requestDetails.setRestOperationType(operation); requestDetails.setRestOperationType(operation);
@ -1693,7 +1707,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/* /*
* Inner method to actually register theProviders * Inner method to actually register theProviders
*/ */
protected void registerProviders(Collection<?> theProviders, boolean inInit) { protected void registerProviders(@Nullable Collection<?> theProviders, boolean inInit) {
Validate.noNullElements(theProviders, "theProviders must not contain any null elements"); Validate.noNullElements(theProviders, "theProviders must not contain any null elements");
List<IResourceProvider> newResourceProviders = new ArrayList<>(); List<IResourceProvider> newResourceProviders = new ArrayList<>();
@ -1772,8 +1786,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
continue; continue;
} }
for (Iterator<BaseMethodBinding<?>> it = resourceBinding.getMethodBindings().iterator(); it.hasNext(); ) { for (Iterator<BaseMethodBinding> it = resourceBinding.getMethodBindings().iterator(); it.hasNext(); ) {
BaseMethodBinding<?> binding = it.next(); BaseMethodBinding binding = it.next();
if (theProvider.equals(binding.getProvider())) { if (theProvider.equals(binding.getProvider())) {
it.remove(); it.remove();
ourLog.info("{} binding of {} was removed", resourceName, binding); ourLog.info("{} binding of {} was removed", resourceName, binding);
@ -1798,7 +1812,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
*/ */
private void removeResourceMethods(Object theProvider, Class<?> clazz, Collection<String> resourceNames) throws ConfigurationException { private void removeResourceMethods(Object theProvider, Class<?> clazz, Collection<String> resourceNames) throws ConfigurationException {
for (Method m : ReflectionUtil.getDeclaredMethods(clazz)) { for (Method m : ReflectionUtil.getDeclaredMethods(clazz)) {
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, getFhirContext(), theProvider); BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(m, getFhirContext(), theProvider);
if (foundMethodBinding == null) { if (foundMethodBinding == null) {
continue; // not a bound method continue; // not a bound method
} }

View File

@ -63,12 +63,11 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
public class RestfulServerConfiguration implements ISearchParamRegistry { public class RestfulServerConfiguration implements ISearchParamRegistry {
public static final String GLOBAL = "GLOBAL";
private static final Logger ourLog = LoggerFactory.getLogger(RestfulServerConfiguration.class); private static final Logger ourLog = LoggerFactory.getLogger(RestfulServerConfiguration.class);
private Collection<ResourceBinding> resourceBindings; private Collection<ResourceBinding> myResourceBindings;
private List<BaseMethodBinding<?>> serverBindings; private List<BaseMethodBinding> myServerBindings;
private List<BaseMethodBinding<?>> myGlobalBindings; private List<BaseMethodBinding> myGlobalBindings;
private Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype; private Map<String, Class<? extends IBaseResource>> myResourceNameToSharedSupertype;
private String myImplementationDescription; private String myImplementationDescription;
private String myServerName = "HAPI FHIR"; private String myServerName = "HAPI FHIR";
private String myServerVersion = VersionUtil.getVersion(); private String myServerVersion = VersionUtil.getVersion();
@ -89,7 +88,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
* @return the resourceBindings * @return the resourceBindings
*/ */
public Collection<ResourceBinding> getResourceBindings() { public Collection<ResourceBinding> getResourceBindings() {
return resourceBindings; return myResourceBindings;
} }
/** /**
@ -98,7 +97,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
* @param resourceBindings the resourceBindings to set * @param resourceBindings the resourceBindings to set
*/ */
public RestfulServerConfiguration setResourceBindings(Collection<ResourceBinding> resourceBindings) { public RestfulServerConfiguration setResourceBindings(Collection<ResourceBinding> resourceBindings) {
this.resourceBindings = resourceBindings; this.myResourceBindings = resourceBindings;
return this; return this;
} }
@ -107,24 +106,24 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
* *
* @return the serverBindings * @return the serverBindings
*/ */
public List<BaseMethodBinding<?>> getServerBindings() { public List<BaseMethodBinding> getServerBindings() {
return serverBindings; return myServerBindings;
} }
/** /**
* Set the theServerBindings * Set the theServerBindings
*/ */
public RestfulServerConfiguration setServerBindings(List<BaseMethodBinding<?>> theServerBindings) { public RestfulServerConfiguration setServerBindings(List<BaseMethodBinding> theServerBindings) {
this.serverBindings = theServerBindings; this.myServerBindings = theServerBindings;
return this; return this;
} }
public Map<String, Class<? extends IBaseResource>> getNameToSharedSupertype() { public Map<String, Class<? extends IBaseResource>> getNameToSharedSupertype() {
return resourceNameToSharedSupertype; return myResourceNameToSharedSupertype;
} }
public RestfulServerConfiguration setNameToSharedSupertype(Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype) { public RestfulServerConfiguration setNameToSharedSupertype(Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype) {
this.resourceNameToSharedSupertype = resourceNameToSharedSupertype; this.myResourceNameToSharedSupertype = resourceNameToSharedSupertype;
return this; return this;
} }
@ -248,8 +247,8 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
IdentityHashMap<OperationMethodBinding, String> operationBindingToId = new IdentityHashMap<>(); IdentityHashMap<OperationMethodBinding, String> operationBindingToId = new IdentityHashMap<>();
HashMap<String, List<OperationMethodBinding>> operationIdToBindings = new HashMap<>(); HashMap<String, List<OperationMethodBinding>> operationIdToBindings = new HashMap<>();
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings(); Map<String, List<BaseMethodBinding>> resourceToMethods = collectMethodBindings();
List<BaseMethodBinding<?>> methodBindings = resourceToMethods List<BaseMethodBinding> methodBindings = resourceToMethods
.values() .values()
.stream().flatMap(t -> t.stream()) .stream().flatMap(t -> t.stream())
.collect(Collectors.toList()); .collect(Collectors.toList());
@ -258,7 +257,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
} }
ListMultimap<String, OperationMethodBinding> nameToOperationMethodBindings = ArrayListMultimap.create(); ListMultimap<String, OperationMethodBinding> nameToOperationMethodBindings = ArrayListMultimap.create();
for (BaseMethodBinding<?> nextMethodBinding : methodBindings) { for (BaseMethodBinding nextMethodBinding : methodBindings) {
if (nextMethodBinding instanceof OperationMethodBinding) { if (nextMethodBinding instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding; OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
nameToOperationMethodBindings.put(methodBinding.getName(), methodBinding); nameToOperationMethodBindings.put(methodBinding.getName(), methodBinding);
@ -332,7 +331,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
nextMethodBindings.forEach(t->operationBindingToId.put(t, operationId)); nextMethodBindings.forEach(t->operationBindingToId.put(t, operationId));
} }
for (BaseMethodBinding<?> nextMethodBinding : methodBindings) { for (BaseMethodBinding nextMethodBinding : methodBindings) {
if (nextMethodBinding instanceof OperationMethodBinding) { if (nextMethodBinding instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding; OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
if (operationBindingToId.containsKey(methodBinding)) { if (operationBindingToId.containsKey(methodBinding)) {
@ -353,18 +352,18 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
return new Bindings(namedSearchMethodBindingToName, searchNameToBindings, operationIdToBindings, operationBindingToId); return new Bindings(namedSearchMethodBindingToName, searchNameToBindings, operationIdToBindings, operationBindingToId);
} }
public Map<String, List<BaseMethodBinding<?>>> collectMethodBindings() { public Map<String, List<BaseMethodBinding>> collectMethodBindings() {
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<>(); Map<String, List<BaseMethodBinding>> resourceToMethods = new TreeMap<>();
for (ResourceBinding next : getResourceBindings()) { for (ResourceBinding next : getResourceBindings()) {
String resourceName = next.getResourceName(); String resourceName = next.getResourceName();
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) { for (BaseMethodBinding nextMethodBinding : next.getMethodBindings()) {
if (resourceToMethods.containsKey(resourceName) == false) { if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<>()); resourceToMethods.put(resourceName, new ArrayList<>());
} }
resourceToMethods.get(resourceName).add(nextMethodBinding); resourceToMethods.get(resourceName).add(nextMethodBinding);
} }
} }
for (BaseMethodBinding<?> nextMethodBinding : getServerBindings()) { for (BaseMethodBinding nextMethodBinding : getServerBindings()) {
String resourceName = ""; String resourceName = "";
if (resourceToMethods.containsKey(resourceName) == false) { if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<>()); resourceToMethods.put(resourceName, new ArrayList<>());
@ -374,11 +373,11 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
return resourceToMethods; return resourceToMethods;
} }
public List<BaseMethodBinding<?>> getGlobalBindings() { public List<BaseMethodBinding> getGlobalBindings() {
return myGlobalBindings; return myGlobalBindings;
} }
public void setGlobalBindings(List<BaseMethodBinding<?>> theGlobalBindings) { public void setGlobalBindings(List<BaseMethodBinding> theGlobalBindings) {
myGlobalBindings = theGlobalBindings; myGlobalBindings = theGlobalBindings;
} }
@ -403,7 +402,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
scanner.register(resourceClass); scanner.register(resourceClass);
}); });
resourceNameToSharedSupertype = resourceNameToScanner.entrySet().stream() myResourceNameToSharedSupertype = resourceNameToScanner.entrySet().stream()
.filter(entry -> entry.getValue().getLowestCommonSuperclass().isPresent()) .filter(entry -> entry.getValue().getLowestCommonSuperclass().isPresent())
.collect(Collectors.toMap( .collect(Collectors.toMap(
entry -> entry.getKey(), entry -> entry.getKey(),

View File

@ -0,0 +1,238 @@
package ca.uhn.fhir.rest.server.interceptor;
/*-
* #%L
* HAPI FHIR - Server Framework
* %%
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
* %%
* 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 ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.OperationMethodBinding;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import static org.apache.commons.lang3.StringUtils.isBlank;
/**
* This interceptor can be used to selectively block specific interactions/operations from
* the server's capabilities. This interceptor must be configured and registered to a
* {@link ca.uhn.fhir.rest.server.RestfulServer} prior to any resource provider
* classes being registered to it. This interceptor will then examine any
* provider classes being registered and may choose to discard some or all
* of the method bindings on each provider.
* <p>
* For example, if this interceptor is configured to block resource creation, then
* when a resource provider is registered that has both a
* {@link ca.uhn.fhir.rest.annotation.Read @Read} method and a
* {@link ca.uhn.fhir.rest.annotation.Create @Create} method, the
* create method will be ignored and not bound.
* </p>
* <p>
* Note: This interceptor is not a security interceptor! It can be used to remove
* writes capabilities from a FHIR endpoint (for example) but it does not guarantee
* that writes won't be possible. Security rules should be enforced using
* {@link ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor} or
* a similar strategy. However, this interceptor can be useful in order to
* clarify the intent of an endpoint to the outside world. Of particular note,
* even if a create method has been blocked from binding by this interceptor,
* it may still be possible to create resources via a FHIR transaction unless
* proper security has been implemented.
* </p>
* <p>
* Use {@link Builder new Builder()} to create a new instance of this class.
* </p>
*
* @see Builder#addAllowedSpec(String) to add allowed interactions
* @since 6.2.0
*/
@Interceptor
public class InteractionBlockingInterceptor {
public static final Set<RestOperationTypeEnum> ALLOWED_OP_TYPES;
private static final Logger ourLog = LoggerFactory.getLogger(InteractionBlockingInterceptor.class);
static {
Set<RestOperationTypeEnum> allowedOpTypes = new TreeSet<>();
allowedOpTypes.add(RestOperationTypeEnum.META);
allowedOpTypes.add(RestOperationTypeEnum.META_ADD);
allowedOpTypes.add(RestOperationTypeEnum.META_DELETE);
allowedOpTypes.add(RestOperationTypeEnum.PATCH);
allowedOpTypes.add(RestOperationTypeEnum.READ);
allowedOpTypes.add(RestOperationTypeEnum.CREATE);
allowedOpTypes.add(RestOperationTypeEnum.UPDATE);
allowedOpTypes.add(RestOperationTypeEnum.DELETE);
allowedOpTypes.add(RestOperationTypeEnum.BATCH);
allowedOpTypes.add(RestOperationTypeEnum.TRANSACTION);
allowedOpTypes.add(RestOperationTypeEnum.VALIDATE);
allowedOpTypes.add(RestOperationTypeEnum.SEARCH_TYPE);
allowedOpTypes.add(RestOperationTypeEnum.HISTORY_TYPE);
allowedOpTypes.add(RestOperationTypeEnum.HISTORY_INSTANCE);
allowedOpTypes.add(RestOperationTypeEnum.HISTORY_SYSTEM);
ALLOWED_OP_TYPES = Collections.unmodifiableSet(allowedOpTypes);
}
private final Set<String> myAllowedKeys;
/**
* Constructor
*/
private InteractionBlockingInterceptor(@Nonnull Builder theBuilder) {
myAllowedKeys = theBuilder.myAllowedKeys;
}
@Hook(Pointcut.SERVER_PROVIDER_METHOD_BOUND)
public BaseMethodBinding bindMethod(BaseMethodBinding theMethodBinding) {
boolean allowed = true;
String resourceName = theMethodBinding.getResourceName();
RestOperationTypeEnum restOperationType = theMethodBinding.getRestOperationType();
switch (restOperationType) {
case EXTENDED_OPERATION_SERVER:
case EXTENDED_OPERATION_TYPE:
case EXTENDED_OPERATION_INSTANCE: {
OperationMethodBinding operationMethodBinding = (OperationMethodBinding) theMethodBinding;
if (!myAllowedKeys.isEmpty()) {
if (!myAllowedKeys.contains(operationMethodBinding.getName())) {
allowed = false;
}
}
break;
}
default: {
if (restOperationType == RestOperationTypeEnum.VREAD) {
restOperationType = RestOperationTypeEnum.READ;
}
String key = toKey(resourceName, restOperationType);
if (!myAllowedKeys.isEmpty()) {
if (!myAllowedKeys.contains(key)) {
allowed = false;
}
}
break;
}
}
if (!allowed) {
ourLog.info("Skipping method binding for {}:{} provided by {}", resourceName, restOperationType, theMethodBinding.getMethod());
return null;
}
return theMethodBinding;
}
private static String toKey(String theResourceType, RestOperationTypeEnum theRestOperationTypeEnum) {
if (isBlank(theResourceType)) {
return theRestOperationTypeEnum.getCode();
}
return theResourceType + ":" + theRestOperationTypeEnum.getCode();
}
public static class Builder {
private final Set<String> myAllowedKeys = new HashSet<>();
private final FhirContext myCtx;
/**
* Constructor
*/
public Builder(@Nonnull FhirContext theCtx) {
Validate.notNull(theCtx, "theCtx must not be null");
myCtx = theCtx;
}
/**
* Adds an interaction or operation that will be permitted. Allowable formats
* are:
* <ul>
* <li>
* <b>[resourceType]:[interaction]</b> - Use this form to allow type- and instance-level interactions, such as
* <code>create</code>, <code>read</code>, and <code>patch</code>. For example, the spec <code>Patient:create</code>
* allows the Patient-level create operation (i.e. <code>POST /Patient</code>).
* </li>
* <li>
* <b>$[operation-name]</b> - Use this form to allow operations (at any level) by name. For example, the spec
* <code>$diff</code> permits the <a href="https://hapifhir.io/hapi-fhir/docs/server_jpa/diff.html">Diff Operation</a>
* to be applied at both the server- and instance-level.
* </li>
* </ul>
* <p>
* Note that the spec does not differentiate between the <code>read</code> and <code>vread</code> interactions. If one
* is permitted the other will also be permitted.
* </p>
*
* @return
*/
public Builder addAllowedSpec(String theSpec) {
Validate.notBlank(theSpec, "theSpec must not be null or blank");
if (theSpec.startsWith("$")) {
addAllowedOperation(theSpec);
return this;
}
int colonIdx = theSpec.indexOf(':');
Validate.isTrue(colonIdx > 0, "Invalid interaction allowed spec: %s", theSpec);
String resourceName = theSpec.substring(0, colonIdx);
String interactionName = theSpec.substring(colonIdx + 1);
RestOperationTypeEnum interaction = RestOperationTypeEnum.forCode(interactionName);
Validate.notNull(interaction, "Unknown interaction %s in spec %s", interactionName, theSpec);
addAllowedInteraction(resourceName, interaction);
return this;
}
/**
* Adds an interaction that will be permitted.
*/
private void addAllowedInteraction(String theResourceType, RestOperationTypeEnum theInteractionType) {
Validate.notBlank(theResourceType, "theResourceType must not be null or blank");
Validate.notNull(theInteractionType, "theInteractionType must not be null");
Validate.isTrue(ALLOWED_OP_TYPES.contains(theInteractionType), "Operation type %s can not be used as an allowable rule", theInteractionType);
Validate.isTrue(myCtx.getResourceType(theResourceType) != null, "Unknown resource type: %s");
String key = toKey(theResourceType, theInteractionType);
myAllowedKeys.add(key);
}
private void addAllowedOperation(String theOperationName) {
Validate.notBlank(theOperationName, "theOperationName must not be null or blank");
Validate.isTrue(theOperationName.startsWith("$"), "Invalid operation name: %s", theOperationName);
myAllowedKeys.add(theOperationName);
}
public InteractionBlockingInterceptor build() {
return new InteractionBlockingInterceptor(this);
}
}
}

View File

@ -469,7 +469,7 @@ public class SearchNarrowingInterceptor {
String url = theModifiableBundleEntry.getRequestUrl(); String url = theModifiableBundleEntry.getRequestUrl();
ServletSubRequestDetails subServletRequestDetails = ServletRequestUtil.getServletSubRequestDetails(myRequestDetails, url, paramValues); ServletSubRequestDetails subServletRequestDetails = ServletRequestUtil.getServletSubRequestDetails(myRequestDetails, url, paramValues);
BaseMethodBinding<?> method = subServletRequestDetails.getServer().determineResourceMethod(subServletRequestDetails, url); BaseMethodBinding method = subServletRequestDetails.getServer().determineResourceMethod(subServletRequestDetails, url);
RestOperationTypeEnum restOperationType = method.getRestOperationType(); RestOperationTypeEnum restOperationType = method.getRestOperationType();
subServletRequestDetails.setRestOperationType(restOperationType); subServletRequestDetails.setRestOperationType(restOperationType);

View File

@ -59,7 +59,7 @@ import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank;
public abstract class BaseMethodBinding<T> { public abstract class BaseMethodBinding {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseMethodBinding.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseMethodBinding.class);
private final List<BaseQueryParameter> myQueryParameters; private final List<BaseQueryParameter> myQueryParameters;
@ -331,7 +331,7 @@ public abstract class BaseMethodBinding<T> {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static BaseMethodBinding<?> bindMethod(Method theMethod, FhirContext theContext, Object theProvider) { public static BaseMethodBinding bindMethod(Method theMethod, FhirContext theContext, Object theProvider) {
Read read = theMethod.getAnnotation(Read.class); Read read = theMethod.getAnnotation(Read.class);
Search search = theMethod.getAnnotation(Search.class); Search search = theMethod.getAnnotation(Search.class);
Metadata conformance = theMethod.getAnnotation(Metadata.class); Metadata conformance = theMethod.getAnnotation(Metadata.class);

View File

@ -44,7 +44,7 @@ import java.util.Set;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull; import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<MethodOutcome> { abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class); static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class);
private boolean myReturnVoid; private boolean myReturnVoid;

View File

@ -95,7 +95,7 @@ public abstract class BaseQueryParameter implements IParameter {
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
List<QualifiedParamList> paramList = new ArrayList<>(); List<QualifiedParamList> paramList = new ArrayList<>();
String name = getName(); String name = getName();

View File

@ -72,7 +72,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
* #L% * #L%
*/ */
public abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding<Object> { public abstract class BaseResourceReturningMethodBinding extends BaseMethodBinding {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseResourceReturningMethodBinding.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseResourceReturningMethodBinding.class);
private MethodReturnTypeEnum myMethodReturnType; private MethodReturnTypeEnum myMethodReturnType;

View File

@ -53,7 +53,7 @@ class ConditionalParamBinder implements IParameter {
} }
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
return theRequest.getConditionalUrl(theMethodBinding.getRestOperationType()); return theRequest.getConditionalUrl(theMethodBinding.getRestOperationType());
} }

View File

@ -40,7 +40,7 @@ public class CountParameter implements IParameter {
private Class<?> myType; private Class<?> myType;
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
String[] countParam = theRequest.getParameters().get(Constants.PARAM_COUNT); String[] countParam = theRequest.getParameters().get(Constants.PARAM_COUNT);
if (countParam != null) { if (countParam != null) {
if (countParam.length > 0) { if (countParam.length > 0) {

View File

@ -46,7 +46,7 @@ public class ElementsParameter implements IParameter {
@Override @Override
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
Set<String> value = getElementsValueOrNull(theRequest, false); Set<String> value = getElementsValueOrNull(theRequest, false);
if (value == null || value.isEmpty()) { if (value == null || value.isEmpty()) {
return null; return null;

View File

@ -47,7 +47,7 @@ public class GraphQLQueryBodyParameter implements IParameter {
private Class<?> myType; private Class<?> myType;
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
String ctValue = defaultString(theRequest.getHeader(Constants.HEADER_CONTENT_TYPE)); String ctValue = defaultString(theRequest.getHeader(Constants.HEADER_CONTENT_TYPE));
Reader requestReader = createRequestReader(theRequest); Reader requestReader = createRequestReader(theRequest);

View File

@ -22,15 +22,11 @@ package ca.uhn.fhir.rest.server.method;
import ca.uhn.fhir.i18n.Msg; import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.context.ConfigurationException; import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Count; import ca.uhn.fhir.rest.annotation.Count;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.ParameterUtil;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collection; import java.util.Collection;
@ -40,7 +36,7 @@ public class GraphQLQueryUrlParameter implements IParameter {
private Class<?> myType; private Class<?> myType;
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
String[] queryParams = theRequest.getParameters().get(Constants.PARAM_GRAPHQL_QUERY); String[] queryParams = theRequest.getParameters().get(Constants.PARAM_GRAPHQL_QUERY);
String retVal = null; String retVal = null;
if (queryParams != null) { if (queryParams != null) {

View File

@ -41,7 +41,7 @@ public interface IParameter {
* @param theMethodBinding TODO * @param theMethodBinding TODO
* @return Returns the argument object as it will be passed to the IResourceProvider method. * @return Returns the argument object as it will be passed to the IResourceProvider method.
*/ */
Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException; Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException;
void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType); void initializeTypes(Method theMethod, Class<? extends Collection<?>> theOuterCollectionType, Class<? extends Collection<?>> theInnerCollectionType, Class<?> theParameterType);

View File

@ -30,7 +30,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
class InterceptorBroadcasterParameter implements IParameter { class InterceptorBroadcasterParameter implements IParameter {
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
return theRequest.getInterceptorBroadcaster(); return theRequest.getInterceptorBroadcaster();
} }

View File

@ -31,7 +31,7 @@ class NullParameter implements IParameter {
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
// nothing // nothing
return null; return null;
} }

View File

@ -30,7 +30,6 @@ import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.model.primitive.IntegerDt; import ca.uhn.fhir.model.primitive.IntegerDt;
import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Offset; import ca.uhn.fhir.rest.annotation.Offset;
import ca.uhn.fhir.rest.annotation.Since;
import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.param.ParameterUtil; import ca.uhn.fhir.rest.param.ParameterUtil;
@ -42,7 +41,7 @@ public class OffsetParameter implements IParameter {
private Class<?> myType; private Class<?> myType;
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
String[] sinceParams = theRequest.getParameters().get(Constants.PARAM_OFFSET); String[] sinceParams = theRequest.getParameters().get(Constants.PARAM_OFFSET);
if (sinceParams != null) { if (sinceParams != null) {
if (sinceParams.length > 0) { if (sinceParams.length > 0) {

View File

@ -251,7 +251,7 @@ public class OperationParameter implements IParameter {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
List<Object> matchingParamValues = new ArrayList<Object>(); List<Object> matchingParamValues = new ArrayList<Object>();
OperationMethodBinding method = (OperationMethodBinding) theMethodBinding; OperationMethodBinding method = (OperationMethodBinding) theMethodBinding;

View File

@ -35,7 +35,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
class PatchTypeParameter implements IParameter { class PatchTypeParameter implements IParameter {
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
return getTypeForRequestOrThrowInvalidRequestException(theRequest); return getTypeForRequestOrThrowInvalidRequestException(theRequest);
} }

View File

@ -45,7 +45,7 @@ public class RawParamsParameter implements IParameter {
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
HashMap<String, List<String>> retVal = null; HashMap<String, List<String>> retVal = null;
for (String nextName : theRequest.getParameters().keySet()) { for (String nextName : theRequest.getParameters().keySet()) {

View File

@ -97,7 +97,7 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
@Override @Override
public List<Class<?>> getAllowableParamAnnotations() { public List<Class<?>> getAllowableParamAnnotations() {
ArrayList<Class<?>> retVal = new ArrayList<Class<?>>(); ArrayList<Class<?>> retVal = new ArrayList<>();
retVal.add(IdParam.class); retVal.add(IdParam.class);
retVal.add(Elements.class); retVal.add(Elements.class);
return retVal; return retVal;

View File

@ -32,7 +32,7 @@ public class RequestDetailsParameter implements IParameter {
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
return theRequest; return theRequest;
} }

View File

@ -93,7 +93,7 @@ public class ResourceParameter implements IParameter {
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
switch (myMode) { switch (myMode) {
case BODY: case BODY:
try { try {
@ -141,7 +141,7 @@ public class ResourceParameter implements IParameter {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <T extends IBaseResource> T loadResourceFromRequest(RequestDetails theRequest, @Nonnull BaseMethodBinding<?> theMethodBinding, Class<T> theResourceType) { static <T extends IBaseResource> T loadResourceFromRequest(RequestDetails theRequest, @Nonnull BaseMethodBinding theMethodBinding, Class<T> theResourceType) {
FhirContext ctx = theRequest.getServer().getFhirContext(); FhirContext ctx = theRequest.getServer().getFhirContext();
final Charset charset = determineRequestCharset(theRequest); final Charset charset = determineRequestCharset(theRequest);
@ -195,7 +195,7 @@ public class ResourceParameter implements IParameter {
return retVal; return retVal;
} }
static IBaseResource parseResourceFromRequest(RequestDetails theRequest, @Nonnull BaseMethodBinding<?> theMethodBinding, Class<? extends IBaseResource> theResourceType) { static IBaseResource parseResourceFromRequest(RequestDetails theRequest, @Nonnull BaseMethodBinding theMethodBinding, Class<? extends IBaseResource> theResourceType) {
if (theRequest.getResource() != null) { if (theRequest.getResource() != null) {
return theRequest.getResource(); return theRequest.getResource();
} }

View File

@ -34,7 +34,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
class SearchContainedModeParameter implements IParameter { class SearchContainedModeParameter implements IParameter {
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
return getTypeForRequestOrThrowInvalidRequestException(theRequest); return getTypeForRequestOrThrowInvalidRequestException(theRequest);
} }

View File

@ -32,7 +32,7 @@ import java.util.Collection;
class SearchTotalModeParameter implements IParameter { class SearchTotalModeParameter implements IParameter {
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
return getTypeForRequestOrThrowInvalidRequestException(theRequest); return getTypeForRequestOrThrowInvalidRequestException(theRequest);
} }

View File

@ -30,7 +30,7 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
class ServerBaseParamBinder implements IParameter { class ServerBaseParamBinder implements IParameter {
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
return theRequest.getFhirServerBase(); return theRequest.getFhirServerBase();
} }

View File

@ -36,7 +36,7 @@ class ServletRequestParameter implements IParameter {
} }
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
return ((ServletRequestDetails) theRequest).getServletRequest(); return ((ServletRequestDetails) theRequest).getServletRequest();
} }

View File

@ -32,7 +32,7 @@ class ServletResponseParameter implements IParameter {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletResponseParameter.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletResponseParameter.class);
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
return ((ServletRequestDetails) theRequest).getServletResponse(); return ((ServletRequestDetails) theRequest).getServletResponse();
} }

View File

@ -56,7 +56,7 @@ public class SortParameter implements IParameter {
} }
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT)) { if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT)) {
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_ASC)) { if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_ASC)) {
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_DESC)) { if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_DESC)) {

View File

@ -41,7 +41,7 @@ public class SummaryEnumParameter implements IParameter {
@Override @Override
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes", "unchecked" })
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
Set<SummaryEnum> value = getSummaryValueOrNull(theRequest); Set<SummaryEnum> value = getSummaryValueOrNull(theRequest);
if (value == null || value.isEmpty()) { if (value == null || value.isEmpty()) {
return null; return null;

View File

@ -88,7 +88,7 @@ public class TransactionParameter implements IParameter {
} }
@Override @Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException { public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
IBaseResource parsedBundle = ResourceParameter.parseResourceFromRequest(theRequest, theMethodBinding, myResourceBundleType); IBaseResource parsedBundle = ResourceParameter.parseResourceFromRequest(theRequest, theMethodBinding, myResourceBundleType);
switch (myParamStyle) { switch (myParamStyle) {

View File

@ -20,6 +20,7 @@ import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.RestfulServerConfiguration; import ca.uhn.fhir.rest.server.RestfulServerConfiguration;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.method.BaseMethodBinding; import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.IParameter; import ca.uhn.fhir.rest.server.method.IParameter;
import ca.uhn.fhir.rest.server.method.OperationMethodBinding; import ca.uhn.fhir.rest.server.method.OperationMethodBinding;
import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType; import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType;
@ -134,7 +135,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
myValidationSupport = theValidationSupport; myValidationSupport = theValidationSupport;
} }
private void checkBindingForSystemOps(FhirTerser theTerser, IBase theRest, Set<String> theSystemOps, BaseMethodBinding<?> theMethodBinding) { private void checkBindingForSystemOps(FhirTerser theTerser, IBase theRest, Set<String> theSystemOps, BaseMethodBinding theMethodBinding) {
RestOperationTypeEnum restOperationType = theMethodBinding.getRestOperationType(); RestOperationTypeEnum restOperationType = theMethodBinding.getRestOperationType();
if (restOperationType.isSystemLevel()) { if (restOperationType.isSystemLevel()) {
String sysOp = restOperationType.getCode(); String sysOp = restOperationType.getCode();
@ -236,15 +237,15 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
Set<String> systemOps = new HashSet<>(); Set<String> systemOps = new HashSet<>();
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = configuration.collectMethodBindings(); Map<String, List<BaseMethodBinding>> resourceToMethods = configuration.collectMethodBindings();
Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype = configuration.getNameToSharedSupertype(); Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype = configuration.getNameToSharedSupertype();
List<BaseMethodBinding<?>> globalMethodBindings = configuration.getGlobalBindings(); List<BaseMethodBinding> globalMethodBindings = configuration.getGlobalBindings();
TreeMultimap<String, String> resourceNameToIncludes = TreeMultimap.create(); TreeMultimap<String, String> resourceNameToIncludes = TreeMultimap.create();
TreeMultimap<String, String> resourceNameToRevIncludes = TreeMultimap.create(); TreeMultimap<String, String> resourceNameToRevIncludes = TreeMultimap.create();
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) { for (Entry<String, List<BaseMethodBinding>> nextEntry : resourceToMethods.entrySet()) {
String resourceName = nextEntry.getKey(); String resourceName = nextEntry.getKey();
for (BaseMethodBinding<?> nextMethod : nextEntry.getValue()) { for (BaseMethodBinding nextMethod : nextEntry.getValue()) {
if (nextMethod instanceof SearchMethodBinding) { if (nextMethod instanceof SearchMethodBinding) {
resourceNameToIncludes.putAll(resourceName, nextMethod.getIncludes()); resourceNameToIncludes.putAll(resourceName, nextMethod.getIncludes());
resourceNameToRevIncludes.putAll(resourceName, nextMethod.getRevIncludes()); resourceNameToRevIncludes.putAll(resourceName, nextMethod.getRevIncludes());
@ -253,7 +254,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
} }
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) { for (Entry<String, List<BaseMethodBinding>> nextEntry : resourceToMethods.entrySet()) {
Set<String> operationNames = new HashSet<>(); Set<String> operationNames = new HashSet<>();
String resourceName = nextEntry.getKey(); String resourceName = nextEntry.getKey();
@ -273,7 +274,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
terser.addElement(resource, "type", def.getName()); terser.addElement(resource, "type", def.getName());
terser.addElement(resource, "profile", def.getResourceProfile(serverBase)); terser.addElement(resource, "profile", def.getResourceProfile(serverBase));
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { for (BaseMethodBinding nextMethodBinding : nextEntry.getValue()) {
RestOperationTypeEnum resOpCode = nextMethodBinding.getRestOperationType(); RestOperationTypeEnum resOpCode = nextMethodBinding.getRestOperationType();
if (resOpCode.isTypeLevel() || resOpCode.isInstanceLevel()) { if (resOpCode.isTypeLevel() || resOpCode.isInstanceLevel()) {
String resOp; String resOp;
@ -358,7 +359,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
// global flag set to true, meaning they apply to all resource types) // global flag set to true, meaning they apply to all resource types)
if (globalMethodBindings != null) { if (globalMethodBindings != null) {
Set<String> globalOperationNames = new HashSet<>(); Set<String> globalOperationNames = new HashSet<>();
for (BaseMethodBinding<?> next : globalMethodBindings) { for (BaseMethodBinding next : globalMethodBindings) {
if (next instanceof OperationMethodBinding) { if (next instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) next; OperationMethodBinding methodBinding = (OperationMethodBinding) next;
if (methodBinding.isGlobalMethod()) { if (methodBinding.isGlobalMethod()) {
@ -482,7 +483,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
} }
} else { } else {
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { for (BaseMethodBinding nextMethodBinding : nextEntry.getValue()) {
checkBindingForSystemOps(terser, rest, systemOps, nextMethodBinding); checkBindingForSystemOps(terser, rest, systemOps, nextMethodBinding);
if (nextMethodBinding instanceof OperationMethodBinding) { if (nextMethodBinding instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding; OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
@ -507,7 +508,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
// global flag set to true, meaning they apply to all resource types) // global flag set to true, meaning they apply to all resource types)
if (globalMethodBindings != null) { if (globalMethodBindings != null) {
Set<String> globalOperationNames = new HashSet<>(); Set<String> globalOperationNames = new HashSet<>();
for (BaseMethodBinding<?> next : globalMethodBindings) { for (BaseMethodBinding next : globalMethodBindings) {
if (next instanceof OperationMethodBinding) { if (next instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) next; OperationMethodBinding methodBinding = (OperationMethodBinding) next;
if (methodBinding.isGlobalMethod()) { if (methodBinding.isGlobalMethod()) {

View File

@ -25,15 +25,15 @@ class ResourceBindingTest {
public void testFILO() throws NoSuchMethodException { public void testFILO() throws NoSuchMethodException {
// setup // setup
Method method = ResourceBindingTest.class.getMethod("testFILO"); Method method = ResourceBindingTest.class.getMethod("testFILO");
BaseMethodBinding<?> first = new PageMethodBinding(ourFhirContext, method); BaseMethodBinding first = new PageMethodBinding(ourFhirContext, method);
BaseMethodBinding<?> second = new PageMethodBinding(ourFhirContext, method);; BaseMethodBinding second = new PageMethodBinding(ourFhirContext, method);;
// execute // execute
myResourceBinding.addMethod(first); myResourceBinding.addMethod(first);
myResourceBinding.addMethod(second); myResourceBinding.addMethod(second);
// verify // verify
List<BaseMethodBinding<?>> list = myResourceBinding.getMethodBindings(); List<BaseMethodBinding> list = myResourceBinding.getMethodBindings();
assertNotEquals(first, second); assertNotEquals(first, second);
assertEquals(second, list.get(0)); assertEquals(second, list.get(0));
assertEquals(first, list.get(1)); assertEquals(first, list.get(1));

View File

@ -32,9 +32,9 @@ import ca.uhn.fhir.batch2.jobs.export.models.BulkExportJobParameters;
import ca.uhn.fhir.batch2.model.ChunkOutcome; import ca.uhn.fhir.batch2.model.ChunkOutcome;
import ca.uhn.fhir.batch2.model.JobInstance; import ca.uhn.fhir.batch2.model.JobInstance;
import ca.uhn.fhir.jpa.api.model.BulkExportJobResults; import ca.uhn.fhir.jpa.api.model.BulkExportJobResults;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger; import org.slf4j.Logger;
import javax.annotation.Nonnull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -47,10 +47,10 @@ public class BulkExportCreateReportStep implements IReductionStepWorker<BulkExpo
private Map<String, List<String>> myResourceToBinaryIds; private Map<String, List<String>> myResourceToBinaryIds;
@NotNull @Nonnull
@Override @Override
public RunOutcome run(@NotNull StepExecutionDetails<BulkExportJobParameters, BulkExportBinaryFileId> theStepExecutionDetails, public RunOutcome run(@Nonnull StepExecutionDetails<BulkExportJobParameters, BulkExportBinaryFileId> theStepExecutionDetails,
@NotNull IJobDataSink<BulkExportJobResults> theDataSink) throws JobExecutionFailedException { @Nonnull IJobDataSink<BulkExportJobResults> theDataSink) throws JobExecutionFailedException {
BulkExportJobResults results = new BulkExportJobResults(); BulkExportJobResults results = new BulkExportJobResults();
String requestUrl = getOriginatingRequestUrl(theStepExecutionDetails, results); String requestUrl = getOriginatingRequestUrl(theStepExecutionDetails, results);
@ -74,7 +74,7 @@ public class BulkExportCreateReportStep implements IReductionStepWorker<BulkExpo
return RunOutcome.SUCCESS; return RunOutcome.SUCCESS;
} }
private static String getOriginatingRequestUrl(@NotNull StepExecutionDetails<BulkExportJobParameters, BulkExportBinaryFileId> theStepExecutionDetails, BulkExportJobResults results) { private static String getOriginatingRequestUrl(@Nonnull StepExecutionDetails<BulkExportJobParameters, BulkExportBinaryFileId> theStepExecutionDetails, BulkExportJobResults results) {
IJobInstance instance = theStepExecutionDetails.getInstance(); IJobInstance instance = theStepExecutionDetails.getInstance();
String url = ""; String url = "";
if (instance instanceof JobInstance) { if (instance instanceof JobInstance) {
@ -86,7 +86,7 @@ public class BulkExportCreateReportStep implements IReductionStepWorker<BulkExpo
return url; return url;
} }
@NotNull @Nonnull
@Override @Override
public ChunkOutcome consume(ChunkExecutionDetails<BulkExportJobParameters, public ChunkOutcome consume(ChunkExecutionDetails<BulkExportJobParameters,
BulkExportBinaryFileId> theChunkDetails) { BulkExportBinaryFileId> theChunkDetails) {

View File

@ -22,9 +22,9 @@ package ca.uhn.fhir.batch2.jobs.expunge;
import ca.uhn.fhir.batch2.api.IJobParametersValidator; import ca.uhn.fhir.batch2.api.IJobParametersValidator;
import ca.uhn.fhir.batch2.jobs.parameters.UrlListValidator; import ca.uhn.fhir.batch2.jobs.parameters.UrlListValidator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List; import java.util.List;
public class DeleteExpungeJobParametersValidator implements IJobParametersValidator<DeleteExpungeJobParameters> { public class DeleteExpungeJobParametersValidator implements IJobParametersValidator<DeleteExpungeJobParameters> {
@ -36,7 +36,7 @@ public class DeleteExpungeJobParametersValidator implements IJobParametersValida
@Nullable @Nullable
@Override @Override
public List<String> validate(@NotNull DeleteExpungeJobParameters theParameters) { public List<String> validate(@Nonnull DeleteExpungeJobParameters theParameters) {
return myUrlListValidator.validatePartitionedUrls(theParameters.getPartitionedUrls()); return myUrlListValidator.validatePartitionedUrls(theParameters.getPartitionedUrls());
} }
} }

View File

@ -23,14 +23,11 @@ package ca.uhn.fhir.batch2.jobs.reindex;
import ca.uhn.fhir.batch2.api.IJobParametersValidator; import ca.uhn.fhir.batch2.api.IJobParametersValidator;
import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrl; import ca.uhn.fhir.batch2.jobs.parameters.PartitionedUrl;
import ca.uhn.fhir.batch2.jobs.parameters.UrlListValidator; import ca.uhn.fhir.batch2.jobs.parameters.UrlListValidator;
import ca.uhn.fhir.util.UrlUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ReindexJobParametersValidator implements IJobParametersValidator<ReindexJobParameters> { public class ReindexJobParametersValidator implements IJobParametersValidator<ReindexJobParameters> {
@ -42,7 +39,7 @@ public class ReindexJobParametersValidator implements IJobParametersValidator<Re
@Nullable @Nullable
@Override @Override
public List<String> validate(@NotNull ReindexJobParameters theParameters) { public List<String> validate(@Nonnull ReindexJobParameters theParameters) {
List<String> errors = myUrlListValidator.validatePartitionedUrls(theParameters.getPartitionedUrls()); List<String> errors = myUrlListValidator.validatePartitionedUrls(theParameters.getPartitionedUrls());
if (errors == null || errors.isEmpty()) { if (errors == null || errors.isEmpty()) {

View File

@ -547,7 +547,7 @@ public abstract class BaseTransactionProcessor {
String url = requestDetails.getRequestPath(); String url = requestDetails.getRequestPath();
BaseMethodBinding<?> method = srd.getServer().determineResourceMethod(requestDetails, url); BaseMethodBinding method = srd.getServer().determineResourceMethod(requestDetails, url);
if (method == null) { if (method == null) {
throw new IllegalArgumentException(Msg.code(532) + "Unable to handle GET " + url); throw new IllegalArgumentException(Msg.code(532) + "Unable to handle GET " + url);
} }

View File

@ -89,7 +89,7 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
super(theServerConfiguration); super(theServerConfiguration);
} }
private void checkBindingForSystemOps(ConformanceRestComponent rest, Set<SystemRestfulInteraction> systemOps, BaseMethodBinding<?> nextMethodBinding) { private void checkBindingForSystemOps(ConformanceRestComponent rest, Set<SystemRestfulInteraction> systemOps, BaseMethodBinding nextMethodBinding) {
if (nextMethodBinding.getRestOperationType() != null) { if (nextMethodBinding.getRestOperationType() != null) {
String sysOpCode = nextMethodBinding.getRestOperationType().getCode(); String sysOpCode = nextMethodBinding.getRestOperationType().getCode();
if (sysOpCode != null) { if (sysOpCode != null) {
@ -110,18 +110,18 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
} }
} }
private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings(RequestDetails theRequestDetails) { private Map<String, List<BaseMethodBinding>> collectMethodBindings(RequestDetails theRequestDetails) {
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<>(); Map<String, List<BaseMethodBinding>> resourceToMethods = new TreeMap<>();
for (ResourceBinding next : getServerConfiguration(theRequestDetails).getResourceBindings()) { for (ResourceBinding next : getServerConfiguration(theRequestDetails).getResourceBindings()) {
String resourceName = next.getResourceName(); String resourceName = next.getResourceName();
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) { for (BaseMethodBinding nextMethodBinding : next.getMethodBindings()) {
if (resourceToMethods.containsKey(resourceName) == false) { if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<>()); resourceToMethods.put(resourceName, new ArrayList<>());
} }
resourceToMethods.get(resourceName).add(nextMethodBinding); resourceToMethods.get(resourceName).add(nextMethodBinding);
} }
} }
for (BaseMethodBinding<?> nextMethodBinding : getServerConfiguration(theRequestDetails).getServerBindings()) { for (BaseMethodBinding nextMethodBinding : getServerConfiguration(theRequestDetails).getServerBindings()) {
String resourceName = ""; String resourceName = "";
if (resourceToMethods.containsKey(resourceName) == false) { if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<>()); resourceToMethods.put(resourceName, new ArrayList<>());
@ -208,8 +208,8 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
Set<SystemRestfulInteraction> systemOps = new HashSet<>(); Set<SystemRestfulInteraction> systemOps = new HashSet<>();
Set<String> operationNames = new HashSet<>(); Set<String> operationNames = new HashSet<>();
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings(theRequestDetails); Map<String, List<BaseMethodBinding>> resourceToMethods = collectMethodBindings(theRequestDetails);
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) { for (Entry<String, List<BaseMethodBinding>> nextEntry : resourceToMethods.entrySet()) {
if (nextEntry.getKey().isEmpty() == false) { if (nextEntry.getKey().isEmpty() == false) {
Set<TypeRestfulInteraction> resourceOps = new HashSet<>(); Set<TypeRestfulInteraction> resourceOps = new HashSet<>();
@ -223,7 +223,7 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
// Map<String, Conformance.RestResourceSearchParam> nameToSearchParam = new HashMap<String, // Map<String, Conformance.RestResourceSearchParam> nameToSearchParam = new HashMap<String,
// Conformance.RestResourceSearchParam>(); // Conformance.RestResourceSearchParam>();
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { for (BaseMethodBinding nextMethodBinding : nextEntry.getValue()) {
if (nextMethodBinding.getRestOperationType() != null) { if (nextMethodBinding.getRestOperationType() != null) {
String resOpCode = nextMethodBinding.getRestOperationType().getCode(); String resOpCode = nextMethodBinding.getRestOperationType().getCode();
if (resOpCode != null) { if (resOpCode != null) {
@ -307,7 +307,7 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
resource.addSearchInclude(nextInclude); resource.addSearchInclude(nextInclude);
} }
} else { } else {
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { for (BaseMethodBinding nextMethodBinding : nextEntry.getValue()) {
checkBindingForSystemOps(rest, systemOps, nextMethodBinding); checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
if (nextMethodBinding instanceof OperationMethodBinding) { if (nextMethodBinding instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding; OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;

View File

@ -89,7 +89,7 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
super(theServerConfiguration); super(theServerConfiguration);
} }
private void checkBindingForSystemOps(Rest rest, Set<SystemRestfulInteractionEnum> systemOps, BaseMethodBinding<?> nextMethodBinding) { private void checkBindingForSystemOps(Rest rest, Set<SystemRestfulInteractionEnum> systemOps, BaseMethodBinding nextMethodBinding) {
if (nextMethodBinding.getRestOperationType() != null) { if (nextMethodBinding.getRestOperationType() != null) {
String sysOpCode = nextMethodBinding.getRestOperationType().getCode(); String sysOpCode = nextMethodBinding.getRestOperationType().getCode();
if (sysOpCode != null) { if (sysOpCode != null) {
@ -105,21 +105,21 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
} }
} }
private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings(RequestDetails theRequestDetails) { 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<String, List<BaseMethodBinding>>();
for (ResourceBinding next : getServerConfiguration(theRequestDetails).getResourceBindings()) { for (ResourceBinding next : getServerConfiguration(theRequestDetails).getResourceBindings()) {
String resourceName = next.getResourceName(); String resourceName = next.getResourceName();
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) { for (BaseMethodBinding nextMethodBinding : next.getMethodBindings()) {
if (resourceToMethods.containsKey(resourceName) == false) { if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>()); resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding>());
} }
resourceToMethods.get(resourceName).add(nextMethodBinding); resourceToMethods.get(resourceName).add(nextMethodBinding);
} }
} }
for (BaseMethodBinding<?> nextMethodBinding : getServerConfiguration(theRequestDetails).getServerBindings()) { for (BaseMethodBinding nextMethodBinding : getServerConfiguration(theRequestDetails).getServerBindings()) {
String resourceName = ""; String resourceName = "";
if (resourceToMethods.containsKey(resourceName) == false) { if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>()); resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding>());
} }
resourceToMethods.get(resourceName).add(nextMethodBinding); resourceToMethods.get(resourceName).add(nextMethodBinding);
} }
@ -209,8 +209,8 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
Set<SystemRestfulInteractionEnum> systemOps = new HashSet<>(); Set<SystemRestfulInteractionEnum> systemOps = new HashSet<>();
Set<String> operationNames = new HashSet<>(); Set<String> operationNames = new HashSet<>();
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings(theRequestDetails); Map<String, List<BaseMethodBinding>> resourceToMethods = collectMethodBindings(theRequestDetails);
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) { for (Entry<String, List<BaseMethodBinding>> nextEntry : resourceToMethods.entrySet()) {
if (nextEntry.getKey().isEmpty() == false) { if (nextEntry.getKey().isEmpty() == false) {
Set<TypeRestfulInteractionEnum> resourceOps = new HashSet<>(); Set<TypeRestfulInteractionEnum> resourceOps = new HashSet<>();
@ -224,7 +224,7 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
// Map<String, Conformance.RestResourceSearchParam> nameToSearchParam = new HashMap<String, // Map<String, Conformance.RestResourceSearchParam> nameToSearchParam = new HashMap<String,
// Conformance.RestResourceSearchParam>(); // Conformance.RestResourceSearchParam>();
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { for (BaseMethodBinding nextMethodBinding : nextEntry.getValue()) {
if (nextMethodBinding.getRestOperationType() != null) { if (nextMethodBinding.getRestOperationType() != null) {
String resOpCode = nextMethodBinding.getRestOperationType().getCode(); String resOpCode = nextMethodBinding.getRestOperationType().getCode();
if (resOpCode != null) { if (resOpCode != null) {
@ -303,7 +303,7 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
resource.addSearchInclude(nextInclude); resource.addSearchInclude(nextInclude);
} }
} else { } else {
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { for (BaseMethodBinding nextMethodBinding : nextEntry.getValue()) {
checkBindingForSystemOps(rest, systemOps, nextMethodBinding); checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
if (nextMethodBinding instanceof OperationMethodBinding) { if (nextMethodBinding instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding; OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;

View File

@ -229,7 +229,7 @@ public class ServerConformanceProviderDstu2Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().iterator().next(); SearchParameter param = (SearchParameter) binding.getParameters().iterator().next();
assertEquals("The patient's identifier", param.getDescription()); assertEquals("The patient's identifier", param.getDescription());
@ -459,7 +459,7 @@ public class ServerConformanceProviderDstu2Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
for (IParameter next : binding.getParameters()) { for (IParameter next : binding.getParameters()) {
SearchParameter param = (SearchParameter) next; SearchParameter param = (SearchParameter) next;
@ -499,7 +499,7 @@ public class ServerConformanceProviderDstu2Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(24); SearchParameter param = (SearchParameter) binding.getParameters().get(24);
assertEquals("The organization at which this person is a patient", param.getDescription()); assertEquals("The organization at which this person is a patient", param.getDescription());
@ -533,7 +533,7 @@ public class ServerConformanceProviderDstu2Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(0); SearchParameter param = (SearchParameter) binding.getParameters().get(0);
assertEquals("The organization at which this person is a patient", param.getDescription()); assertEquals("The organization at which this person is a patient", param.getDescription());
@ -569,7 +569,7 @@ public class ServerConformanceProviderDstu2Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(0); SearchParameter param = (SearchParameter) binding.getParameters().get(0);
assertEquals("The organization at which this person is a patient", param.getDescription()); assertEquals("The organization at which this person is a patient", param.getDescription());

View File

@ -94,7 +94,7 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
super(theServerConfiguration); super(theServerConfiguration);
} }
private void checkBindingForSystemOps(CapabilityStatementRestComponent rest, Set<SystemRestfulInteraction> systemOps, BaseMethodBinding<?> nextMethodBinding) { private void checkBindingForSystemOps(CapabilityStatementRestComponent rest, Set<SystemRestfulInteraction> systemOps, BaseMethodBinding nextMethodBinding) {
if (nextMethodBinding.getRestOperationType() != null) { if (nextMethodBinding.getRestOperationType() != null) {
String sysOpCode = nextMethodBinding.getRestOperationType().getCode(); String sysOpCode = nextMethodBinding.getRestOperationType().getCode();
if (sysOpCode != null) { if (sysOpCode != null) {
@ -115,18 +115,18 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
} }
} }
private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings(RequestDetails theRequestDetails) { private Map<String, List<BaseMethodBinding>> collectMethodBindings(RequestDetails theRequestDetails) {
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<>(); Map<String, List<BaseMethodBinding>> resourceToMethods = new TreeMap<>();
for (ResourceBinding next : getServerConfiguration(theRequestDetails).getResourceBindings()) { for (ResourceBinding next : getServerConfiguration(theRequestDetails).getResourceBindings()) {
String resourceName = next.getResourceName(); String resourceName = next.getResourceName();
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) { for (BaseMethodBinding nextMethodBinding : next.getMethodBindings()) {
if (resourceToMethods.containsKey(resourceName) == false) { if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<>()); resourceToMethods.put(resourceName, new ArrayList<>());
} }
resourceToMethods.get(resourceName).add(nextMethodBinding); resourceToMethods.get(resourceName).add(nextMethodBinding);
} }
} }
for (BaseMethodBinding<?> nextMethodBinding : getServerConfiguration(theRequestDetails).getServerBindings()) { for (BaseMethodBinding nextMethodBinding : getServerConfiguration(theRequestDetails).getServerBindings()) {
String resourceName = ""; String resourceName = "";
if (resourceToMethods.containsKey(resourceName) == false) { if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<>()); resourceToMethods.put(resourceName, new ArrayList<>());
@ -235,9 +235,9 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
Set<SystemRestfulInteraction> systemOps = new HashSet<>(); Set<SystemRestfulInteraction> systemOps = new HashSet<>();
Set<String> operationNames = new HashSet<>(); Set<String> operationNames = new HashSet<>();
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings(theRequestDetails); Map<String, List<BaseMethodBinding>> resourceToMethods = collectMethodBindings(theRequestDetails);
Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype = serverConfiguration.getNameToSharedSupertype(); Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype = serverConfiguration.getNameToSharedSupertype();
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) { for (Entry<String, List<BaseMethodBinding>> nextEntry : resourceToMethods.entrySet()) {
if (nextEntry.getKey().isEmpty() == false) { if (nextEntry.getKey().isEmpty() == false) {
Set<TypeRestfulInteraction> resourceOps = new HashSet<>(); Set<TypeRestfulInteraction> resourceOps = new HashSet<>();
@ -258,7 +258,7 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
// Map<String, CapabilityStatement.RestResourceSearchParam> nameToSearchParam = new HashMap<String, // Map<String, CapabilityStatement.RestResourceSearchParam> nameToSearchParam = new HashMap<String,
// CapabilityStatement.RestResourceSearchParam>(); // CapabilityStatement.RestResourceSearchParam>();
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { for (BaseMethodBinding nextMethodBinding : nextEntry.getValue()) {
if (nextMethodBinding.getRestOperationType() != null) { if (nextMethodBinding.getRestOperationType() != null) {
String resOpCode = nextMethodBinding.getRestOperationType().getCode(); String resOpCode = nextMethodBinding.getRestOperationType().getCode();
if (resOpCode != null) { if (resOpCode != null) {
@ -350,7 +350,7 @@ public class ServerCapabilityStatementProvider extends BaseServerCapabilityState
resource.addSearchInclude(nextInclude); resource.addSearchInclude(nextInclude);
} }
} else { } else {
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { for (BaseMethodBinding nextMethodBinding : nextEntry.getValue()) {
checkBindingForSystemOps(rest, systemOps, nextMethodBinding); checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
if (nextMethodBinding instanceof OperationMethodBinding) { if (nextMethodBinding instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding; OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;

View File

@ -74,7 +74,6 @@ import org.junit.jupiter.api.Test;
import javax.servlet.ServletConfig; import javax.servlet.ServletConfig;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.tools.Diagnostic;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
@ -153,7 +152,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(0); SearchParameter param = (SearchParameter) binding.getParameters().get(0);
assertEquals("The organization at which this person is a patient", param.getDescription()); assertEquals("The organization at which this person is a patient", param.getDescription());
@ -304,7 +303,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().iterator().next(); SearchParameter param = (SearchParameter) binding.getParameters().iterator().next();
assertEquals("The patient's identifier", param.getDescription()); assertEquals("The patient's identifier", param.getDescription());
@ -521,7 +520,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
for (IParameter next : binding.getParameters()) { for (IParameter next : binding.getParameters()) {
SearchParameter param = (SearchParameter) next; SearchParameter param = (SearchParameter) next;
@ -561,7 +560,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(25); SearchParameter param = (SearchParameter) binding.getParameters().get(25);
assertEquals("The organization at which this person is a patient", param.getDescription()); assertEquals("The organization at which this person is a patient", param.getDescription());
@ -594,7 +593,7 @@ public class ServerCapabilityStatementProviderDstu3Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(0); SearchParameter param = (SearchParameter) binding.getParameters().get(0);
assertEquals("The organization at which this person is a patient", param.getDescription()); assertEquals("The organization at which this person is a patient", param.getDescription());

View File

@ -111,7 +111,7 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
} }
private void checkBindingForSystemOps(ConformanceRestComponent rest, Set<SystemRestfulInteraction> systemOps, private void checkBindingForSystemOps(ConformanceRestComponent rest, Set<SystemRestfulInteraction> systemOps,
BaseMethodBinding<?> nextMethodBinding) { BaseMethodBinding nextMethodBinding) {
if (nextMethodBinding.getRestOperationType() != null) { if (nextMethodBinding.getRestOperationType() != null) {
String sysOpCode = nextMethodBinding.getRestOperationType().getCode(); String sysOpCode = nextMethodBinding.getRestOperationType().getCode();
if (sysOpCode != null) { if (sysOpCode != null) {
@ -132,18 +132,18 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
} }
} }
private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings(RequestDetails theRequestDetails) { 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<String, List<BaseMethodBinding>>();
for (ResourceBinding next : getServerConfiguration(theRequestDetails).getResourceBindings()) { for (ResourceBinding next : getServerConfiguration(theRequestDetails).getResourceBindings()) {
String resourceName = next.getResourceName(); String resourceName = next.getResourceName();
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) { for (BaseMethodBinding nextMethodBinding : next.getMethodBindings()) {
if (resourceToMethods.containsKey(resourceName) == false) { if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>()); resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding>());
} }
resourceToMethods.get(resourceName).add(nextMethodBinding); resourceToMethods.get(resourceName).add(nextMethodBinding);
} }
} }
for (BaseMethodBinding<?> nextMethodBinding : getServerConfiguration(theRequestDetails).getServerBindings()) { for (BaseMethodBinding nextMethodBinding : getServerConfiguration(theRequestDetails).getServerBindings()) {
String resourceName = ""; String resourceName = "";
if (resourceToMethods.containsKey(resourceName) == false) { if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<>()); resourceToMethods.put(resourceName, new ArrayList<>());
@ -196,8 +196,8 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
Set<SystemRestfulInteraction> systemOps = new HashSet<>(); Set<SystemRestfulInteraction> systemOps = new HashSet<>();
Set<String> operationNames = new HashSet<>(); Set<String> operationNames = new HashSet<>();
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings(theRequestDetails); Map<String, List<BaseMethodBinding>> resourceToMethods = collectMethodBindings(theRequestDetails);
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) { for (Entry<String, List<BaseMethodBinding>> nextEntry : resourceToMethods.entrySet()) {
if (nextEntry.getKey().isEmpty() == false) { if (nextEntry.getKey().isEmpty() == false) {
Set<TypeRestfulInteraction> resourceOps = new HashSet<>(); Set<TypeRestfulInteraction> resourceOps = new HashSet<>();
@ -214,7 +214,7 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
// Map<String, Conformance.RestResourceSearchParam> nameToSearchParam = // Map<String, Conformance.RestResourceSearchParam> nameToSearchParam =
// new HashMap<String, // new HashMap<String,
// Conformance.RestResourceSearchParam>(); // Conformance.RestResourceSearchParam>();
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { for (BaseMethodBinding nextMethodBinding : nextEntry.getValue()) {
if (nextMethodBinding.getRestOperationType() != null) { if (nextMethodBinding.getRestOperationType() != null) {
String resOpCode = nextMethodBinding.getRestOperationType().getCode(); String resOpCode = nextMethodBinding.getRestOperationType().getCode();
if (resOpCode != null) { if (resOpCode != null) {
@ -296,7 +296,7 @@ public class ServerConformanceProvider extends BaseServerCapabilityStatementProv
resource.addSearchInclude(nextInclude); resource.addSearchInclude(nextInclude);
} }
} else { } else {
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) { for (BaseMethodBinding nextMethodBinding : nextEntry.getValue()) {
checkBindingForSystemOps(rest, systemOps, nextMethodBinding); checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
if (nextMethodBinding instanceof OperationMethodBinding) { if (nextMethodBinding instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding; OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;

View File

@ -169,7 +169,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().iterator().next(); SearchParameter param = (SearchParameter) binding.getParameters().iterator().next();
assertEquals("The patient's identifier", param.getDescription()); assertEquals("The patient's identifier", param.getDescription());
@ -225,7 +225,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().iterator().next(); SearchParameter param = (SearchParameter) binding.getParameters().iterator().next();
assertEquals("The patient's identifier (MRN or other card number)", param.getDescription()); assertEquals("The patient's identifier (MRN or other card number)", param.getDescription());
@ -328,7 +328,7 @@ public class ServerConformanceProviderHl7OrgDstu2Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().iterator().next(); SearchParameter param = (SearchParameter) binding.getParameters().iterator().next();
assertEquals("The patient's identifier (MRN or other card number)", param.getDescription()); assertEquals("The patient's identifier (MRN or other card number)", param.getDescription());

View File

@ -0,0 +1,206 @@
package ca.uhn.fhir.rest.server.interceptor;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import ca.uhn.fhir.rest.server.provider.HashMapResourceProvider;
import ca.uhn.fhir.test.utilities.ITestDataBuilder;
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.CapabilityStatement;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Patient;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.util.Set;
import java.util.TreeSet;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.junit.jupiter.api.Assertions.fail;
public class InteractionBlockingInterceptorTest implements ITestDataBuilder {
public static final String SERVER_OP = "$server-op";
public static final String TYPE_OP = "$type-op";
public static final String INSTANCE_OP = "$instance-op";
private static final FhirContext ourCtx = FhirContext.forR4Cached();
private static final Logger ourLog = LoggerFactory.getLogger(InteractionBlockingInterceptorTest.class);
@RegisterExtension
private final RestfulServerExtension myServer = new RestfulServerExtension(ourCtx);
private final HashMapResourceProvider<Patient> myPatientProvider = new HashMapResourceProvider<>(ourCtx, Patient.class);
private final HashMapResourceProvider<Observation> myObservationProvider = new HashMapResourceProvider<>(ourCtx, Observation.class);
private final HashMapResourceProvider<Organization> myOrganizationProvider = new HashMapResourceProvider<>(ourCtx, Organization.class);
private InteractionBlockingInterceptor mySvc;
@Test
public void testAllowInteractions() {
// Setup
mySvc = new InteractionBlockingInterceptor.Builder(ourCtx)
.addAllowedSpec("Patient:read")
.addAllowedSpec("Observation:read")
.addAllowedSpec("Observation:create")
.build();
// Test
registerProviders();
// Verify CapabilityStatement
Set<String> supportedOps = fetchCapabilityInteractions();
assertThat(supportedOps.toString(), supportedOps, containsInAnyOrder(
"Observation:create",
"Observation:read",
"Observation:vread",
"OperationDefinition:read",
"Patient:read",
"Patient:vread"
));
// Verify Server
verifyCreateObservationOk();
verifyReadObservationOk();
verifyReadEncounterFails();
}
@Test
public void testAllowOperations() {
// Setup
mySvc = new InteractionBlockingInterceptor.Builder(ourCtx)
.addAllowedSpec(SERVER_OP)
.addAllowedSpec(TYPE_OP)
.addAllowedSpec(INSTANCE_OP)
.build();
// Test
registerProviders();
// Verify CapabilityStatement
Set<String> supportedOps = fetchCapabilityInteractions();
assertThat(supportedOps.toString(), supportedOps, containsInAnyOrder(
"OperationDefinition:read",
"Patient:$instance-op",
"Patient:$type-op",
"server:$server-op"
));
// Verify Server
verifyCreateObservationFails();
}
private void verifyReadEncounterFails() {
try {
myServer.getFhirClient().read().resource("Encounter").withId("E0").execute();
fail();
} catch (ResourceNotFoundException e) {
assertThat(e.getMessage(), containsString("Unknown resource type"));
}
}
private void verifyReadObservationOk() {
myServer.getFhirClient().read().resource("Observation").withId("O0").execute();
}
private void verifyCreateObservationOk() {
myServer.getFhirClient().create().resource(new Observation()).execute();
}
private void verifyCreateObservationFails() {
try {
myServer.getFhirClient().create().resource(new Observation()).execute();
fail();
} catch (ResourceNotFoundException e) {
assertThat(e.getMessage(), containsString("Unknown resource type"));
}
}
private void registerProviders() {
createPatient(withId("P0"), withActiveTrue());
createObservation(withId("O0"), withStatus("final"));
createOrganization(withId("O0"), withName("An Organization"));
myServer.registerInterceptor(mySvc);
myServer.registerProvider(myPatientProvider);
myServer.registerProvider(myObservationProvider);
myServer.registerProvider(myOrganizationProvider);
myServer.registerProvider(new DummyOperationProvider());
}
@Nonnull
private Set<String> fetchCapabilityInteractions() {
CapabilityStatement cs = myServer.getFhirClient().capabilities().ofType(CapabilityStatement.class).execute();
TreeSet<String> supportedOps = new TreeSet<>();
// Type level
for (var nextResource : cs.getRestFirstRep().getResource()) {
for (var nextOp : nextResource.getInteraction()) {
supportedOps.add(nextResource.getType() + ":" + nextOp.getCode().toCode());
}
for (var nextOp : nextResource.getOperation()) {
supportedOps.add(nextResource.getType() + ":$" + nextOp.getName());
}
}
// Server level
for (var nextOp : cs.getRestFirstRep().getInteraction()) {
supportedOps.add("server:" + nextOp.getCode().toCode());
}
for (var nextOp : cs.getRestFirstRep().getOperation()) {
supportedOps.add("server:$" + nextOp.getName());
}
ourLog.info(ourCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(cs));
return supportedOps;
}
@Override
public IIdType doCreateResource(IBaseResource theResource) {
if (theResource instanceof Patient) {
return myPatientProvider.store((Patient) theResource);
} else if (theResource instanceof Observation) {
return myObservationProvider.store((Observation) theResource);
} else if (theResource instanceof Organization) {
return myOrganizationProvider.store((Organization) theResource);
}
throw new UnsupportedOperationException();
}
@Override
public IIdType doUpdateResource(IBaseResource theResource) {
return doCreateResource(theResource);
}
@Override
public FhirContext getFhirContext() {
return ourCtx;
}
@SuppressWarnings("unused")
private static class DummyOperationProvider {
@Operation(name = SERVER_OP)
public Parameters serverOp(@ResourceParam Parameters theParameters) {
return null;
}
@Operation(name = TYPE_OP, typeName = "Patient")
public Parameters typeOp(@ResourceParam Parameters theParameters) {
return null;
}
@Operation(name = INSTANCE_OP, typeName = "Patient")
public Parameters serverOp(@IdParam IdType theId, @ResourceParam Parameters theParameters) {
return null;
}
}
}

View File

@ -259,7 +259,7 @@ public class ServerCapabilityStatementProviderR5Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().iterator().next(); SearchParameter param = (SearchParameter) binding.getParameters().iterator().next();
assertEquals("The patient's identifier", param.getDescription()); assertEquals("The patient's identifier", param.getDescription());
@ -459,7 +459,7 @@ public class ServerCapabilityStatementProviderR5Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
for (IParameter next : binding.getParameters()) { for (IParameter next : binding.getParameters()) {
SearchParameter param = (SearchParameter) next; SearchParameter param = (SearchParameter) next;
@ -499,7 +499,7 @@ public class ServerCapabilityStatementProviderR5Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(25); SearchParameter param = (SearchParameter) binding.getParameters().get(25);
assertEquals("careprovider", param.getName()); assertEquals("careprovider", param.getName());
@ -533,7 +533,7 @@ public class ServerCapabilityStatementProviderR5Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(0); SearchParameter param = (SearchParameter) binding.getParameters().get(0);
assertEquals("The organization at which this person is a patient", param.getDescription()); assertEquals("The organization at which this person is a patient", param.getDescription());

View File

@ -303,7 +303,7 @@ public class ServerCapabilityStatementProviderR4Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().iterator().next(); SearchParameter param = (SearchParameter) binding.getParameters().iterator().next();
assertEquals("The patient's identifier", param.getDescription()); assertEquals("The patient's identifier", param.getDescription());
@ -537,7 +537,7 @@ public class ServerCapabilityStatementProviderR4Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
for (IParameter next : binding.getParameters()) { for (IParameter next : binding.getParameters()) {
SearchParameter param = (SearchParameter) next; SearchParameter param = (SearchParameter) next;
@ -595,7 +595,7 @@ public class ServerCapabilityStatementProviderR4Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(25); SearchParameter param = (SearchParameter) binding.getParameters().get(25);
assertEquals("The organization at which this person is a patient", param.getDescription()); assertEquals("The organization at which this person is a patient", param.getDescription());
@ -627,7 +627,7 @@ public class ServerCapabilityStatementProviderR4Test {
Collection<ResourceBinding> resourceBindings = rs.getResourceBindings(); Collection<ResourceBinding> resourceBindings = rs.getResourceBindings();
for (ResourceBinding resourceBinding : resourceBindings) { for (ResourceBinding resourceBinding : resourceBindings) {
if (resourceBinding.getResourceName().equals("Patient")) { if (resourceBinding.getResourceName().equals("Patient")) {
List<BaseMethodBinding<?>> methodBindings = resourceBinding.getMethodBindings(); List<BaseMethodBinding> methodBindings = resourceBinding.getMethodBindings();
SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0); SearchMethodBinding binding = (SearchMethodBinding) methodBindings.get(0);
SearchParameter param = (SearchParameter) binding.getParameters().get(0); SearchParameter param = (SearchParameter) binding.getParameters().get(0);
assertEquals("The organization at which this person is a patient", param.getDescription()); assertEquals("The organization at which this person is a patient", param.getDescription());

View File

@ -12,11 +12,11 @@ import org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationS
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain; import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Patient;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
@ -71,7 +71,7 @@ public class NpmPackageValidationSupportTest {
} }
@NotNull @Nonnull
private NpmPackageValidationSupport getNpmPackageValidationSupport(String theClasspath) throws IOException { private NpmPackageValidationSupport getNpmPackageValidationSupport(String theClasspath) throws IOException {
NpmPackageValidationSupport npmPackageSupport = new NpmPackageValidationSupport(myFhirContext); NpmPackageValidationSupport npmPackageSupport = new NpmPackageValidationSupport(myFhirContext);
npmPackageSupport.loadPackageFromClasspath(theClasspath); npmPackageSupport.loadPackageFromClasspath(theClasspath);

View File

@ -13,9 +13,9 @@ import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator; import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
import org.hl7.fhir.r4.model.IntegerType; import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.MedicationRequest; import org.hl7.fhir.r4.model.MedicationRequest;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import javax.annotation.Nonnull;
import java.io.IOException; import java.io.IOException;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -63,12 +63,12 @@ public class XverExtensionsValidationTest {
assertEquals("Extension_EXT_Type", errorMessage.getMessageId()); assertEquals("Extension_EXT_Type", errorMessage.getMessageId());
} }
@NotNull @Nonnull
private static Predicate<SingleValidationMessage> errorMessagePredicate() { private static Predicate<SingleValidationMessage> errorMessagePredicate() {
return message -> message.getSeverity() == ResultSeverityEnum.ERROR; return message -> message.getSeverity() == ResultSeverityEnum.ERROR;
} }
@NotNull @Nonnull
private static FhirValidator getFhirValidator() { private static FhirValidator getFhirValidator() {
FhirValidator validator; FhirValidator validator;
final FhirInstanceValidator instanceValidator = new FhirInstanceValidator(ourCtx); final FhirInstanceValidator instanceValidator = new FhirInstanceValidator(ourCtx);
@ -79,14 +79,14 @@ public class XverExtensionsValidationTest {
return validator; return validator;
} }
@NotNull @Nonnull
private static MedicationRequest getMedicationRequest() { private static MedicationRequest getMedicationRequest() {
MedicationRequest med_req; MedicationRequest med_req;
med_req = ourCtx.newJsonParser().parseResource(MedicationRequest.class, loadResource("/r4/amz/medication-request-amz.json")); med_req = ourCtx.newJsonParser().parseResource(MedicationRequest.class, loadResource("/r4/amz/medication-request-amz.json"));
return med_req; return med_req;
} }
@NotNull @Nonnull
private IValidationSupport getValidationSupport() throws IOException { private IValidationSupport getValidationSupport() throws IOException {
NpmPackageValidationSupport npmPackageSupport = new NpmPackageValidationSupport(ourCtx); NpmPackageValidationSupport npmPackageSupport = new NpmPackageValidationSupport(ourCtx);
npmPackageSupport.loadPackageFromClasspath("classpath:package/hl7.fhir.xver-extensions-0.0.11.tgz"); npmPackageSupport.loadPackageFromClasspath("classpath:package/hl7.fhir.xver-extensions-0.0.11.tgz");

View File

@ -1436,7 +1436,7 @@
<dependency> <dependency>
<groupId>com.fasterxml.woodstox</groupId> <groupId>com.fasterxml.woodstox</groupId>
<artifactId>woodstox-core</artifactId> <artifactId>woodstox-core</artifactId>
<version>6.2.5</version> <version>6.3.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.ebaysf.web</groupId> <groupId>org.ebaysf.web</groupId>