Merge branch 'master' into ja_20200206_multitenancy
This commit is contained in:
commit
48057b9d8e
|
@ -118,6 +118,19 @@ public class ParameterUtil {
|
|||
return parseQueryParams(theContext, paramType, theUnqualifiedParamName, theParameters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes :modifiers and .chains from URL parameter names
|
||||
*/
|
||||
public static String stripModifierPart(String theParam) {
|
||||
for (int i = 0; i < theParam.length(); i++) {
|
||||
char nextChar = theParam.charAt(i);
|
||||
if (nextChar == ':' || nextChar == '.') {
|
||||
return theParam.substring(0, i);
|
||||
}
|
||||
}
|
||||
return theParam;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a string according to the rules for parameter escaping specified in the <a href="http://www.hl7.org/implement/standards/fhir/search.html#escaping">FHIR Specification Escaping
|
||||
* Section</a>
|
||||
|
|
|
@ -31,31 +31,63 @@ import java.lang.reflect.ParameterizedType;
|
|||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ReflectionUtil {
|
||||
|
||||
private static final ConcurrentHashMap<String, Object> ourFhirServerVersions = new ConcurrentHashMap<>();
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReflectionUtil.class);
|
||||
public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
|
||||
public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0];
|
||||
private static final ConcurrentHashMap<String, Object> ourFhirServerVersions = new ConcurrentHashMap<>();
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ReflectionUtil.class);
|
||||
|
||||
public static LinkedHashSet<Method> getDeclaredMethods(Class<?> theClazz) {
|
||||
LinkedHashSet<Method> retVal = new LinkedHashSet<>();
|
||||
for (Method next : theClazz.getDeclaredMethods()) {
|
||||
/**
|
||||
* Returns all methods declared against {@literal theClazz}. This method returns a predictable order, which is
|
||||
* sorted by method name and then by parameters.
|
||||
*/
|
||||
public static List<Method> getDeclaredMethods(Class<?> theClazz) {
|
||||
HashSet<Method> foundMethods = new LinkedHashSet<>();
|
||||
Method[] declaredMethods = theClazz.getDeclaredMethods();
|
||||
for (Method next : declaredMethods) {
|
||||
try {
|
||||
Method method = theClazz.getMethod(next.getName(), next.getParameterTypes());
|
||||
retVal.add(method);
|
||||
foundMethods.add(method);
|
||||
} catch (NoSuchMethodException | SecurityException e) {
|
||||
retVal.add(next);
|
||||
foundMethods.add(next);
|
||||
}
|
||||
}
|
||||
|
||||
List<Method> retVal = new ArrayList<>(foundMethods);
|
||||
retVal.sort((Comparator.comparing(ReflectionUtil::describeMethodInSortFriendlyWay)));
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description like <code>startsWith params(java.lang.String, int) returns(boolean)</code>.
|
||||
* The format is chosen in order to provide a predictable and useful sorting order.
|
||||
*/
|
||||
public static String describeMethodInSortFriendlyWay(Method theMethod) {
|
||||
StringBuilder b = new StringBuilder();
|
||||
b.append(theMethod.getName());
|
||||
b.append(" returns(");
|
||||
b.append(theMethod.getReturnType().getName());
|
||||
b.append(") params(");
|
||||
Class<?>[] parameterTypes = theMethod.getParameterTypes();
|
||||
for (int i = 0, parameterTypesLength = parameterTypes.length; i < parameterTypesLength; i++) {
|
||||
if (i > 0) {
|
||||
b.append(", ");
|
||||
}
|
||||
Class<?> next = parameterTypes[i];
|
||||
b.append(next.getName());
|
||||
}
|
||||
b.append(")");
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
public static Class<?> getGenericCollectionTypeOfField(Field next) {
|
||||
ParameterizedType collectionType = (ParameterizedType) next.getGenericType();
|
||||
return getGenericCollectionTypeOf(collectionType.getActualTypeArguments()[0]);
|
||||
|
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.util;
|
|||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -51,4 +52,12 @@ public class ReflectionUtilTest {
|
|||
assertFalse(ReflectionUtil.typeExists("ca.Foo"));
|
||||
assertTrue(ReflectionUtil.typeExists(String.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDescribeMethod() throws NoSuchMethodException {
|
||||
Method method = String.class.getMethod("startsWith", String.class, int.class);
|
||||
String description = ReflectionUtil.describeMethodInSortFriendlyWay(method);
|
||||
assertEquals("startsWith returns(boolean) params(java.lang.String, int)", description);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
type: add
|
||||
issue: 1802
|
||||
title: "In a plain server, if a Resource Provider class had two methods with the same parameter names
|
||||
(as specified in the @OptionalParam or @RequiredParam) but different cardinalities, the server could
|
||||
sometimes pick the incorrect method to execute. The selection algorithm has been improved to no longer
|
||||
have this issue, and to be more consistent and predictable in terms of which resource provider
|
||||
method is selected when the choice is somewhat ambiguous."
|
|
@ -228,7 +228,7 @@ public abstract class BaseStorageDao {
|
|||
|
||||
Set<String> paramNames = theSource.keySet();
|
||||
for (String nextParamName : paramNames) {
|
||||
QualifierDetails qualifiedParamName = SearchMethodBinding.extractQualifiersFromParameterName(nextParamName);
|
||||
QualifierDetails qualifiedParamName = QualifierDetails.extractQualifiersFromParameterName(nextParamName);
|
||||
RuntimeSearchParam param = searchParams.get(qualifiedParamName.getParamName());
|
||||
if (param == null) {
|
||||
String msg = getContext().getLocalizer().getMessageSanitized(BaseHapiFhirResourceDao.class, "invalidSearchParameter", qualifiedParamName.getParamName(), new TreeSet<>(searchParams.keySet()));
|
||||
|
|
|
@ -6,7 +6,9 @@ import ca.uhn.fhir.parser.LenientErrorHandler;
|
|||
import org.hl7.fhir.r4.model.Observation;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class TolerantJsonParserR4Test {
|
||||
|
||||
|
@ -42,7 +44,7 @@ public class TolerantJsonParserR4Test {
|
|||
try {
|
||||
parser.parseResource(Observation.class, input);
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("[element=\"value\"] Invalid attribute value \".\": No digits found.", e.getMessage());
|
||||
assertThat(e.getMessage(), containsString("[element=\"value\"] Invalid attribute value \".\""));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,9 +25,10 @@ import java.util.List;
|
|||
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.method.BaseMethodBinding;
|
||||
import ca.uhn.fhir.rest.server.method.MethodMatchEnum;
|
||||
|
||||
/**
|
||||
* Created by dsotnikov on 2/25/2014.
|
||||
* Holds all method bindings for an individual resource type
|
||||
*/
|
||||
public class ResourceBinding {
|
||||
|
||||
|
@ -36,9 +37,16 @@ public class ResourceBinding {
|
|||
private String resourceName;
|
||||
private List<BaseMethodBinding<?>> myMethodBindings = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceBinding() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public ResourceBinding(String resourceName, List<BaseMethodBinding<?>> methods) {
|
||||
this.resourceName = resourceName;
|
||||
this.myMethodBindings = methods;
|
||||
|
@ -51,14 +59,28 @@ public class ResourceBinding {
|
|||
}
|
||||
|
||||
ourLog.debug("Looking for a handler for {}", theRequest);
|
||||
|
||||
/*
|
||||
* Look for the method with the highest match strength
|
||||
*/
|
||||
|
||||
BaseMethodBinding<?> matchedMethod = null;
|
||||
MethodMatchEnum matchedMethodStrength = null;
|
||||
|
||||
for (BaseMethodBinding<?> rm : myMethodBindings) {
|
||||
if (rm.incomingServerRequestMatchesMethod(theRequest)) {
|
||||
ourLog.debug("Handler {} matches", rm);
|
||||
return rm;
|
||||
MethodMatchEnum nextMethodMatch = rm.incomingServerRequestMatchesMethod(theRequest);
|
||||
if (nextMethodMatch != MethodMatchEnum.NONE) {
|
||||
if (matchedMethodStrength == null || matchedMethodStrength.ordinal() < nextMethodMatch.ordinal()) {
|
||||
matchedMethod = rm;
|
||||
matchedMethodStrength = nextMethodMatch;
|
||||
}
|
||||
if (matchedMethodStrength == MethodMatchEnum.EXACT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ourLog.trace("Handler {} does not match", rm);
|
||||
}
|
||||
return null;
|
||||
|
||||
return matchedMethod;
|
||||
}
|
||||
|
||||
public String getResourceName() {
|
||||
|
|
|
@ -45,6 +45,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.MethodMatchEnum;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.tenant.ITenantIdentificationStrategy;
|
||||
import ca.uhn.fhir.util.*;
|
||||
|
@ -298,7 +299,7 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
ResourceBinding resourceBinding = null;
|
||||
BaseMethodBinding<?> resourceMethod = null;
|
||||
String resourceName = requestDetails.getResourceName();
|
||||
if (myServerConformanceMethod.incomingServerRequestMatchesMethod(requestDetails)) {
|
||||
if (myServerConformanceMethod.incomingServerRequestMatchesMethod(requestDetails) != MethodMatchEnum.NONE) {
|
||||
resourceMethod = myServerConformanceMethod;
|
||||
} else if (resourceName == null) {
|
||||
resourceBinding = myServerBinding;
|
||||
|
@ -1785,6 +1786,21 @@ public class RestfulServer extends HttpServlet implements IRestfulServer<Servlet
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters all plain and resource providers (but not the conformance provider).
|
||||
*/
|
||||
public void unregisterAllProviders() {
|
||||
unregisterAllProviders(myPlainProviders);
|
||||
unregisterAllProviders(myResourceProviders);
|
||||
}
|
||||
|
||||
private void unregisterAllProviders(List<?> theProviders) {
|
||||
while (theProviders.size() > 0) {
|
||||
unregisterProvider(theProviders.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void writeExceptionToResponse(HttpServletResponse theResponse, BaseServerResponseException theException) throws IOException {
|
||||
theResponse.setStatus(theException.getStatusCode());
|
||||
addHeadersToResponse(theResponse);
|
||||
|
|
|
@ -26,47 +26,44 @@ import ca.uhn.fhir.interceptor.api.HookParams;
|
|||
import ca.uhn.fhir.interceptor.api.Pointcut;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.Include;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.annotation.*;
|
||||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.MethodOutcome;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.api.server.IBundleProvider;
|
||||
import ca.uhn.fhir.rest.api.server.IRestfulServer;
|
||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.client.exceptions.NonFhirResponseException;
|
||||
import ca.uhn.fhir.rest.server.BundleProviders;
|
||||
import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class BaseMethodBinding<T> {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseMethodBinding.class);
|
||||
private final List<BaseQueryParameter> myQueryParameters;
|
||||
private FhirContext myContext;
|
||||
private Method myMethod;
|
||||
private List<IParameter> myParameters;
|
||||
private Object myProvider;
|
||||
private boolean mySupportsConditional;
|
||||
private boolean mySupportsConditionalMultiple;
|
||||
|
||||
public BaseMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
assert theMethod != null;
|
||||
assert theContext != null;
|
||||
|
@ -75,6 +72,11 @@ public abstract class BaseMethodBinding<T> {
|
|||
myContext = theContext;
|
||||
myProvider = theProvider;
|
||||
myParameters = MethodUtil.getResourceParameters(theContext, theMethod, theProvider, getRestOperationType());
|
||||
myQueryParameters = myParameters
|
||||
.stream()
|
||||
.filter(t -> t instanceof BaseQueryParameter)
|
||||
.map(t -> (BaseQueryParameter) t)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (IParameter next : myParameters) {
|
||||
if (next instanceof ConditionalParamBinder) {
|
||||
|
@ -90,6 +92,10 @@ public abstract class BaseMethodBinding<T> {
|
|||
myMethod.setAccessible(true);
|
||||
}
|
||||
|
||||
protected List<BaseQueryParameter> getQueryParameters() {
|
||||
return myQueryParameters;
|
||||
}
|
||||
|
||||
protected Object[] createMethodParams(RequestDetails theRequest) {
|
||||
Object[] params = new Object[getParameters().size()];
|
||||
for (int i = 0; i < getParameters().size(); i++) {
|
||||
|
@ -211,7 +217,7 @@ public abstract class BaseMethodBinding<T> {
|
|||
return getRestOperationType();
|
||||
}
|
||||
|
||||
public abstract boolean incomingServerRequestMatchesMethod(RequestDetails theRequest);
|
||||
public abstract MethodMatchEnum incomingServerRequestMatchesMethod(RequestDetails theRequest);
|
||||
|
||||
public abstract Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException;
|
||||
|
||||
|
|
|
@ -111,20 +111,20 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
public MethodMatchEnum incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
Set<RequestTypeEnum> allowableRequestTypes = provideAllowableRequestTypes();
|
||||
RequestTypeEnum requestType = theRequest.getRequestType();
|
||||
if (!allowableRequestTypes.contains(requestType)) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (!getResourceName().equals(theRequest.getResourceName())) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (getMatchingOperation() == null && StringUtils.isNotBlank(theRequest.getOperation())) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (getMatchingOperation() != null && !getMatchingOperation().equals(theRequest.getOperation())) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -137,7 +137,7 @@ abstract class BaseOutcomeReturningMethodBinding extends BaseMethodBinding<Metho
|
|||
* It's also needed for conditional update..
|
||||
*/
|
||||
|
||||
return true;
|
||||
return MethodMatchEnum.EXACT;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -58,6 +58,8 @@ public abstract class BaseQueryParameter implements IParameter {
|
|||
return null;
|
||||
}
|
||||
|
||||
protected abstract boolean supportsRepetition();
|
||||
|
||||
/**
|
||||
* Parameter should return true if {@link #parse(FhirContext, List)} should be called even if the query string
|
||||
* contained no values for the given parameter
|
||||
|
|
|
@ -152,25 +152,25 @@ public class ConformanceMethodBinding extends BaseResourceReturningMethodBinding
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
public MethodMatchEnum incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.OPTIONS) {
|
||||
if (theRequest.getOperation() == null && theRequest.getResourceName() == null) {
|
||||
return true;
|
||||
return MethodMatchEnum.EXACT;
|
||||
}
|
||||
}
|
||||
|
||||
if (theRequest.getResourceName() != null) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
if ("metadata".equals(theRequest.getOperation())) {
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.GET) {
|
||||
return true;
|
||||
return MethodMatchEnum.EXACT;
|
||||
}
|
||||
throw new MethodNotAllowedException("/metadata request must use HTTP GET", RequestTypeEnum.GET);
|
||||
}
|
||||
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
|
|
@ -29,10 +29,8 @@ import ca.uhn.fhir.rest.api.server.IRestfulServer;
|
|||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||
import ca.uhn.fhir.rest.api.server.ResponseDetails;
|
||||
import ca.uhn.fhir.rest.param.ParameterUtil;
|
||||
import ca.uhn.fhir.rest.param.TokenParam;
|
||||
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
|
||||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
@ -69,12 +67,12 @@ public class GraphQLMethodBinding extends BaseMethodBinding<String> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
public MethodMatchEnum incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
if (Constants.OPERATION_NAME_GRAPHQL.equals(theRequest.getOperation())) {
|
||||
return true;
|
||||
return MethodMatchEnum.EXACT;
|
||||
}
|
||||
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -51,7 +51,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
|
||||
private final Integer myIdParamIndex;
|
||||
private final RestOperationTypeEnum myResourceOperationType;
|
||||
private String myResourceName;
|
||||
private final String myResourceName;
|
||||
|
||||
public HistoryMethodBinding(Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super(toReturnType(theMethod, theProvider), theMethod, theContext, theProvider);
|
||||
|
@ -105,30 +105,36 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
|
||||
// ObjectUtils.equals is replaced by a JDK7 method..
|
||||
@Override
|
||||
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
public MethodMatchEnum incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
if (!Constants.PARAM_HISTORY.equals(theRequest.getOperation())) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (theRequest.getResourceName() == null) {
|
||||
return myResourceOperationType == RestOperationTypeEnum.HISTORY_SYSTEM;
|
||||
if (myResourceOperationType == RestOperationTypeEnum.HISTORY_SYSTEM) {
|
||||
return MethodMatchEnum.EXACT;
|
||||
} else {
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
}
|
||||
if (!StringUtils.equals(theRequest.getResourceName(), myResourceName)) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
boolean haveIdParam = theRequest.getId() != null && !theRequest.getId().isEmpty();
|
||||
boolean wantIdParam = myIdParamIndex != null;
|
||||
if (haveIdParam != wantIdParam) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
if (theRequest.getId() == null) {
|
||||
return myResourceOperationType == RestOperationTypeEnum.HISTORY_TYPE;
|
||||
if (myResourceOperationType != RestOperationTypeEnum.HISTORY_TYPE) {
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
} else if (theRequest.getId().hasVersionIdPart()) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
return true;
|
||||
return MethodMatchEnum.EXACT;
|
||||
}
|
||||
|
||||
|
||||
|
@ -179,7 +185,7 @@ public class HistoryMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
if (isBlank(nextResource.getIdElement().getVersionIdPart()) && nextResource instanceof IResource) {
|
||||
//TODO: Use of a deprecated method should be resolved.
|
||||
IdDt versionId = (IdDt) ResourceMetadataKeyEnum.VERSION_ID.get((IResource) nextResource);
|
||||
IdDt versionId = ResourceMetadataKeyEnum.VERSION_ID.get((IResource) nextResource);
|
||||
if (versionId == null || versionId.isEmpty()) {
|
||||
throw new InternalErrorException("Server provided resource at index " + index + " with no Version ID set (using IResource#setId(IdDt))");
|
||||
}
|
||||
|
|
|
@ -106,6 +106,11 @@ class IncludeParameter extends BaseQueryParameter {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsRepetition() {
|
||||
return myInstantiableCollectionType != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handlesMissing() {
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
package ca.uhn.fhir.rest.server.method;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Server Framework
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2020 University Health Network
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
public enum MethodMatchEnum {
|
||||
|
||||
// Order these from worst to best!
|
||||
|
||||
NONE,
|
||||
APPROXIMATE,
|
||||
EXACT;
|
||||
|
||||
public MethodMatchEnum weakerOf(MethodMatchEnum theOther) {
|
||||
if (this.ordinal() < theOther.ordinal()) {
|
||||
return this;
|
||||
} else {
|
||||
return theOther;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -36,7 +36,6 @@ import ca.uhn.fhir.rest.server.method.ResourceParameter.Mode;
|
|||
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
|
||||
import ca.uhn.fhir.util.ParametersUtil;
|
||||
import ca.uhn.fhir.util.ReflectionUtil;
|
||||
import ca.uhn.fhir.util.ValidateUtil;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||
|
||||
|
@ -144,7 +143,7 @@ public class MethodUtil {
|
|||
parameter.setRequired(true);
|
||||
parameter.setDeclaredTypes(((RequiredParam) nextAnnotation).targetTypes());
|
||||
parameter.setCompositeTypes(((RequiredParam) nextAnnotation).compositeTypes());
|
||||
parameter.setChainlists(((RequiredParam) nextAnnotation).chainWhitelist(), ((RequiredParam) nextAnnotation).chainBlacklist());
|
||||
parameter.setChainLists(((RequiredParam) nextAnnotation).chainWhitelist(), ((RequiredParam) nextAnnotation).chainBlacklist());
|
||||
parameter.setType(theContext, parameterType, innerCollectionType, outerCollectionType);
|
||||
MethodUtil.extractDescription(parameter, annotations);
|
||||
param = parameter;
|
||||
|
@ -154,12 +153,12 @@ public class MethodUtil {
|
|||
parameter.setRequired(false);
|
||||
parameter.setDeclaredTypes(((OptionalParam) nextAnnotation).targetTypes());
|
||||
parameter.setCompositeTypes(((OptionalParam) nextAnnotation).compositeTypes());
|
||||
parameter.setChainlists(((OptionalParam) nextAnnotation).chainWhitelist(), ((OptionalParam) nextAnnotation).chainBlacklist());
|
||||
parameter.setChainLists(((OptionalParam) nextAnnotation).chainWhitelist(), ((OptionalParam) nextAnnotation).chainBlacklist());
|
||||
parameter.setType(theContext, parameterType, innerCollectionType, outerCollectionType);
|
||||
MethodUtil.extractDescription(parameter, annotations);
|
||||
param = parameter;
|
||||
} else if (nextAnnotation instanceof RawParam) {
|
||||
param = new RawParamsParmeter(parameters);
|
||||
param = new RawParamsParameter(parameters);
|
||||
} else if (nextAnnotation instanceof IncludeParam) {
|
||||
Class<? extends Collection<Include>> instantiableCollectionType;
|
||||
Class<?> specType;
|
||||
|
|
|
@ -226,43 +226,43 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
public MethodMatchEnum incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
if (isBlank(theRequest.getOperation())) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
if (!myName.equals(theRequest.getOperation())) {
|
||||
if (!myName.equals(WILDCARD_NAME)) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (getResourceName() == null) {
|
||||
if (isNotBlank(theRequest.getResourceName())) {
|
||||
if (!isGlobalMethod()) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getResourceName() != null && !getResourceName().equals(theRequest.getResourceName())) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
RequestTypeEnum requestType = theRequest.getRequestType();
|
||||
if (requestType != RequestTypeEnum.GET && requestType != RequestTypeEnum.POST) {
|
||||
// Operations can only be invoked with GET and POST
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
boolean requestHasId = theRequest.getId() != null;
|
||||
if (requestHasId) {
|
||||
return myCanOperateAtInstanceLevel;
|
||||
return myCanOperateAtInstanceLevel ? MethodMatchEnum.EXACT : MethodMatchEnum.NONE;
|
||||
}
|
||||
if (isNotBlank(theRequest.getResourceName())) {
|
||||
return myCanOperateAtTypeLevel;
|
||||
return myCanOperateAtTypeLevel ? MethodMatchEnum.EXACT : MethodMatchEnum.NONE;
|
||||
}
|
||||
return myCanOperateAtServerLevel;
|
||||
return myCanOperateAtServerLevel ? MethodMatchEnum.EXACT : MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -174,12 +174,16 @@ public class PageMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
public MethodMatchEnum incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
String[] pageId = theRequest.getParameters().get(Constants.PARAM_PAGINGACTION);
|
||||
if (pageId == null || pageId.length == 0 || isBlank(pageId[0])) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
return theRequest.getRequestType() == RequestTypeEnum.GET;
|
||||
if (theRequest.getRequestType() != RequestTypeEnum.GET) {
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
return MethodMatchEnum.EXACT;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -80,9 +80,9 @@ public class PatchMethodBinding extends BaseOutcomeReturningMethodBindingWithRes
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
boolean retVal = super.incomingServerRequestMatchesMethod(theRequest);
|
||||
if (retVal) {
|
||||
public MethodMatchEnum incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
MethodMatchEnum retVal = super.incomingServerRequestMatchesMethod(theRequest);
|
||||
if (retVal.ordinal() > MethodMatchEnum.NONE.ordinal()) {
|
||||
PatchTypeParameter.getTypeForRequestOrThrowInvalidRequestException(theRequest);
|
||||
}
|
||||
return retVal;
|
||||
|
|
|
@ -35,11 +35,11 @@ import ca.uhn.fhir.rest.param.QualifierDetails;
|
|||
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
|
||||
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||
|
||||
public class RawParamsParmeter implements IParameter {
|
||||
public class RawParamsParameter implements IParameter {
|
||||
|
||||
private final List<IParameter> myAllMethodParameters;
|
||||
|
||||
public RawParamsParmeter(List<IParameter> theParameters) {
|
||||
public RawParamsParameter(List<IParameter> theParameters) {
|
||||
myAllMethodParameters = theParameters;
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,7 @@ public class RawParamsParmeter implements IParameter {
|
|||
continue;
|
||||
}
|
||||
|
||||
QualifierDetails qualifiers = SearchMethodBinding.extractQualifiersFromParameterName(nextName);
|
||||
QualifierDetails qualifiers = QualifierDetails.extractQualifiersFromParameterName(nextName);
|
||||
|
||||
boolean alreadyCaptured = false;
|
||||
for (IParameter nextParameter : myAllMethodParameters) {
|
||||
|
@ -70,7 +70,7 @@ public class RawParamsParmeter implements IParameter {
|
|||
|
||||
if (!alreadyCaptured) {
|
||||
if (retVal == null) {
|
||||
retVal = new HashMap<String, List<String>>();
|
||||
retVal = new HashMap<>();
|
||||
}
|
||||
retVal.put(nextName, Arrays.asList(theRequest.getParameters().get(nextName)));
|
||||
}
|
|
@ -114,40 +114,40 @@ public class ReadMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
public MethodMatchEnum incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
if (!theRequest.getResourceName().equals(getResourceName())) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
for (String next : theRequest.getParameters().keySet()) {
|
||||
if (!next.startsWith("_")) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
}
|
||||
if (theRequest.getId() == null) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (mySupportsVersion == false) {
|
||||
if (theRequest.getId().hasVersionIdPart()) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
}
|
||||
if (isNotBlank(theRequest.getCompartmentName())) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (theRequest.getRequestType() != RequestTypeEnum.GET && theRequest.getRequestType() != RequestTypeEnum.HEAD ) {
|
||||
ourLog.trace("Method {} doesn't match because request type is not GET or HEAD: {}", theRequest.getId(), theRequest.getRequestType());
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (Constants.PARAM_HISTORY.equals(theRequest.getOperation())) {
|
||||
if (mySupportsVersion == false) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
} else if (theRequest.getId().hasVersionIdPart() == false) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
} else if (!StringUtils.isBlank(theRequest.getOperation())) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
return true;
|
||||
return MethodMatchEnum.EXACT;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -41,7 +41,11 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
|||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
@ -61,11 +65,13 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
private final String myResourceProviderResourceName;
|
||||
private String myCompartmentName;
|
||||
private final List<String> myRequiredParamNames;
|
||||
private final List<String> myOptionalParamNames;
|
||||
private final String myCompartmentName;
|
||||
private String myDescription;
|
||||
private Integer myIdParamIndex;
|
||||
private String myQueryName;
|
||||
private boolean myAllowUnknownParams;
|
||||
private final Integer myIdParamIndex;
|
||||
private final String myQueryName;
|
||||
private final boolean myAllowUnknownParams;
|
||||
|
||||
public SearchMethodBinding(Class<? extends IBaseResource> theReturnResourceType, Class<? extends IBaseResource> theResourceProviderResourceType, Method theMethod, FhirContext theContext, Object theProvider) {
|
||||
super(theReturnResourceType, theMethod, theContext, theProvider);
|
||||
|
@ -98,6 +104,17 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
this.myResourceProviderResourceName = null;
|
||||
}
|
||||
|
||||
myRequiredParamNames = getQueryParameters()
|
||||
.stream()
|
||||
.filter(t -> t.isRequired())
|
||||
.map(t -> t.getName())
|
||||
.collect(Collectors.toList());
|
||||
myOptionalParamNames = getQueryParameters()
|
||||
.stream()
|
||||
.filter(t -> !t.isRequired())
|
||||
.map(t -> t.getName())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
|
@ -129,122 +146,129 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
public MethodMatchEnum incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
|
||||
if (theRequest.getId() != null && myIdParamIndex == null) {
|
||||
ourLog.trace("Method {} doesn't match because ID is not null: {}", getMethod(), theRequest.getId());
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.GET && theRequest.getOperation() != null && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
|
||||
ourLog.trace("Method {} doesn't match because request type is GET but operation is not null: {}", theRequest.getId(), theRequest.getOperation());
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (theRequest.getRequestType() == RequestTypeEnum.POST && !Constants.PARAM_SEARCH.equals(theRequest.getOperation())) {
|
||||
ourLog.trace("Method {} doesn't match because request type is POST but operation is not _search: {}", theRequest.getId(), theRequest.getOperation());
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (theRequest.getRequestType() != RequestTypeEnum.GET && theRequest.getRequestType() != RequestTypeEnum.POST) {
|
||||
ourLog.trace("Method {} doesn't match because request type is {}", getMethod(), theRequest.getRequestType());
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (!StringUtils.equals(myCompartmentName, theRequest.getCompartmentName())) {
|
||||
ourLog.trace("Method {} doesn't match because it is for compartment {} but request is compartment {}", getMethod(), myCompartmentName, theRequest.getCompartmentName());
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (theRequest.getParameters().get(Constants.PARAM_PAGINGACTION) != null) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
|
||||
// This is used to track all the parameters so we can reject queries that
|
||||
// have additional params we don't understand
|
||||
Set<String> methodParamsTemp = new HashSet<>();
|
||||
|
||||
Set<String> unqualifiedNames = theRequest.getUnqualifiedToQualifiedNames().keySet();
|
||||
Set<String> qualifiedParamNames = theRequest.getParameters().keySet();
|
||||
for (IParameter nextParameter : getParameters()) {
|
||||
if (!(nextParameter instanceof BaseQueryParameter)) {
|
||||
continue;
|
||||
}
|
||||
BaseQueryParameter nextQueryParameter = (BaseQueryParameter) nextParameter;
|
||||
String name = nextQueryParameter.getName();
|
||||
if (nextQueryParameter.isRequired()) {
|
||||
|
||||
if (qualifiedParamNames.contains(name)) {
|
||||
QualifierDetails qualifiers = extractQualifiersFromParameterName(name);
|
||||
if (qualifiers.passes(nextQueryParameter.getQualifierWhitelist(), nextQueryParameter.getQualifierBlacklist())) {
|
||||
methodParamsTemp.add(name);
|
||||
}
|
||||
}
|
||||
if (unqualifiedNames.contains(name)) {
|
||||
List<String> qualifiedNames = theRequest.getUnqualifiedToQualifiedNames().get(name);
|
||||
qualifiedNames = processWhitelistAndBlacklist(qualifiedNames, nextQueryParameter.getQualifierWhitelist(), nextQueryParameter.getQualifierBlacklist());
|
||||
methodParamsTemp.addAll(qualifiedNames);
|
||||
}
|
||||
if (!qualifiedParamNames.contains(name) && !unqualifiedNames.contains(name)) {
|
||||
ourLog.trace("Method {} doesn't match param '{}' is not present", getMethod().getName(), name);
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (qualifiedParamNames.contains(name)) {
|
||||
QualifierDetails qualifiers = extractQualifiersFromParameterName(name);
|
||||
if (qualifiers.passes(nextQueryParameter.getQualifierWhitelist(), nextQueryParameter.getQualifierBlacklist())) {
|
||||
methodParamsTemp.add(name);
|
||||
}
|
||||
}
|
||||
if (unqualifiedNames.contains(name)) {
|
||||
List<String> qualifiedNames = theRequest.getUnqualifiedToQualifiedNames().get(name);
|
||||
qualifiedNames = processWhitelistAndBlacklist(qualifiedNames, nextQueryParameter.getQualifierWhitelist(), nextQueryParameter.getQualifierBlacklist());
|
||||
methodParamsTemp.addAll(qualifiedNames);
|
||||
}
|
||||
if (!qualifiedParamNames.contains(name)) {
|
||||
methodParamsTemp.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (myQueryName != null) {
|
||||
String[] queryNameValues = theRequest.getParameters().get(Constants.PARAM_QUERY);
|
||||
if (queryNameValues != null && StringUtils.isNotBlank(queryNameValues[0])) {
|
||||
String queryName = queryNameValues[0];
|
||||
if (!myQueryName.equals(queryName)) {
|
||||
ourLog.trace("Query name does not match {}", myQueryName);
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
methodParamsTemp.add(Constants.PARAM_QUERY);
|
||||
} else {
|
||||
ourLog.trace("Query name does not match {}", myQueryName);
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
} else {
|
||||
String[] queryNameValues = theRequest.getParameters().get(Constants.PARAM_QUERY);
|
||||
if (queryNameValues != null && StringUtils.isNotBlank(queryNameValues[0])) {
|
||||
ourLog.trace("Query has name");
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
}
|
||||
for (String next : theRequest.getParameters().keySet()) {
|
||||
if (next.startsWith("_") && !SPECIAL_SEARCH_PARAMS.contains(truncModifierPart(next))) {
|
||||
methodParamsTemp.add(next);
|
||||
}
|
||||
}
|
||||
Set<String> keySet = theRequest.getParameters().keySet();
|
||||
|
||||
if (myAllowUnknownParams == false) {
|
||||
for (String next : keySet) {
|
||||
if (!methodParamsTemp.contains(next)) {
|
||||
return false;
|
||||
Set<String> unqualifiedNames = theRequest.getUnqualifiedToQualifiedNames().keySet();
|
||||
Set<String> qualifiedParamNames = theRequest.getParameters().keySet();
|
||||
|
||||
MethodMatchEnum retVal = MethodMatchEnum.EXACT;
|
||||
for (String nextRequestParam : theRequest.getParameters().keySet()) {
|
||||
String nextUnqualifiedRequestParam = ParameterUtil.stripModifierPart(nextRequestParam);
|
||||
if (nextRequestParam.startsWith("_") && !SPECIAL_SEARCH_PARAMS.contains(nextUnqualifiedRequestParam)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean parameterMatches = false;
|
||||
boolean approx = false;
|
||||
for (BaseQueryParameter nextMethodParam : getQueryParameters()) {
|
||||
|
||||
if (nextRequestParam.equals(nextMethodParam.getName())) {
|
||||
QualifierDetails qualifiers = QualifierDetails.extractQualifiersFromParameterName(nextRequestParam);
|
||||
if (qualifiers.passes(nextMethodParam.getQualifierWhitelist(), nextMethodParam.getQualifierBlacklist())) {
|
||||
parameterMatches = true;
|
||||
}
|
||||
} else if (nextUnqualifiedRequestParam.equals(nextMethodParam.getName())) {
|
||||
List<String> qualifiedNames = theRequest.getUnqualifiedToQualifiedNames().get(nextUnqualifiedRequestParam);
|
||||
if (passesWhitelistAndBlacklist(qualifiedNames, nextMethodParam.getQualifierWhitelist(), nextMethodParam.getQualifierBlacklist())) {
|
||||
parameterMatches = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Repetitions supplied by URL but not supported by this parameter
|
||||
if (theRequest.getParameters().get(nextRequestParam).length > 1 != nextMethodParam.supportsRepetition()) {
|
||||
approx = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
if (parameterMatches) {
|
||||
|
||||
if (approx) {
|
||||
retVal = retVal.weakerOf(MethodMatchEnum.APPROXIMATE);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
if (myAllowUnknownParams) {
|
||||
retVal = retVal.weakerOf(MethodMatchEnum.APPROXIMATE);
|
||||
} else {
|
||||
retVal = retVal.weakerOf(MethodMatchEnum.NONE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (retVal == MethodMatchEnum.NONE) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (retVal != MethodMatchEnum.NONE) {
|
||||
for (String nextRequiredParamName : myRequiredParamNames) {
|
||||
if (!qualifiedParamNames.contains(nextRequiredParamName)) {
|
||||
if (!unqualifiedNames.contains(nextRequiredParamName)) {
|
||||
retVal = MethodMatchEnum.NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private String truncModifierPart(String param) {
|
||||
int indexOfSeparator = param.indexOf(":");
|
||||
if (indexOfSeparator != -1) {
|
||||
return param.substring(0, indexOfSeparator);
|
||||
if (retVal != MethodMatchEnum.NONE) {
|
||||
for (String nextRequiredParamName : myOptionalParamNames) {
|
||||
if (!qualifiedParamNames.contains(nextRequiredParamName)) {
|
||||
if (!unqualifiedNames.contains(nextRequiredParamName)) {
|
||||
retVal = retVal.weakerOf(MethodMatchEnum.APPROXIMATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return param;
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -264,19 +288,18 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return false;
|
||||
}
|
||||
|
||||
private List<String> processWhitelistAndBlacklist(List<String> theQualifiedNames, Set<String> theQualifierWhitelist, Set<String> theQualifierBlacklist) {
|
||||
|
||||
private boolean passesWhitelistAndBlacklist(List<String> theQualifiedNames, Set<String> theQualifierWhitelist, Set<String> theQualifierBlacklist) {
|
||||
if (theQualifierWhitelist == null && theQualifierBlacklist == null) {
|
||||
return theQualifiedNames;
|
||||
return true;
|
||||
}
|
||||
ArrayList<String> retVal = new ArrayList<>(theQualifiedNames.size());
|
||||
for (String next : theQualifiedNames) {
|
||||
QualifierDetails qualifiers = extractQualifiersFromParameterName(next);
|
||||
QualifierDetails qualifiers = QualifierDetails.extractQualifiersFromParameterName(next);
|
||||
if (!qualifiers.passes(theQualifierWhitelist, theQualifierBlacklist)) {
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
retVal.add(next);
|
||||
}
|
||||
return retVal;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -284,52 +307,5 @@ public class SearchMethodBinding extends BaseResourceReturningMethodBinding {
|
|||
return getMethod().toString();
|
||||
}
|
||||
|
||||
public static QualifierDetails extractQualifiersFromParameterName(String theParamName) {
|
||||
QualifierDetails retVal = new QualifierDetails();
|
||||
if (theParamName == null || theParamName.length() == 0) {
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int dotIdx = -1;
|
||||
int colonIdx = -1;
|
||||
for (int idx = 0; idx < theParamName.length(); idx++) {
|
||||
char nextChar = theParamName.charAt(idx);
|
||||
if (nextChar == '.' && dotIdx == -1) {
|
||||
dotIdx = idx;
|
||||
} else if (nextChar == ':' && colonIdx == -1) {
|
||||
colonIdx = idx;
|
||||
}
|
||||
}
|
||||
|
||||
if (dotIdx != -1 && colonIdx != -1) {
|
||||
if (dotIdx < colonIdx) {
|
||||
retVal.setDotQualifier(theParamName.substring(dotIdx, colonIdx));
|
||||
retVal.setColonQualifier(theParamName.substring(colonIdx));
|
||||
retVal.setParamName(theParamName.substring(0, dotIdx));
|
||||
retVal.setWholeQualifier(theParamName.substring(dotIdx));
|
||||
} else {
|
||||
retVal.setColonQualifier(theParamName.substring(colonIdx, dotIdx));
|
||||
retVal.setDotQualifier(theParamName.substring(dotIdx));
|
||||
retVal.setParamName(theParamName.substring(0, colonIdx));
|
||||
retVal.setWholeQualifier(theParamName.substring(colonIdx));
|
||||
}
|
||||
} else if (dotIdx != -1) {
|
||||
retVal.setDotQualifier(theParamName.substring(dotIdx));
|
||||
retVal.setParamName(theParamName.substring(0, dotIdx));
|
||||
retVal.setWholeQualifier(theParamName.substring(dotIdx));
|
||||
} else if (colonIdx != -1) {
|
||||
retVal.setColonQualifier(theParamName.substring(colonIdx));
|
||||
retVal.setParamName(theParamName.substring(0, colonIdx));
|
||||
retVal.setWholeQualifier(theParamName.substring(colonIdx));
|
||||
} else {
|
||||
retVal.setParamName(theParamName);
|
||||
retVal.setColonQualifier(null);
|
||||
retVal.setDotQualifier(null);
|
||||
retVal.setWholeQualifier(null);
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -42,8 +42,8 @@ import ca.uhn.fhir.util.ReflectionUtil;
|
|||
public class SearchParameter extends BaseQueryParameter {
|
||||
|
||||
private static final String EMPTY_STRING = "";
|
||||
private static HashMap<RestSearchParameterTypeEnum, Set<String>> ourParamQualifiers;
|
||||
private static HashMap<Class<?>, RestSearchParameterTypeEnum> ourParamTypes;
|
||||
private static final HashMap<RestSearchParameterTypeEnum, Set<String>> ourParamQualifiers;
|
||||
private static final HashMap<Class<?>, RestSearchParameterTypeEnum> ourParamTypes;
|
||||
static final String QUALIFIER_ANY_TYPE = ":*";
|
||||
|
||||
static {
|
||||
|
@ -114,6 +114,7 @@ public class SearchParameter extends BaseQueryParameter {
|
|||
private Set<String> myQualifierWhitelist;
|
||||
private boolean myRequired;
|
||||
private Class<?> myType;
|
||||
private boolean mySupportsRepetition = false;
|
||||
|
||||
public SearchParameter() {
|
||||
}
|
||||
|
@ -202,17 +203,17 @@ public class SearchParameter extends BaseQueryParameter {
|
|||
return myParamBinder.parse(theContext, getName(), theString);
|
||||
}
|
||||
|
||||
public void setChainlists(String[] theChainWhitelist, String[] theChainBlacklist) {
|
||||
public void setChainLists(String[] theChainWhitelist, String[] theChainBlacklist) {
|
||||
myQualifierWhitelist = new HashSet<>(theChainWhitelist.length);
|
||||
myQualifierWhitelist.add(QUALIFIER_ANY_TYPE);
|
||||
|
||||
for (int i = 0; i < theChainWhitelist.length; i++) {
|
||||
if (theChainWhitelist[i].equals(OptionalParam.ALLOW_CHAIN_ANY)) {
|
||||
for (String nextChain : theChainWhitelist) {
|
||||
if (nextChain.equals(OptionalParam.ALLOW_CHAIN_ANY)) {
|
||||
myQualifierWhitelist.add('.' + OptionalParam.ALLOW_CHAIN_ANY);
|
||||
} else if (theChainWhitelist[i].equals(EMPTY_STRING)) {
|
||||
} else if (nextChain.equals(EMPTY_STRING)) {
|
||||
myQualifierWhitelist.add(".");
|
||||
} else {
|
||||
myQualifierWhitelist.add('.' + theChainWhitelist[i]);
|
||||
myQualifierWhitelist.add('.' + nextChain);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,42 +249,48 @@ public class SearchParameter extends BaseQueryParameter {
|
|||
this.myRequired = required;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsRepetition() {
|
||||
return mySupportsRepetition;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setType(FhirContext theContext, final Class<?> type, Class<? extends Collection<?>> theInnerCollectionType, Class<? extends Collection<?>> theOuterCollectionType) {
|
||||
public void setType(FhirContext theContext, final Class<?> theType, Class<? extends Collection<?>> theInnerCollectionType, Class<? extends Collection<?>> theOuterCollectionType) {
|
||||
|
||||
|
||||
this.myType = type;
|
||||
if (IQueryParameterType.class.isAssignableFrom(type)) {
|
||||
myParamBinder = new QueryParameterTypeBinder((Class<? extends IQueryParameterType>) type, myCompositeTypes);
|
||||
} else if (IQueryParameterOr.class.isAssignableFrom(type)) {
|
||||
myParamBinder = new QueryParameterOrBinder((Class<? extends IQueryParameterOr<?>>) type, myCompositeTypes);
|
||||
} else if (IQueryParameterAnd.class.isAssignableFrom(type)) {
|
||||
myParamBinder = new QueryParameterAndBinder((Class<? extends IQueryParameterAnd<?>>) type, myCompositeTypes);
|
||||
} else if (String.class.equals(type)) {
|
||||
this.myType = theType;
|
||||
if (IQueryParameterType.class.isAssignableFrom(theType)) {
|
||||
myParamBinder = new QueryParameterTypeBinder((Class<? extends IQueryParameterType>) theType, myCompositeTypes);
|
||||
} else if (IQueryParameterOr.class.isAssignableFrom(theType)) {
|
||||
myParamBinder = new QueryParameterOrBinder((Class<? extends IQueryParameterOr<?>>) theType, myCompositeTypes);
|
||||
} else if (IQueryParameterAnd.class.isAssignableFrom(theType)) {
|
||||
myParamBinder = new QueryParameterAndBinder((Class<? extends IQueryParameterAnd<?>>) theType, myCompositeTypes);
|
||||
mySupportsRepetition = true;
|
||||
} else if (String.class.equals(theType)) {
|
||||
myParamBinder = new StringBinder();
|
||||
myParamType = RestSearchParameterTypeEnum.STRING;
|
||||
} else if (Date.class.equals(type)) {
|
||||
} else if (Date.class.equals(theType)) {
|
||||
myParamBinder = new DateBinder();
|
||||
myParamType = RestSearchParameterTypeEnum.DATE;
|
||||
} else if (Calendar.class.equals(type)) {
|
||||
} else if (Calendar.class.equals(theType)) {
|
||||
myParamBinder = new CalendarBinder();
|
||||
myParamType = RestSearchParameterTypeEnum.DATE;
|
||||
} else if (IPrimitiveType.class.isAssignableFrom(type) && ReflectionUtil.isInstantiable(type)) {
|
||||
RuntimePrimitiveDatatypeDefinition def = (RuntimePrimitiveDatatypeDefinition) theContext.getElementDefinition((Class<? extends IPrimitiveType<?>>) type);
|
||||
} else if (IPrimitiveType.class.isAssignableFrom(theType) && ReflectionUtil.isInstantiable(theType)) {
|
||||
RuntimePrimitiveDatatypeDefinition def = (RuntimePrimitiveDatatypeDefinition) theContext.getElementDefinition((Class<? extends IPrimitiveType<?>>) theType);
|
||||
if (def.getNativeType() != null) {
|
||||
if (def.getNativeType().equals(Date.class)) {
|
||||
myParamBinder = new FhirPrimitiveBinder((Class<IPrimitiveType<?>>) type);
|
||||
myParamBinder = new FhirPrimitiveBinder((Class<IPrimitiveType<?>>) theType);
|
||||
myParamType = RestSearchParameterTypeEnum.DATE;
|
||||
} else if (def.getNativeType().equals(String.class)) {
|
||||
myParamBinder = new FhirPrimitiveBinder((Class<IPrimitiveType<?>>) type);
|
||||
myParamBinder = new FhirPrimitiveBinder((Class<IPrimitiveType<?>>) theType);
|
||||
myParamType = RestSearchParameterTypeEnum.STRING;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new ConfigurationException("Unsupported data type for parameter: " + type.getCanonicalName());
|
||||
throw new ConfigurationException("Unsupported data type for parameter: " + theType.getCanonicalName());
|
||||
}
|
||||
|
||||
RestSearchParameterTypeEnum typeEnum = ourParamTypes.get(type);
|
||||
RestSearchParameterTypeEnum typeEnum = ourParamTypes.get(theType);
|
||||
if (typeEnum != null) {
|
||||
Set<String> builtInQualifiers = ourParamQualifiers.get(typeEnum);
|
||||
if (builtInQualifiers != null) {
|
||||
|
@ -304,22 +311,22 @@ public class SearchParameter extends BaseQueryParameter {
|
|||
|
||||
if (myParamType != null) {
|
||||
// ok
|
||||
} else if (StringDt.class.isAssignableFrom(type)) {
|
||||
} else if (StringDt.class.isAssignableFrom(theType)) {
|
||||
myParamType = RestSearchParameterTypeEnum.STRING;
|
||||
} else if (BaseIdentifierDt.class.isAssignableFrom(type)) {
|
||||
} else if (BaseIdentifierDt.class.isAssignableFrom(theType)) {
|
||||
myParamType = RestSearchParameterTypeEnum.TOKEN;
|
||||
} else if (BaseQuantityDt.class.isAssignableFrom(type)) {
|
||||
} else if (BaseQuantityDt.class.isAssignableFrom(theType)) {
|
||||
myParamType = RestSearchParameterTypeEnum.QUANTITY;
|
||||
} else if (ReferenceParam.class.isAssignableFrom(type)) {
|
||||
} else if (ReferenceParam.class.isAssignableFrom(theType)) {
|
||||
myParamType = RestSearchParameterTypeEnum.REFERENCE;
|
||||
} else if (HasParam.class.isAssignableFrom(type)) {
|
||||
} else if (HasParam.class.isAssignableFrom(theType)) {
|
||||
myParamType = RestSearchParameterTypeEnum.STRING;
|
||||
} else {
|
||||
throw new ConfigurationException("Unknown search parameter type: " + type);
|
||||
throw new ConfigurationException("Unknown search parameter theType: " + theType);
|
||||
}
|
||||
|
||||
// NB: Once this is enabled, we should return true from handlesMissing if
|
||||
// it's a collection type
|
||||
// it's a collection theType
|
||||
// if (theInnerCollectionType != null) {
|
||||
// this.parser = new CollectionBinder(this.parser, theInnerCollectionType);
|
||||
// }
|
||||
|
|
|
@ -22,17 +22,13 @@ package ca.uhn.fhir.rest.server.method;
|
|||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.IResource;
|
||||
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
|
||||
import ca.uhn.fhir.model.base.resource.BaseOperationOutcome;
|
||||
import ca.uhn.fhir.model.primitive.IdDt;
|
||||
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.rest.annotation.Transaction;
|
||||
import ca.uhn.fhir.rest.annotation.TransactionParam;
|
||||
|
@ -92,17 +88,17 @@ public class TransactionMethodBinding extends BaseResourceReturningMethodBinding
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
public MethodMatchEnum incomingServerRequestMatchesMethod(RequestDetails theRequest) {
|
||||
if (theRequest.getRequestType() != RequestTypeEnum.POST) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (isNotBlank(theRequest.getOperation())) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
if (isNotBlank(theRequest.getResourceName())) {
|
||||
return false;
|
||||
return MethodMatchEnum.NONE;
|
||||
}
|
||||
return true;
|
||||
return MethodMatchEnum.EXACT;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
@ -3,14 +3,16 @@ package ca.uhn.fhir.rest.server;
|
|||
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.model.api.annotation.ResourceDef;
|
||||
import java.util.List;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import org.hl7.fhir.instance.model.api.IBaseMetaType;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class CommonResourceSupertypeScannerTest {
|
||||
|
||||
private final CommonResourceSupertypeScanner scanner = new CommonResourceSupertypeScanner();
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package ca.uhn.fhir.rest.server.method;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class MethodMatchEnumTest {
|
||||
|
||||
@Test
|
||||
public void testOrder() {
|
||||
assertEquals(0, MethodMatchEnum.NONE.ordinal());
|
||||
assertEquals(1, MethodMatchEnum.APPROXIMATE.ordinal());
|
||||
assertEquals(2, MethodMatchEnum.EXACT.ordinal());
|
||||
}
|
||||
|
||||
}
|
|
@ -20,6 +20,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
|||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
|
@ -56,17 +57,17 @@ public class ReadMethodBindingTest {
|
|||
// Read
|
||||
ReadMethodBinding binding = createBinding(new MyProvider());
|
||||
when(myRequestDetails.getId()).thenReturn(new IdDt("Patient/123"));
|
||||
assertTrue(binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
assertEquals(MethodMatchEnum.EXACT, binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
|
||||
// VRead
|
||||
when(myRequestDetails.getId()).thenReturn(new IdDt("Patient/123/_history/123"));
|
||||
assertFalse(binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
assertEquals(MethodMatchEnum.NONE, binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
|
||||
// Type history
|
||||
when(myRequestDetails.getId()).thenReturn(new IdDt("Patient/123"));
|
||||
when(myRequestDetails.getResourceName()).thenReturn("Patient");
|
||||
when(myRequestDetails.getOperation()).thenReturn("_history");
|
||||
assertFalse(binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
assertEquals(MethodMatchEnum.NONE, binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
|
||||
}
|
||||
|
||||
|
@ -89,27 +90,27 @@ public class ReadMethodBindingTest {
|
|||
ReadMethodBinding binding = createBinding(new MyProvider());
|
||||
when(myRequestDetails.getResourceName()).thenReturn("Observation");
|
||||
when(myRequestDetails.getId()).thenReturn(new IdDt("Observation/123"));
|
||||
assertFalse(binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
assertEquals(MethodMatchEnum.NONE, binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
|
||||
// Read
|
||||
when(myRequestDetails.getResourceName()).thenReturn("Patient");
|
||||
when(myRequestDetails.getId()).thenReturn(new IdDt("Patient/123"));
|
||||
assertTrue(binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
assertEquals(MethodMatchEnum.EXACT, binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
|
||||
// VRead
|
||||
when(myRequestDetails.getId()).thenReturn(new IdDt("Patient/123/_history/123"));
|
||||
when(myRequestDetails.getOperation()).thenReturn("_history");
|
||||
assertTrue(binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
assertEquals(MethodMatchEnum.EXACT, binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
|
||||
// Some other operation
|
||||
when(myRequestDetails.getId()).thenReturn(new IdDt("Patient/123/_history/123"));
|
||||
when(myRequestDetails.getOperation()).thenReturn("$foo");
|
||||
assertFalse(binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
assertEquals(MethodMatchEnum.NONE, binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
|
||||
// History operation
|
||||
when(myRequestDetails.getId()).thenReturn(new IdDt("Patient/123"));
|
||||
when(myRequestDetails.getOperation()).thenReturn("_history");
|
||||
assertFalse(binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
assertEquals(MethodMatchEnum.NONE, binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
|
||||
}
|
||||
|
||||
|
@ -130,7 +131,7 @@ public class ReadMethodBindingTest {
|
|||
when(myRequestDetails.getId()).thenReturn(new IdDt("Patient/123"));
|
||||
|
||||
ReadMethodBinding binding = createBinding(new MyProvider());
|
||||
assertFalse(binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
assertEquals(MethodMatchEnum.NONE, binding.incomingServerRequestMatchesMethod(myRequestDetails));
|
||||
}
|
||||
|
||||
public ReadMethodBinding createBinding(Object theProvider) throws NoSuchMethodException {
|
||||
|
|
|
@ -42,39 +42,39 @@ public class SearchMethodBindingTest {
|
|||
public void methodShouldNotMatchWhenUnderscoreQueryParameter() throws NoSuchMethodException {
|
||||
Assert.assertThat(getBinding("param", String.class).incomingServerRequestMatchesMethod(
|
||||
mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "_include", new String[]{"test"}))),
|
||||
Matchers.is(false));
|
||||
Matchers.is(MethodMatchEnum.NONE));
|
||||
Assert.assertThat(getBinding("paramAndTest", String.class, String.class).incomingServerRequestMatchesMethod(
|
||||
mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "_include", new String[]{"test"}))),
|
||||
Matchers.is(false));
|
||||
Matchers.is(MethodMatchEnum.NONE));
|
||||
Assert.assertThat(getBinding("paramAndUnderscoreTest", String.class, String.class).incomingServerRequestMatchesMethod(
|
||||
mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "_include", new String[]{"test"}))),
|
||||
Matchers.is(false));
|
||||
Matchers.is(MethodMatchEnum.NONE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodShouldNotMatchWhenExtraQueryParameter() throws NoSuchMethodException {
|
||||
Assert.assertThat(getBinding("param", String.class).incomingServerRequestMatchesMethod(
|
||||
mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "extra", new String[]{"test"}))),
|
||||
Matchers.is(false));
|
||||
Matchers.is(MethodMatchEnum.NONE));
|
||||
Assert.assertThat(getBinding("paramAndTest", String.class, String.class).incomingServerRequestMatchesMethod(
|
||||
mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "extra", new String[]{"test"}))),
|
||||
Matchers.is(false));
|
||||
Matchers.is(MethodMatchEnum.NONE));
|
||||
Assert.assertThat(getBinding("paramAndUnderscoreTest", String.class, String.class).incomingServerRequestMatchesMethod(
|
||||
mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "extra", new String[]{"test"}))),
|
||||
Matchers.is(false));
|
||||
Matchers.is(MethodMatchEnum.NONE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void methodMatchesOwnParams() throws NoSuchMethodException {
|
||||
Assert.assertThat(getBinding("param", String.class).incomingServerRequestMatchesMethod(
|
||||
mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}))),
|
||||
Matchers.is(true));
|
||||
Matchers.is(MethodMatchEnum.EXACT));
|
||||
Assert.assertThat(getBinding("paramAndTest", String.class, String.class).incomingServerRequestMatchesMethod(
|
||||
mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "test", new String[]{"test"}))),
|
||||
Matchers.is(true));
|
||||
Matchers.is(MethodMatchEnum.EXACT));
|
||||
Assert.assertThat(getBinding("paramAndUnderscoreTest", String.class, String.class).incomingServerRequestMatchesMethod(
|
||||
mockSearchRequest(ImmutableMap.of("param", new String[]{"value"}, "_test", new String[]{"test"}))),
|
||||
Matchers.is(true));
|
||||
Matchers.is(MethodMatchEnum.EXACT));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -83,10 +83,10 @@ public class SearchMethodBindingTest {
|
|||
ourLog.info("Testing binding: {}", binding);
|
||||
Assert.assertThat(binding.incomingServerRequestMatchesMethod(
|
||||
mockSearchRequest(ImmutableMap.of("refChainBlacklist.badChain", new String[]{"foo"}))),
|
||||
Matchers.is(false));
|
||||
Matchers.is(MethodMatchEnum.NONE));
|
||||
Assert.assertThat(binding.incomingServerRequestMatchesMethod(
|
||||
mockSearchRequest(ImmutableMap.of("refChainBlacklist.goodChain", new String[]{"foo"}))),
|
||||
Matchers.is(true));
|
||||
Matchers.is(MethodMatchEnum.EXACT));
|
||||
}
|
||||
|
||||
private SearchMethodBinding getBinding(String name, Class<?>... parameters) throws NoSuchMethodException {
|
||||
|
|
|
@ -612,8 +612,7 @@ public class XmlParserDstu2_1Test {
|
|||
String out = xmlParser.encodeResourceToString(patient);
|
||||
ourLog.info(out);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(out, stringContainsInOrder("<identifier>",
|
||||
assertThat(out, stringContainsInOrder("<identifier>",
|
||||
"<type>",
|
||||
"<coding>",
|
||||
"<system value=\"http://hl7.org/fhir/v2/0203\"/>",
|
||||
|
@ -623,7 +622,6 @@ public class XmlParserDstu2_1Test {
|
|||
"<system value=\"SYS\"/>",
|
||||
"<value value=\"VAL\"/>",
|
||||
"</identifier>"));
|
||||
//@formatter:on
|
||||
|
||||
patient = ourCtx.newXmlParser().parseResource(Patient.class, out);
|
||||
assertEquals("http://hl7.org/fhir/v2/0203", patient.getIdentifier().get(0).getType().getCoding().get(0).getSystem());
|
||||
|
@ -645,120 +643,6 @@ public class XmlParserDstu2_1Test {
|
|||
assertEquals("2015-10-05", mo.getDateWrittenElement().getValueAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeAndParseMetaProfileAndTags() {
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("FAMILY");
|
||||
|
||||
p.getMeta().addProfile("http://foo/Profile1");
|
||||
p.getMeta().addProfile("http://foo/Profile2");
|
||||
|
||||
p.getMeta().addTag().setSystem("scheme1").setCode("term1").setDisplay("label1");
|
||||
p.getMeta().addTag().setSystem("scheme2").setCode("term2").setDisplay("label2");
|
||||
|
||||
p.getMeta().addSecurity().setSystem("sec_scheme1").setCode("sec_term1").setDisplay("sec_label1");
|
||||
p.getMeta().addSecurity().setSystem("sec_scheme2").setCode("sec_term2").setDisplay("sec_label2");
|
||||
|
||||
String enc = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
|
||||
ourLog.info(enc);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(enc, stringContainsInOrder("<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<meta>",
|
||||
"<meta>",
|
||||
"<profile value=\"http://foo/Profile1\"/>",
|
||||
"<profile value=\"http://foo/Profile2\"/>",
|
||||
"<tag>",
|
||||
"<system value=\"scheme1\"/>",
|
||||
"<code value=\"term1\"/>",
|
||||
"<display value=\"label1\"/>",
|
||||
"</tag>",
|
||||
"<tag>",
|
||||
"<system value=\"scheme2\"/>",
|
||||
"<code value=\"term2\"/>",
|
||||
"<display value=\"label2\"/>",
|
||||
"</tag>",
|
||||
"</meta>",
|
||||
"</meta>",
|
||||
"<name>",
|
||||
"<family value=\"FAMILY\"/>",
|
||||
"</name>",
|
||||
"</Patient>"));
|
||||
//@formatter:on
|
||||
|
||||
Patient parsed = ourCtx.newXmlParser().parseResource(Patient.class, enc);
|
||||
List<UriType> gotLabels = parsed.getMeta().getProfile();
|
||||
assertEquals(2, gotLabels.size());
|
||||
UriType label = gotLabels.get(0);
|
||||
assertEquals("http://foo/Profile1", label.getValue());
|
||||
label = gotLabels.get(1);
|
||||
assertEquals("http://foo/Profile2", label.getValue());
|
||||
|
||||
List<Coding> tagList = parsed.getMeta().getTag();
|
||||
assertEquals(2, tagList.size());
|
||||
assertEquals("scheme1", tagList.get(0).getSystem());
|
||||
assertEquals("term1", tagList.get(0).getCode());
|
||||
assertEquals("label1", tagList.get(0).getDisplay());
|
||||
assertEquals("scheme2", tagList.get(1).getSystem());
|
||||
assertEquals("term2", tagList.get(1).getCode());
|
||||
assertEquals("label2", tagList.get(1).getDisplay());
|
||||
|
||||
tagList = parsed.getMeta().getSecurity();
|
||||
assertEquals(2, tagList.size());
|
||||
assertEquals("sec_scheme1", tagList.get(0).getSystem());
|
||||
assertEquals("sec_term1", tagList.get(0).getCode());
|
||||
assertEquals("sec_label1", tagList.get(0).getDisplay());
|
||||
assertEquals("sec_scheme2", tagList.get(1).getSystem());
|
||||
assertEquals("sec_term2", tagList.get(1).getCode());
|
||||
assertEquals("sec_label2", tagList.get(1).getDisplay());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeAndParseMetaProfiles() {
|
||||
Patient p = new Patient();
|
||||
p.addName().addFamily("FAMILY");
|
||||
|
||||
p.getMeta().addTag().setSystem("scheme1").setCode("term1").setDisplay("label1");
|
||||
p.getMeta().addTag().setSystem("scheme2").setCode("term2").setDisplay("label2");
|
||||
|
||||
String enc = ourCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(p);
|
||||
ourLog.info(enc);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(enc, stringContainsInOrder("<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<meta>",
|
||||
"<meta>",
|
||||
"<tag>",
|
||||
"<system value=\"scheme1\"/>",
|
||||
"<code value=\"term1\"/>",
|
||||
"<display value=\"label1\"/>",
|
||||
"</tag>",
|
||||
"<tag>",
|
||||
"<system value=\"scheme2\"/>",
|
||||
"<code value=\"term2\"/>",
|
||||
"<display value=\"label2\"/>",
|
||||
"</tag>",
|
||||
"</meta>",
|
||||
"</meta>",
|
||||
"<name>",
|
||||
"<family value=\"FAMILY\"/>",
|
||||
"</name>",
|
||||
"</Patient>"));
|
||||
//@formatter:on
|
||||
|
||||
Patient parsed = ourCtx.newXmlParser().parseResource(Patient.class, enc);
|
||||
assertThat(parsed.getMeta().getProfile(), empty());
|
||||
|
||||
List<Coding> tagList = parsed.getMeta().getTag();
|
||||
assertEquals(2, tagList.size());
|
||||
assertEquals("scheme1", tagList.get(0).getSystem());
|
||||
assertEquals("term1", tagList.get(0).getCode());
|
||||
assertEquals("label1", tagList.get(0).getDisplay());
|
||||
assertEquals("scheme2", tagList.get(1).getSystem());
|
||||
assertEquals("term2", tagList.get(1).getCode());
|
||||
assertEquals("label2", tagList.get(1).getDisplay());
|
||||
}
|
||||
|
||||
/**
|
||||
* See #336
|
||||
*/
|
||||
|
|
|
@ -156,53 +156,47 @@ public class ServerSearchDstu2Test {
|
|||
|
||||
public static class DummyPatientResourceProvider {
|
||||
|
||||
//@formatter:off
|
||||
@Search(allowUnknownParams=true)
|
||||
public List<IBaseResource> searchParam1(
|
||||
@RequiredParam(name = "param1") StringParam theParam) {
|
||||
ourLastMethod = "searchParam1";
|
||||
ourLastRef = theParam;
|
||||
|
||||
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
List<IBaseResource> retVal = new ArrayList<>();
|
||||
Patient patient = new Patient();
|
||||
patient.setId("123");
|
||||
patient.addName().addGiven("GIVEN");
|
||||
retVal.add(patient);
|
||||
return retVal;
|
||||
}
|
||||
//@formatter:on
|
||||
|
||||
//@formatter:off
|
||||
@Search(allowUnknownParams=true)
|
||||
public List<IBaseResource> searchParam2(
|
||||
@RequiredParam(name = "param2") StringParam theParam) {
|
||||
ourLastMethod = "searchParam2";
|
||||
ourLastRef = theParam;
|
||||
|
||||
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
List<IBaseResource> retVal = new ArrayList<>();
|
||||
Patient patient = new Patient();
|
||||
patient.setId("123");
|
||||
patient.addName().addGiven("GIVEN");
|
||||
retVal.add(patient);
|
||||
return retVal;
|
||||
}
|
||||
//@formatter:on
|
||||
|
||||
//@formatter:off
|
||||
@Search(allowUnknownParams=true)
|
||||
public List<IBaseResource> searchParam3(
|
||||
@RequiredParam(name = "param3") ReferenceParam theParam) {
|
||||
ourLastMethod = "searchParam3";
|
||||
ourLastRef2 = theParam;
|
||||
|
||||
List<IBaseResource> retVal = new ArrayList<IBaseResource>();
|
||||
List<IBaseResource> retVal = new ArrayList<>();
|
||||
Patient patient = new Patient();
|
||||
patient.setId("123");
|
||||
patient.addName().addGiven("GIVEN");
|
||||
retVal.add(patient);
|
||||
return retVal;
|
||||
}
|
||||
//@formatter:on
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ public class XmlParserDstu3Test {
|
|||
}
|
||||
|
||||
/**
|
||||
* We specifically include extensions on CapabilityStatment even in
|
||||
* We specifically include extensions on CapabilityStatement even in
|
||||
* summary mode, since this is behaviour that people depend on
|
||||
*/
|
||||
@Test
|
||||
|
@ -839,7 +839,6 @@ public class XmlParserDstu3Test {
|
|||
ourLog.info(enc);
|
||||
|
||||
assertThat(enc, stringContainsInOrder("<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<meta>",
|
||||
"<meta>",
|
||||
"<profile value=\"http://foo/Profile1\"/>",
|
||||
"<profile value=\"http://foo/Profile2\"/>",
|
||||
|
@ -854,7 +853,6 @@ public class XmlParserDstu3Test {
|
|||
"<display value=\"label2\"/>",
|
||||
"</tag>",
|
||||
"</meta>",
|
||||
"</meta>",
|
||||
"<name>",
|
||||
"<family value=\"FAMILY\"/>",
|
||||
"</name>",
|
||||
|
@ -899,7 +897,6 @@ public class XmlParserDstu3Test {
|
|||
ourLog.info(enc);
|
||||
|
||||
assertThat(enc, stringContainsInOrder("<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<meta>",
|
||||
"<meta>",
|
||||
"<tag>",
|
||||
"<system value=\"scheme1\"/>",
|
||||
|
@ -912,7 +909,6 @@ public class XmlParserDstu3Test {
|
|||
"<display value=\"label2\"/>",
|
||||
"</tag>",
|
||||
"</meta>",
|
||||
"</meta>",
|
||||
"<name>",
|
||||
"<family value=\"FAMILY\"/>",
|
||||
"</name>",
|
||||
|
@ -1935,7 +1931,7 @@ public class XmlParserDstu3Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeUndeclaredBlock() throws Exception {
|
||||
public void testEncodeUndeclaredBlock() {
|
||||
FooMessageHeader.FooMessageSourceComponent source = new FooMessageHeader.FooMessageSourceComponent();
|
||||
source.getMessageHeaderApplicationId().setValue("APPID");
|
||||
source.setName("NAME");
|
||||
|
@ -1965,7 +1961,7 @@ public class XmlParserDstu3Test {
|
|||
Patient patient = new Patient();
|
||||
patient.addAddress().setUse(AddressUse.HOME);
|
||||
EnumFactory<AddressUse> fact = new AddressUseEnumFactory();
|
||||
PrimitiveType<AddressUse> enumeration = new Enumeration<AddressUse>(fact).setValue(AddressUse.HOME);
|
||||
PrimitiveType<AddressUse> enumeration = new Enumeration<>(fact).setValue(AddressUse.HOME);
|
||||
patient.addExtension().setUrl("urn:foo").setValue(enumeration);
|
||||
|
||||
String val = parser.encodeResourceToString(patient);
|
||||
|
@ -1981,7 +1977,7 @@ public class XmlParserDstu3Test {
|
|||
|
||||
@Test
|
||||
public void testEncodeWithContained() {
|
||||
List<Resource> contained = new ArrayList<Resource>();
|
||||
List<Resource> contained = new ArrayList<>();
|
||||
|
||||
// Will be added by reference
|
||||
Patient p = new Patient();
|
||||
|
@ -2037,7 +2033,7 @@ public class XmlParserDstu3Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeWithDontEncodeElements() throws Exception {
|
||||
public void testEncodeWithDontEncodeElements() {
|
||||
Patient patient = new Patient();
|
||||
patient.setId("123");
|
||||
patient.getMeta().addProfile("http://profile");
|
||||
|
@ -2092,7 +2088,7 @@ public class XmlParserDstu3Test {
|
|||
{
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
p.setDontEncodeElements(Sets.newHashSet("Patient.meta"));
|
||||
p.setEncodeElements(new HashSet<String>(Arrays.asList("Patient.name")));
|
||||
p.setEncodeElements(new HashSet<>(Arrays.asList("Patient.name")));
|
||||
p.setPrettyPrint(true);
|
||||
String out = p.encodeResourceToString(patient);
|
||||
ourLog.info(out);
|
||||
|
@ -2106,7 +2102,7 @@ public class XmlParserDstu3Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeWithEncodeElements() throws Exception {
|
||||
public void testEncodeWithEncodeElements() {
|
||||
Patient patient = new Patient();
|
||||
patient.getMeta().addProfile("http://profile");
|
||||
patient.addName().setFamily("FAMILY");
|
||||
|
@ -2129,7 +2125,7 @@ public class XmlParserDstu3Test {
|
|||
}
|
||||
{
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
p.setEncodeElements(new HashSet<String>(Arrays.asList("Patient.name")));
|
||||
p.setEncodeElements(new HashSet<>(Arrays.asList("Patient.name")));
|
||||
p.setPrettyPrint(true);
|
||||
String out = p.encodeResourceToString(bundle);
|
||||
ourLog.info(out);
|
||||
|
@ -2140,7 +2136,7 @@ public class XmlParserDstu3Test {
|
|||
}
|
||||
{
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
p.setEncodeElements(new HashSet<String>(Arrays.asList("Patient")));
|
||||
p.setEncodeElements(new HashSet<>(Arrays.asList("Patient")));
|
||||
p.setPrettyPrint(true);
|
||||
String out = p.encodeResourceToString(bundle);
|
||||
ourLog.info(out);
|
||||
|
@ -2153,7 +2149,7 @@ public class XmlParserDstu3Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testEncodeWithEncodeElementsAppliesToChildResourcesOnly() throws Exception {
|
||||
public void testEncodeWithEncodeElementsAppliesToChildResourcesOnly() {
|
||||
Patient patient = new Patient();
|
||||
patient.getMeta().addProfile("http://profile");
|
||||
patient.addName().setFamily("FAMILY");
|
||||
|
@ -2208,7 +2204,7 @@ public class XmlParserDstu3Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testMoreExtensions() throws Exception {
|
||||
public void testMoreExtensions() {
|
||||
|
||||
Patient patient = new Patient();
|
||||
patient.addIdentifier().setUse(IdentifierUse.OFFICIAL).setSystem("urn:example").setValue("7000135");
|
||||
|
|
|
@ -112,7 +112,6 @@ public class SearchCountParamDstu3Test {
|
|||
assertEquals("searchWithNoCountParam", ourLastMethod);
|
||||
assertEquals(null, ourLastParam);
|
||||
|
||||
//@formatter:off
|
||||
assertThat(responseContent, stringContainsInOrder(
|
||||
"<link>",
|
||||
"<relation value=\"self\"/>",
|
||||
|
@ -122,7 +121,6 @@ public class SearchCountParamDstu3Test {
|
|||
"<relation value=\"next\"/>",
|
||||
"<url value=\"http://localhost:" + ourPort + "?_getpages=", "&_getpagesoffset=2&_count=2&_bundletype=searchset\"/>",
|
||||
"</link>"));
|
||||
//@formatter:on
|
||||
|
||||
} finally {
|
||||
IOUtils.closeQuietly(status.getEntity().getContent());
|
||||
|
|
|
@ -0,0 +1,221 @@
|
|||
package ca.uhn.fhir.rest.server;
|
||||
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.rest.annotation.OptionalParam;
|
||||
import ca.uhn.fhir.rest.annotation.RequiredParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
import ca.uhn.fhir.rest.param.DateParam;
|
||||
import ca.uhn.fhir.rest.param.DateRangeParam;
|
||||
import ca.uhn.fhir.rest.param.StringAndListParam;
|
||||
import ca.uhn.fhir.test.utilities.server.RestfulServerRule;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class SearchMethodPriorityTest {
|
||||
|
||||
@ClassRule
|
||||
public static RestfulServerRule ourServerRule = new RestfulServerRule(FhirVersionEnum.R4);
|
||||
|
||||
private String myLastMethod;
|
||||
private IGenericClient myClient;
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myLastMethod = null;
|
||||
myClient = ourServerRule.getFhirClient();
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
ourServerRule.getRestfulServer().unregisterAllProviders();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateRangeSelectedWhenMultipleParametersProvided() {
|
||||
ourServerRule.getRestfulServer().registerProviders(new DateStrengthsWithRequiredResourceProvider());
|
||||
|
||||
myClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.BIRTHDATE.after().day("2001-01-01"))
|
||||
.and(Patient.BIRTHDATE.before().day("2002-01-01"))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
assertEquals("findDateRangeParam", myLastMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateRangeNotSelectedWhenSingleParameterProvided() {
|
||||
ourServerRule.getRestfulServer().registerProviders(new DateStrengthsWithRequiredResourceProvider());
|
||||
|
||||
myClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.BIRTHDATE.after().day("2001-01-01"))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
assertEquals("findDateParam", myLastMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyDateSearchProvidedWithNoParameters() {
|
||||
ourServerRule.getRestfulServer().registerProviders(new DateStrengthsWithRequiredResourceProvider());
|
||||
|
||||
myClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
assertEquals("find", myLastMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringAndListSelectedWhenMultipleParametersProvided() {
|
||||
ourServerRule.getRestfulServer().registerProviders(new StringStrengthsWithOptionalResourceProvider());
|
||||
|
||||
myClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.NAME.matches().value("hello"))
|
||||
.and(Patient.NAME.matches().value("goodbye"))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
assertEquals("findStringAndListParam", myLastMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringAndListNotSelectedWhenSingleParameterProvided() {
|
||||
ourServerRule.getRestfulServer().registerProviders(new StringStrengthsWithOptionalResourceProvider());
|
||||
|
||||
myClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.where(Patient.NAME.matches().value("hello"))
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
assertEquals("findString", myLastMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyStringSearchProvidedWithNoParameters() {
|
||||
ourServerRule.getRestfulServer().registerProviders(new StringStrengthsWithOptionalResourceProvider());
|
||||
|
||||
myClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
assertEquals("find", myLastMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyStringSearchProvidedWithNoParameters2() {
|
||||
ourServerRule.getRestfulServer().registerProviders(new StringStrengthsWithOptionalResourceProviderReverseOrder());
|
||||
|
||||
myClient
|
||||
.search()
|
||||
.forResource("Patient")
|
||||
.returnBundle(Bundle.class)
|
||||
.execute();
|
||||
|
||||
assertEquals("find", myLastMethod);
|
||||
}
|
||||
|
||||
public class DateStrengthsWithRequiredResourceProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<Patient> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<Patient> find() {
|
||||
myLastMethod = "find";
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
@Search()
|
||||
public List<Patient> findDateParam(
|
||||
@RequiredParam(name = Patient.SP_BIRTHDATE) DateParam theDate) {
|
||||
myLastMethod = "findDateParam";
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
@Search()
|
||||
public List<Patient> findDateRangeParam(
|
||||
@RequiredParam(name = Patient.SP_BIRTHDATE) DateRangeParam theRange) {
|
||||
myLastMethod = "findDateRangeParam";
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class StringStrengthsWithOptionalResourceProvider implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<Patient> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
@Search()
|
||||
public List<Patient> findString(
|
||||
@OptionalParam(name = Patient.SP_NAME) String theDate) {
|
||||
myLastMethod = "findString";
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
@Search()
|
||||
public List<Patient> findStringAndListParam(
|
||||
@OptionalParam(name = Patient.SP_NAME) StringAndListParam theRange) {
|
||||
myLastMethod = "findStringAndListParam";
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<Patient> find() {
|
||||
myLastMethod = "find";
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public class StringStrengthsWithOptionalResourceProviderReverseOrder implements IResourceProvider {
|
||||
|
||||
@Override
|
||||
public Class<Patient> getResourceType() {
|
||||
return Patient.class;
|
||||
}
|
||||
|
||||
@Search()
|
||||
public List<Patient> findA(
|
||||
@OptionalParam(name = Patient.SP_NAME) String theDate) {
|
||||
myLastMethod = "findString";
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
@Search
|
||||
public List<Patient> findB() {
|
||||
myLastMethod = "find";
|
||||
return Lists.newArrayList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -7,6 +7,7 @@ import ca.uhn.fhir.rest.annotation.Search;
|
|||
import ca.uhn.fhir.rest.api.Constants;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import ca.uhn.fhir.util.TestUtil;
|
||||
import com.google.common.base.Charsets;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@ -18,9 +19,12 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.*;
|
||||
import org.hl7.fhir.r4.model.Bundle;
|
||||
import org.hl7.fhir.r4.model.IdType;
|
||||
import org.hl7.fhir.r4.model.MedicationRequest;
|
||||
import org.hl7.fhir.r4.model.Patient;
|
||||
import org.hl7.fhir.r4.model.Reference;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
|
@ -32,14 +36,13 @@ import java.util.List;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.containsStringIgnoringCase;
|
||||
import static org.hamcrest.CoreMatchers.containsStringIgnoringCase;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.containsStringIgnoringCase;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
|
||||
public class SummaryParamR4Test {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(SummaryParamR4Test.class);
|
||||
|
@ -223,7 +226,7 @@ public class SummaryParamR4Test {
|
|||
assertThat(responseContent, (containsString("entry")));
|
||||
assertThat(responseContent, (containsString(">TEXT<")));
|
||||
assertThat(responseContent, (containsString("Medication/123")));
|
||||
assertThat(responseContent, not(CoreMatchers.containsStringIgnoringCase("note")));
|
||||
assertThat(responseContent, not(containsStringIgnoringCase("note")));
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -21,10 +21,12 @@ package ca.uhn.fhir.test.utilities.server;
|
|||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||
import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
|
||||
import ca.uhn.fhir.rest.server.RestfulServer;
|
||||
import ca.uhn.fhir.test.utilities.JettyUtil;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
|
@ -43,35 +45,66 @@ import java.util.concurrent.TimeUnit;
|
|||
public class RestfulServerRule implements TestRule {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(RestfulServerRule.class);
|
||||
|
||||
private final FhirContext myFhirContext;
|
||||
private final Object[] myProviders;
|
||||
private FhirContext myFhirContext;
|
||||
private Object[] myProviders;
|
||||
private FhirVersionEnum myFhirVersion;
|
||||
private Server myServer;
|
||||
private RestfulServer myServlet;
|
||||
private int myPort;
|
||||
private CloseableHttpClient myHttpClient;
|
||||
private IGenericClient myFhirClient;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
public RestfulServerRule(FhirContext theFhirContext, Object... theProviders) {
|
||||
Validate.notNull(theFhirContext);
|
||||
myFhirContext = theFhirContext;
|
||||
myProviders = theProviders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor: If this is used, it will create and tear down a FhirContext which is good for memory
|
||||
*/
|
||||
public RestfulServerRule(FhirVersionEnum theFhirVersionEnum) {
|
||||
Validate.notNull(theFhirVersionEnum);
|
||||
myFhirVersion = theFhirVersionEnum;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement theBase, Description theDescription) {
|
||||
return new Statement() {
|
||||
@Override
|
||||
public void evaluate() throws Throwable {
|
||||
createContextIfNeeded();
|
||||
startServer();
|
||||
theBase.evaluate();
|
||||
stopServer();
|
||||
destroyContextIfWeCreatedIt();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void createContextIfNeeded() {
|
||||
if (myFhirVersion != null) {
|
||||
myFhirContext = new FhirContext(myFhirVersion);
|
||||
}
|
||||
}
|
||||
|
||||
private void destroyContextIfWeCreatedIt() {
|
||||
if (myFhirVersion != null) {
|
||||
myFhirContext = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void stopServer() throws Exception {
|
||||
JettyUtil.closeServer(myServer);
|
||||
myServer = null;
|
||||
myFhirClient = null;
|
||||
|
||||
myHttpClient.close();
|
||||
myHttpClient = null;
|
||||
}
|
||||
|
||||
private void startServer() throws Exception {
|
||||
|
@ -80,7 +113,9 @@ public class RestfulServerRule implements TestRule {
|
|||
ServletHandler servletHandler = new ServletHandler();
|
||||
myServlet = new RestfulServer(myFhirContext);
|
||||
myServlet.setDefaultPrettyPrint(true);
|
||||
myServlet.registerProviders(myProviders);
|
||||
if (myProviders != null) {
|
||||
myServlet.registerProviders(myProviders);
|
||||
}
|
||||
ServletHolder servletHolder = new ServletHolder(myServlet);
|
||||
servletHandler.addServletWithMapping(servletHolder, "/*");
|
||||
|
||||
|
|
|
@ -644,7 +644,6 @@ public class XmlParserDstu2_1Test {
|
|||
|
||||
//@formatter:off
|
||||
assertThat(enc, stringContainsInOrder("<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<meta>",
|
||||
"<meta>",
|
||||
"<profile value=\"http://foo/Profile1\"/>",
|
||||
"<profile value=\"http://foo/Profile2\"/>",
|
||||
|
@ -659,7 +658,6 @@ public class XmlParserDstu2_1Test {
|
|||
"<display value=\"label2\"/>",
|
||||
"</tag>",
|
||||
"</meta>",
|
||||
"</meta>",
|
||||
"<name>",
|
||||
"<family value=\"FAMILY\"/>",
|
||||
"</name>",
|
||||
|
@ -706,7 +704,6 @@ public class XmlParserDstu2_1Test {
|
|||
|
||||
//@formatter:off
|
||||
assertThat(enc, stringContainsInOrder("<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<meta>",
|
||||
"<meta>",
|
||||
"<tag>",
|
||||
"<system value=\"scheme1\"/>",
|
||||
|
@ -719,7 +716,6 @@ public class XmlParserDstu2_1Test {
|
|||
"<display value=\"label2\"/>",
|
||||
"</tag>",
|
||||
"</meta>",
|
||||
"</meta>",
|
||||
"<name>",
|
||||
"<family value=\"FAMILY\"/>",
|
||||
"</name>",
|
||||
|
|
|
@ -755,7 +755,6 @@ public class Dstu3XmlParserTest {
|
|||
ourLog.info(enc);
|
||||
|
||||
assertThat(enc, stringContainsInOrder("<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<meta>",
|
||||
"<meta>",
|
||||
"<profile value=\"http://foo/Profile1\"/>",
|
||||
"<profile value=\"http://foo/Profile2\"/>",
|
||||
|
@ -770,7 +769,6 @@ public class Dstu3XmlParserTest {
|
|||
"<display value=\"label2\"/>",
|
||||
"</tag>",
|
||||
"</meta>",
|
||||
"</meta>",
|
||||
"<name>",
|
||||
"<family value=\"FAMILY\"/>",
|
||||
"</name>",
|
||||
|
@ -815,7 +813,6 @@ public class Dstu3XmlParserTest {
|
|||
ourLog.info(enc);
|
||||
|
||||
assertThat(enc, stringContainsInOrder("<Patient xmlns=\"http://hl7.org/fhir\">",
|
||||
"<meta>",
|
||||
"<meta>",
|
||||
"<tag>",
|
||||
"<system value=\"scheme1\"/>",
|
||||
|
@ -828,7 +825,6 @@ public class Dstu3XmlParserTest {
|
|||
"<display value=\"label2\"/>",
|
||||
"</tag>",
|
||||
"</meta>",
|
||||
"</meta>",
|
||||
"<name>",
|
||||
"<family value=\"FAMILY\"/>",
|
||||
"</name>",
|
||||
|
|
10
pom.xml
10
pom.xml
|
@ -76,14 +76,14 @@
|
|||
<scope>test</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest-core</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>java-hamcrest</artifactId>
|
||||
<artifactId>hamcrest</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
@ -1278,11 +1278,17 @@
|
|||
<artifactId>jscience</artifactId>
|
||||
<version>4.3.1</version>
|
||||
</dependency>
|
||||
<!-- TODO: remove this once we're fully switched over to hamcrest 2.2 -->
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>java-hamcrest</artifactId>
|
||||
<version>2.0.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>hamcrest</artifactId>
|
||||
<version>2.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-core</artifactId>
|
||||
|
|
Loading…
Reference in New Issue