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"
),
/**
* <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>
* 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
*/
public RestOperationTypeEnum forCode(@Nonnull String theCode) {
public static RestOperationTypeEnum forCode(@Nonnull String theCode) {
Validate.notNull(theCode, "theCode must not be null");
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.
# 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
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

View File

@ -124,7 +124,7 @@ public abstract class AbstractJaxRsBundleProvider extends AbstractJaxRsProvider
private Response execute(final Builder theRequestBuilder, final String methodKey)
throws IOException {
final JaxRsRequest theRequest = theRequestBuilder.build();
final BaseMethodBinding<?> method = getBinding(theRequest.getRestOperationType(), methodKey);
final BaseMethodBinding method = getBinding(theRequest.getRestOperationType(), methodKey);
try {
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)
* @return
*/
protected BaseMethodBinding<?> getBinding(final RestOperationTypeEnum restOperation, final String theBindingKey) {
protected BaseMethodBinding getBinding(final RestOperationTypeEnum restOperation, final String theBindingKey) {
return getBindings().getBinding(restOperation, theBindingKey);
}

View File

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

View File

@ -322,7 +322,7 @@ implements IRestfulServer<JaxRsRequest>, IResourceProvider {
private Response execute(final Builder theRequestBuilder, final String methodKey)
throws IOException {
final JaxRsRequest theRequest = theRequestBuilder.build();
final BaseMethodBinding<?> method = getBinding(theRequest.getRestOperationType(), methodKey);
final BaseMethodBinding method = getBinding(theRequest.getRestOperationType(), methodKey);
try {
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)
* @return
*/
protected BaseMethodBinding<?> getBinding(final RestOperationTypeEnum restOperation, final String theBindingKey) {
protected BaseMethodBinding getBinding(final RestOperationTypeEnum restOperation, final String 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 java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@ -48,7 +47,7 @@ public class JaxRsMethodBindings {
/** Static collection of bindings mapped to a class*/
private static final ConcurrentHashMap<Class<?>, JaxRsMethodBindings> classBindings = new ConcurrentHashMap<Class<?>, JaxRsMethodBindings>();
/** 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
@ -59,7 +58,7 @@ public class JaxRsMethodBindings {
List<Method> declaredMethodsForCurrentProvider = ReflectionUtil.getDeclaredMethods(theProviderClass);
declaredMethodsForCurrentProvider.addAll(ReflectionUtil.getDeclaredMethods(theProviderClass.getSuperclass()));
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) {
continue;
}
@ -78,7 +77,7 @@ public class JaxRsMethodBindings {
* @param theBinding the methodbinding
* @return the key for the methodbinding.
*/
private String getBindingKey(final BaseMethodBinding<?> theBinding) {
private String getBindingKey(final BaseMethodBinding theBinding) {
if (theBinding instanceof OperationMethodBinding) {
return ((OperationMethodBinding) theBinding).getName();
} else if (theBinding instanceof SearchMethodBinding) {
@ -89,8 +88,8 @@ public class JaxRsMethodBindings {
}
}
private void addMethodBinding(String key, BaseMethodBinding<?> binding) {
ConcurrentHashMap<String, BaseMethodBinding<?>> mapByOperation = getMapForOperation(binding.getRestOperationType());
private void addMethodBinding(String key, BaseMethodBinding binding) {
ConcurrentHashMap<String, BaseMethodBinding> mapByOperation = getMapForOperation(binding.getRestOperationType());
if (mapByOperation.containsKey(key)) {
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.
* @return the map defined in the operation bindings
*/
private ConcurrentHashMap<String, BaseMethodBinding<?>> getMapForOperation(RestOperationTypeEnum operationType) {
ConcurrentHashMap<String, BaseMethodBinding<?>> result = operationBindings.get(operationType);
private ConcurrentHashMap<String, BaseMethodBinding> getMapForOperation(RestOperationTypeEnum operationType) {
ConcurrentHashMap<String, BaseMethodBinding> result = operationBindings.get(operationType);
if(result == null) {
operationBindings.putIfAbsent(operationType, new ConcurrentHashMap<String, BaseMethodBinding<?>>());
operationBindings.putIfAbsent(operationType, new ConcurrentHashMap<String, BaseMethodBinding>());
return getMapForOperation(operationType);
} else {
return result;
@ -122,9 +121,9 @@ public class JaxRsMethodBindings {
* @return the binding defined
* @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);
ConcurrentHashMap<String, BaseMethodBinding<?>> map = getMapForOperation(operationType);
ConcurrentHashMap<String, BaseMethodBinding> map = getMapForOperation(operationType);
if(map == null || !map.containsKey(bindingKey)) {
throw new NotImplementedOperationException(Msg.code(598) + "Operation not implemented");
} else {

View File

@ -20,10 +20,10 @@ package ca.uhn.fhir.rest.openapi;
* #L%
*/
import ca.uhn.fhir.i18n.Msg;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
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.Pointcut;
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.tags.Tag;
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_40_50;
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> myExtensionToContentType = new HashMap<>();
private String myBannerImage;
private String myCssText;
private boolean myUseResourcePages;
/**
* Constructor
@ -150,6 +153,7 @@ public class OpenApiInterceptor {
private void initResources() {
setBannerImage(RACCOON_PNG);
setUseResourcePages(true);
addResourcePathToClasspath("/swagger-ui/index.html", "/ca/uhn/fhir/rest/openapi/index.html");
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());
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)) {
if (resourcePath.endsWith(".js") || resourcePath.endsWith(".map")) {
@ -275,12 +288,34 @@ public class OpenApiInterceptor {
}
public String removeTrailingSlash(String theUrl) {
while(theUrl != null && theUrl.endsWith("/")) {
while (theUrl != null && theUrl.endsWith("/")) {
theUrl = theUrl.substring(0, theUrl.length() - 1);
}
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")
private void serveSwaggerUiHtml(ServletRequestDetails theRequestDetails, HttpServletResponse theResponse) throws IOException {
CapabilityStatement cs = getCapabilityStatement(theRequestDetails);
@ -299,6 +334,8 @@ public class OpenApiInterceptor {
context.setVariable("BANNER_IMAGE_URL", getBannerImage());
context.setVariable("OPENAPI_DOCS", baseUrl + "/api-docs");
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());
String copyright = cs.getCopyright();
@ -341,7 +378,12 @@ public class OpenApiInterceptor {
context.setVariable("PAGE_NAMES", pageNames);
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);
populateOIDCVariables(theRequestDetails, context);
@ -828,7 +870,6 @@ public class OpenApiInterceptor {
};
}
private Content provideContentFhirResource(OpenAPI theOpenApi, FhirContext theExampleFhirContext, Supplier<IBaseResource> theExampleSupplier) {
addSchemaFhirResource(theOpenApi);
Content retVal = new Content();
@ -862,14 +903,36 @@ public class OpenApiInterceptor {
return new ClassLoaderTemplateResource(myResourcePathToClasspath.get("/swagger-ui/index.html"), StandardCharsets.UTF_8.name());
}
public void setBannerImage(String theBannerImage) {
myBannerImage = theBannerImage;
}
public String getBannerImage() {
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 {
@Override
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;
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">
<title>Swagger UI</title>
<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="./swagger-ui-custom.css" th:if="${ADDITIONAL_CSS_TEXT} != null"/>
</head>
<body>
<div class="container">
</div>
<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>
</div>
<div class="banner2">
@ -29,7 +28,7 @@
</div>
<div class="bannerCopyright" th:if="${COPYRIGHT_HTML} != null" th:utext="${COPYRIGHT_HTML}">
</div>
<div class="banner3">
<div class="banner3" th:if="${USE_RESOURCE_PAGES}">
<div class="pageButtons" id="pageButtons">
<a class="pageButton" th:each="pageName : ${PAGE_NAMES}" th:classappend="${pageName} == ${PAGE} ? 'pageButtonSelected' : ''" th:href="@{/swagger-ui/(page=${pageName})}">
[[${pageName}]]

View File

@ -57,11 +57,14 @@ import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
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.assertNotNull;
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 resp = fetchSwaggerUi(url);
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
@ -238,6 +291,10 @@ public class OpenApiInterceptorTest {
private List<String> parsePageButtonTexts(String resp, String url) throws IOException {
HtmlPage html = HtmlUtil.parseAsHtml(resp, new URL(url));
HtmlDivision pageButtons = (HtmlDivision) html.getElementById("pageButtons");
if (pageButtons == null) {
return Collections.emptyList();
}
List<String> buttonTexts = new ArrayList<>();
for (DomElement next : pageButtons.getChildElements()) {
buttonTexts.add(next.asNormalizedText());

View File

@ -32,13 +32,13 @@ public interface IRestfulServerUtil {
Object getResourceParameter(
RequestDetails requestDetails,
Mode myMode,
BaseMethodBinding<?> theMethodBinding,
BaseMethodBinding theMethodBinding,
Class<? extends IBaseResource> myResourceType);
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.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
import ca.uhn.fhir.rest.server.method.MethodMatchEnum;
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 String resourceName;
private LinkedList<BaseMethodBinding<?>> myMethodBindings = new LinkedList<>();
private LinkedList<BaseMethodBinding> myMethodBindings = new LinkedList<>();
/**
* Constructor
@ -44,7 +45,7 @@ public class ResourceBinding {
super();
}
public BaseMethodBinding<?> getMethod(RequestDetails theRequest) {
public BaseMethodBinding getMethod(RequestDetails theRequest) {
if (null == myMethodBindings) {
ourLog.warn("No methods exist for resource: {}", resourceName);
return null;
@ -56,10 +57,10 @@ public class ResourceBinding {
* Look for the method with the highest match strength
*/
BaseMethodBinding<?> matchedMethod = null;
BaseMethodBinding matchedMethod = null;
MethodMatchEnum matchedMethodStrength = null;
for (BaseMethodBinding<?> rm : myMethodBindings) {
for (BaseMethodBinding rm : myMethodBindings) {
MethodMatchEnum nextMethodMatch = rm.incomingServerRequestMatchesMethod(theRequest);
if (nextMethodMatch != MethodMatchEnum.NONE) {
if (matchedMethodStrength == null || matchedMethodStrength.ordinal() < nextMethodMatch.ordinal()) {
@ -83,11 +84,11 @@ public class ResourceBinding {
this.resourceName = resourceName;
}
public List<BaseMethodBinding<?>> getMethodBindings() {
public List<BaseMethodBinding> getMethodBindings() {
return myMethodBindings;
}
public void addMethod(BaseMethodBinding<?> method) {
public void addMethod(BaseMethodBinding 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.method.BaseMethodBinding;
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.servlet.ServletRequestDetails;
import ca.uhn.fhir.rest.server.tenant.ITenantIdentificationStrategy;
@ -77,6 +78,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
@ -264,7 +266,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
return result;
}
private List<BaseMethodBinding<?>> getGlobalBindings() {
private List<BaseMethodBinding> getGlobalBindings() {
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
* the given request
*/
public BaseMethodBinding<?> determineResourceMethod(RequestDetails requestDetails, String requestPath) {
public BaseMethodBinding determineResourceMethod(RequestDetails requestDetails, String requestPath) {
RequestTypeEnum requestType = requestDetails.getRequestType();
ResourceBinding resourceBinding = null;
BaseMethodBinding<?> resourceMethod = null;
BaseMethodBinding resourceMethod = null;
String resourceName = requestDetails.getResourceName();
if (myServerConformanceMethod.incomingServerRequestMatchesMethod(requestDetails) != MethodMatchEnum.NONE) {
resourceMethod = myServerConformanceMethod;
@ -466,7 +468,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
int count = 0;
for (Method m : ReflectionUtil.getDeclaredMethods(clazz)) {
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, getFhirContext(), theProvider);
BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(m, getFhirContext(), theProvider);
if (foundMethodBinding == null) {
continue;
}
@ -489,6 +491,18 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
}
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();
ResourceBinding resourceBinding;
if (resourceName == null) {
@ -515,7 +529,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
Package pack = annotation.annotationType().getPackage();
if (pack.equals(IdParam.class.getPackage())) {
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();
}
public Collection<BaseMethodBinding<?>> getProviderMethodBindings(Object theProvider) {
Set<BaseMethodBinding<?>> retVal = new HashSet<>();
public Collection<BaseMethodBinding> getProviderMethodBindings(Object theProvider) {
Set<BaseMethodBinding> retVal = new HashSet<>();
for (ResourceBinding resourceBinding : getResourceBindings()) {
for (BaseMethodBinding<?> methodBinding : resourceBinding.getMethodBindings()) {
for (BaseMethodBinding methodBinding : resourceBinding.getMethodBindings()) {
if (theProvider.equals(methodBinding.getProvider())) {
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
* with caution, as it may change.
*/
public List<BaseMethodBinding<?>> getServerBindings() {
public List<BaseMethodBinding> getServerBindings() {
return myServerBinding.getMethodBindings();
}
@ -1125,7 +1139,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
validateRequest(requestDetails);
BaseMethodBinding<?> resourceMethod = determineResourceMethod(requestDetails, requestPath);
BaseMethodBinding resourceMethod = determineResourceMethod(requestDetails, requestPath);
RestOperationTypeEnum operation = resourceMethod.getRestOperationType(requestDetails);
requestDetails.setRestOperationType(operation);
@ -1693,7 +1707,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
/*
* 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");
List<IResourceProvider> newResourceProviders = new ArrayList<>();
@ -1772,8 +1786,8 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
continue;
}
for (Iterator<BaseMethodBinding<?>> it = resourceBinding.getMethodBindings().iterator(); it.hasNext(); ) {
BaseMethodBinding<?> binding = it.next();
for (Iterator<BaseMethodBinding> it = resourceBinding.getMethodBindings().iterator(); it.hasNext(); ) {
BaseMethodBinding binding = it.next();
if (theProvider.equals(binding.getProvider())) {
it.remove();
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 {
for (Method m : ReflectionUtil.getDeclaredMethods(clazz)) {
BaseMethodBinding<?> foundMethodBinding = BaseMethodBinding.bindMethod(m, getFhirContext(), theProvider);
BaseMethodBinding foundMethodBinding = BaseMethodBinding.bindMethod(m, getFhirContext(), theProvider);
if (foundMethodBinding == null) {
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 static final String GLOBAL = "GLOBAL";
private static final Logger ourLog = LoggerFactory.getLogger(RestfulServerConfiguration.class);
private Collection<ResourceBinding> resourceBindings;
private List<BaseMethodBinding<?>> serverBindings;
private List<BaseMethodBinding<?>> myGlobalBindings;
private Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype;
private Collection<ResourceBinding> myResourceBindings;
private List<BaseMethodBinding> myServerBindings;
private List<BaseMethodBinding> myGlobalBindings;
private Map<String, Class<? extends IBaseResource>> myResourceNameToSharedSupertype;
private String myImplementationDescription;
private String myServerName = "HAPI FHIR";
private String myServerVersion = VersionUtil.getVersion();
@ -89,7 +88,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
* @return the resourceBindings
*/
public Collection<ResourceBinding> getResourceBindings() {
return resourceBindings;
return myResourceBindings;
}
/**
@ -98,7 +97,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
* @param resourceBindings the resourceBindings to set
*/
public RestfulServerConfiguration setResourceBindings(Collection<ResourceBinding> resourceBindings) {
this.resourceBindings = resourceBindings;
this.myResourceBindings = resourceBindings;
return this;
}
@ -107,24 +106,24 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
*
* @return the serverBindings
*/
public List<BaseMethodBinding<?>> getServerBindings() {
return serverBindings;
public List<BaseMethodBinding> getServerBindings() {
return myServerBindings;
}
/**
* Set the theServerBindings
*/
public RestfulServerConfiguration setServerBindings(List<BaseMethodBinding<?>> theServerBindings) {
this.serverBindings = theServerBindings;
public RestfulServerConfiguration setServerBindings(List<BaseMethodBinding> theServerBindings) {
this.myServerBindings = theServerBindings;
return this;
}
public Map<String, Class<? extends IBaseResource>> getNameToSharedSupertype() {
return resourceNameToSharedSupertype;
return myResourceNameToSharedSupertype;
}
public RestfulServerConfiguration setNameToSharedSupertype(Map<String, Class<? extends IBaseResource>> resourceNameToSharedSupertype) {
this.resourceNameToSharedSupertype = resourceNameToSharedSupertype;
this.myResourceNameToSharedSupertype = resourceNameToSharedSupertype;
return this;
}
@ -248,8 +247,8 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
IdentityHashMap<OperationMethodBinding, String> operationBindingToId = new IdentityHashMap<>();
HashMap<String, List<OperationMethodBinding>> operationIdToBindings = new HashMap<>();
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings();
List<BaseMethodBinding<?>> methodBindings = resourceToMethods
Map<String, List<BaseMethodBinding>> resourceToMethods = collectMethodBindings();
List<BaseMethodBinding> methodBindings = resourceToMethods
.values()
.stream().flatMap(t -> t.stream())
.collect(Collectors.toList());
@ -258,7 +257,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
}
ListMultimap<String, OperationMethodBinding> nameToOperationMethodBindings = ArrayListMultimap.create();
for (BaseMethodBinding<?> nextMethodBinding : methodBindings) {
for (BaseMethodBinding nextMethodBinding : methodBindings) {
if (nextMethodBinding instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
nameToOperationMethodBindings.put(methodBinding.getName(), methodBinding);
@ -332,7 +331,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
nextMethodBindings.forEach(t->operationBindingToId.put(t, operationId));
}
for (BaseMethodBinding<?> nextMethodBinding : methodBindings) {
for (BaseMethodBinding nextMethodBinding : methodBindings) {
if (nextMethodBinding instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
if (operationBindingToId.containsKey(methodBinding)) {
@ -353,18 +352,18 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
return new Bindings(namedSearchMethodBindingToName, searchNameToBindings, operationIdToBindings, operationBindingToId);
}
public Map<String, List<BaseMethodBinding<?>>> collectMethodBindings() {
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<>();
public Map<String, List<BaseMethodBinding>> collectMethodBindings() {
Map<String, List<BaseMethodBinding>> resourceToMethods = new TreeMap<>();
for (ResourceBinding next : getResourceBindings()) {
String resourceName = next.getResourceName();
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) {
for (BaseMethodBinding nextMethodBinding : next.getMethodBindings()) {
if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<>());
}
resourceToMethods.get(resourceName).add(nextMethodBinding);
}
}
for (BaseMethodBinding<?> nextMethodBinding : getServerBindings()) {
for (BaseMethodBinding nextMethodBinding : getServerBindings()) {
String resourceName = "";
if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<>());
@ -374,11 +373,11 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
return resourceToMethods;
}
public List<BaseMethodBinding<?>> getGlobalBindings() {
public List<BaseMethodBinding> getGlobalBindings() {
return myGlobalBindings;
}
public void setGlobalBindings(List<BaseMethodBinding<?>> theGlobalBindings) {
public void setGlobalBindings(List<BaseMethodBinding> theGlobalBindings) {
myGlobalBindings = theGlobalBindings;
}
@ -403,7 +402,7 @@ public class RestfulServerConfiguration implements ISearchParamRegistry {
scanner.register(resourceClass);
});
resourceNameToSharedSupertype = resourceNameToScanner.entrySet().stream()
myResourceNameToSharedSupertype = resourceNameToScanner.entrySet().stream()
.filter(entry -> entry.getValue().getLowestCommonSuperclass().isPresent())
.collect(Collectors.toMap(
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();
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();
subServletRequestDetails.setRestOperationType(restOperationType);

View File

@ -59,7 +59,7 @@ import java.util.stream.Collectors;
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 final List<BaseQueryParameter> myQueryParameters;
@ -331,7 +331,7 @@ public abstract class BaseMethodBinding<T> {
}
@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);
Search search = theMethod.getAnnotation(Search.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;
abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<MethodOutcome> {
abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding {
static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseOutcomeReturningMethodBinding.class);
private boolean myReturnVoid;

View File

@ -95,7 +95,7 @@ public abstract class BaseQueryParameter implements IParameter {
@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<>();
String name = getName();

View File

@ -72,7 +72,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
* #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 MethodReturnTypeEnum myMethodReturnType;

View File

@ -53,7 +53,7 @@ class ConditionalParamBinder implements IParameter {
}
@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());
}

View File

@ -40,7 +40,7 @@ public class CountParameter implements IParameter {
private Class<?> myType;
@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);
if (countParam != null) {
if (countParam.length > 0) {

View File

@ -46,7 +46,7 @@ public class ElementsParameter implements IParameter {
@Override
@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);
if (value == null || value.isEmpty()) {
return null;

View File

@ -47,7 +47,7 @@ public class GraphQLQueryBodyParameter implements IParameter {
private Class<?> myType;
@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));
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.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.api.Constants;
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.InvalidRequestException;
import org.apache.commons.lang3.StringUtils;
import java.lang.reflect.Method;
import java.util.Collection;
@ -40,7 +36,7 @@ public class GraphQLQueryUrlParameter implements IParameter {
private Class<?> myType;
@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 retVal = null;
if (queryParams != null) {

View File

@ -41,7 +41,7 @@ public interface IParameter {
* @param theMethodBinding TODO
* @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);

View File

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

View File

@ -31,7 +31,7 @@ class NullParameter implements IParameter {
@Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
// nothing
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.parser.DataFormatException;
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.server.RequestDetails;
import ca.uhn.fhir.rest.param.ParameterUtil;
@ -42,7 +41,7 @@ public class OffsetParameter implements IParameter {
private Class<?> myType;
@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);
if (sinceParams != null) {
if (sinceParams.length > 0) {

View File

@ -251,7 +251,7 @@ public class OperationParameter implements IParameter {
@SuppressWarnings("unchecked")
@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>();
OperationMethodBinding method = (OperationMethodBinding) theMethodBinding;

View File

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

View File

@ -45,7 +45,7 @@ public class RawParamsParameter implements IParameter {
@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;
for (String nextName : theRequest.getParameters().keySet()) {

View File

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

View File

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

View File

@ -93,7 +93,7 @@ public class ResourceParameter implements IParameter {
@Override
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding<?> theMethodBinding) throws InternalErrorException, InvalidRequestException {
public Object translateQueryParametersIntoServerArgument(RequestDetails theRequest, BaseMethodBinding theMethodBinding) throws InternalErrorException, InvalidRequestException {
switch (myMode) {
case BODY:
try {
@ -141,7 +141,7 @@ public class ResourceParameter implements IParameter {
}
@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();
final Charset charset = determineRequestCharset(theRequest);
@ -195,7 +195,7 @@ public class ResourceParameter implements IParameter {
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) {
return theRequest.getResource();
}

View File

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

View File

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

View File

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

View File

@ -36,7 +36,7 @@ class ServletRequestParameter implements IParameter {
}
@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();
}

View File

@ -32,7 +32,7 @@ class ServletResponseParameter implements IParameter {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServletResponseParameter.class);
@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();
}

View File

@ -56,7 +56,7 @@ public class SortParameter implements IParameter {
}
@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_ASC)) {
if (!theRequest.getParameters().containsKey(Constants.PARAM_SORT_DESC)) {

View File

@ -41,7 +41,7 @@ public class SummaryEnumParameter implements IParameter {
@Override
@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);
if (value == null || value.isEmpty()) {
return null;

View File

@ -88,7 +88,7 @@ public class TransactionParameter implements IParameter {
}
@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);
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.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.IParameter;
import ca.uhn.fhir.rest.server.method.OperationMethodBinding;
import ca.uhn.fhir.rest.server.method.OperationMethodBinding.ReturnType;
@ -134,7 +135,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
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();
if (restOperationType.isSystemLevel()) {
String sysOp = restOperationType.getCode();
@ -236,15 +237,15 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
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();
List<BaseMethodBinding<?>> globalMethodBindings = configuration.getGlobalBindings();
List<BaseMethodBinding> globalMethodBindings = configuration.getGlobalBindings();
TreeMultimap<String, String> resourceNameToIncludes = 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();
for (BaseMethodBinding<?> nextMethod : nextEntry.getValue()) {
for (BaseMethodBinding nextMethod : nextEntry.getValue()) {
if (nextMethod instanceof SearchMethodBinding) {
resourceNameToIncludes.putAll(resourceName, nextMethod.getIncludes());
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<>();
String resourceName = nextEntry.getKey();
@ -273,7 +274,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
terser.addElement(resource, "type", def.getName());
terser.addElement(resource, "profile", def.getResourceProfile(serverBase));
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
for (BaseMethodBinding nextMethodBinding : nextEntry.getValue()) {
RestOperationTypeEnum resOpCode = nextMethodBinding.getRestOperationType();
if (resOpCode.isTypeLevel() || resOpCode.isInstanceLevel()) {
String resOp;
@ -358,7 +359,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
// global flag set to true, meaning they apply to all resource types)
if (globalMethodBindings != null) {
Set<String> globalOperationNames = new HashSet<>();
for (BaseMethodBinding<?> next : globalMethodBindings) {
for (BaseMethodBinding next : globalMethodBindings) {
if (next instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) next;
if (methodBinding.isGlobalMethod()) {
@ -482,7 +483,7 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
}
} else {
for (BaseMethodBinding<?> nextMethodBinding : nextEntry.getValue()) {
for (BaseMethodBinding nextMethodBinding : nextEntry.getValue()) {
checkBindingForSystemOps(terser, rest, systemOps, nextMethodBinding);
if (nextMethodBinding instanceof OperationMethodBinding) {
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)
if (globalMethodBindings != null) {
Set<String> globalOperationNames = new HashSet<>();
for (BaseMethodBinding<?> next : globalMethodBindings) {
for (BaseMethodBinding next : globalMethodBindings) {
if (next instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) next;
if (methodBinding.isGlobalMethod()) {

View File

@ -25,15 +25,15 @@ class ResourceBindingTest {
public void testFILO() throws NoSuchMethodException {
// setup
Method method = ResourceBindingTest.class.getMethod("testFILO");
BaseMethodBinding<?> first = new PageMethodBinding(ourFhirContext, method);
BaseMethodBinding<?> second = new PageMethodBinding(ourFhirContext, method);;
BaseMethodBinding first = new PageMethodBinding(ourFhirContext, method);
BaseMethodBinding second = new PageMethodBinding(ourFhirContext, method);;
// execute
myResourceBinding.addMethod(first);
myResourceBinding.addMethod(second);
// verify
List<BaseMethodBinding<?>> list = myResourceBinding.getMethodBindings();
List<BaseMethodBinding> list = myResourceBinding.getMethodBindings();
assertNotEquals(first, second);
assertEquals(second, list.get(0));
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.JobInstance;
import ca.uhn.fhir.jpa.api.model.BulkExportJobResults;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@ -47,10 +47,10 @@ public class BulkExportCreateReportStep implements IReductionStepWorker<BulkExpo
private Map<String, List<String>> myResourceToBinaryIds;
@NotNull
@Nonnull
@Override
public RunOutcome run(@NotNull StepExecutionDetails<BulkExportJobParameters, BulkExportBinaryFileId> theStepExecutionDetails,
@NotNull IJobDataSink<BulkExportJobResults> theDataSink) throws JobExecutionFailedException {
public RunOutcome run(@Nonnull StepExecutionDetails<BulkExportJobParameters, BulkExportBinaryFileId> theStepExecutionDetails,
@Nonnull IJobDataSink<BulkExportJobResults> theDataSink) throws JobExecutionFailedException {
BulkExportJobResults results = new BulkExportJobResults();
String requestUrl = getOriginatingRequestUrl(theStepExecutionDetails, results);
@ -74,7 +74,7 @@ public class BulkExportCreateReportStep implements IReductionStepWorker<BulkExpo
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();
String url = "";
if (instance instanceof JobInstance) {
@ -86,7 +86,7 @@ public class BulkExportCreateReportStep implements IReductionStepWorker<BulkExpo
return url;
}
@NotNull
@Nonnull
@Override
public ChunkOutcome consume(ChunkExecutionDetails<BulkExportJobParameters,
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.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;
public class DeleteExpungeJobParametersValidator implements IJobParametersValidator<DeleteExpungeJobParameters> {
@ -36,7 +36,7 @@ public class DeleteExpungeJobParametersValidator implements IJobParametersValida
@Nullable
@Override
public List<String> validate(@NotNull DeleteExpungeJobParameters theParameters) {
public List<String> validate(@Nonnull DeleteExpungeJobParameters theParameters) {
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.jobs.parameters.PartitionedUrl;
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.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ReindexJobParametersValidator implements IJobParametersValidator<ReindexJobParameters> {
@ -42,7 +39,7 @@ public class ReindexJobParametersValidator implements IJobParametersValidator<Re
@Nullable
@Override
public List<String> validate(@NotNull ReindexJobParameters theParameters) {
public List<String> validate(@Nonnull ReindexJobParameters theParameters) {
List<String> errors = myUrlListValidator.validatePartitionedUrls(theParameters.getPartitionedUrls());
if (errors == null || errors.isEmpty()) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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.validator.FhirInstanceValidator;
import org.hl7.fhir.r4.model.Patient;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.Map;
@ -71,7 +71,7 @@ public class NpmPackageValidationSupportTest {
}
@NotNull
@Nonnull
private NpmPackageValidationSupport getNpmPackageValidationSupport(String theClasspath) throws IOException {
NpmPackageValidationSupport npmPackageSupport = new NpmPackageValidationSupport(myFhirContext);
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.r4.model.IntegerType;
import org.hl7.fhir.r4.model.MedicationRequest;
import org.jetbrains.annotations.NotNull;
import org.junit.jupiter.api.Test;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.function.Predicate;
@ -63,12 +63,12 @@ public class XverExtensionsValidationTest {
assertEquals("Extension_EXT_Type", errorMessage.getMessageId());
}
@NotNull
@Nonnull
private static Predicate<SingleValidationMessage> errorMessagePredicate() {
return message -> message.getSeverity() == ResultSeverityEnum.ERROR;
}
@NotNull
@Nonnull
private static FhirValidator getFhirValidator() {
FhirValidator validator;
final FhirInstanceValidator instanceValidator = new FhirInstanceValidator(ourCtx);
@ -79,14 +79,14 @@ public class XverExtensionsValidationTest {
return validator;
}
@NotNull
@Nonnull
private static MedicationRequest getMedicationRequest() {
MedicationRequest med_req;
med_req = ourCtx.newJsonParser().parseResource(MedicationRequest.class, loadResource("/r4/amz/medication-request-amz.json"));
return med_req;
}
@NotNull
@Nonnull
private IValidationSupport getValidationSupport() throws IOException {
NpmPackageValidationSupport npmPackageSupport = new NpmPackageValidationSupport(ourCtx);
npmPackageSupport.loadPackageFromClasspath("classpath:package/hl7.fhir.xver-extensions-0.0.11.tgz");

View File

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