Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Simon Janic 2017-11-26 14:23:33 +01:00
commit 8427fc4547
146 changed files with 5279 additions and 3712 deletions

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-standalone-overlay-example</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -27,7 +27,7 @@ public class JaxRsConformanceProvider extends AbstractJaxRsConformanceProvider {
private JaxRsPatientRestProvider provider;
public JaxRsConformanceProvider() {
super("My Server Version", "My Server Description", "My Server Name");
super("My Server Description", "My Server Name", "My Server Version");
}
@Override

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -59,6 +59,13 @@
<groupId>com.phloc</groupId>
<artifactId>phloc-commons</artifactId>
<optional>true</optional>
<exclusions>
<exclusion>
<!-- Guava brings in a newer replacement for this dependency called com.google.code.findbugs:jsr305 -->
<groupId>com.google.code.findbugs</groupId>
<artifactId>annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- <dependency> <groupId>xerces</groupId> <artifactId>xercesImpl</artifactId>
@ -77,6 +84,10 @@
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<!-- Logging -->
<dependency>

View File

@ -1127,7 +1127,7 @@ public abstract class BaseParser implements IParser {
}
}
return true;
return false;
}
public BaseRuntimeChildDefinition getDef() {

View File

@ -1450,7 +1450,7 @@ public class JsonParser extends BaseParser implements IJsonLikeParser {
}
BaseRuntimeElementDefinition<?> childDef = extDef.getChildElementDefinitionByDatatype(value.getClass());
if (childDef == null) {
throw new ConfigurationException("Unable to encode extension, unregognized child element type: " + value.getClass().getCanonicalName());
throw new ConfigurationException("Unable to encode extension, unrecognized child element type: " + value.getClass().getCanonicalName());
}
encodeChildElementToStreamWriter(theResDef, theResource, theEventWriter, value, childDef, childName, true, false, myParent, false);
managePrimitiveExtension(value, theResDef, theResource, theEventWriter, childDef, childName);

View File

@ -138,19 +138,6 @@ public class ParameterUtil {
return b.toString();
}
/**
* 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>
*/
public static String escapeAndUrlEncode(String theValue) {
if (theValue == null) {
return null;
}
String escaped = escape(theValue);
return UrlUtil.escape(escaped);
}
/**
* 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>

View File

@ -47,6 +47,10 @@ public class InvalidRequestException extends BaseServerResponseException {
public InvalidRequestException(String theMessage) {
super(STATUS_CODE, theMessage);
}
public InvalidRequestException(Throwable theCause) {
super(STATUS_CODE, theCause);
}
/**
* Constructor

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.util;
/*-
* #%L
* HAPI FHIR - Core Library
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
import ca.uhn.fhir.context.FhirContext;

View File

@ -1,16 +1,20 @@
package ca.uhn.fhir.util;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.isBlank;
import java.io.UnsupportedEncodingException;
import java.net.*;
import java.util.*;
import java.util.Map.Entry;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import com.google.common.escape.Escaper;
import com.google.common.net.PercentEscaper;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.Map.Entry;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.isBlank;
/*
* #%L
@ -35,6 +39,10 @@ import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
public class UrlUtil {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(UrlUtil.class);
private static final String URL_FORM_PARAMETER_OTHER_SAFE_CHARS = "-_.*";
private static final Escaper PARAMETER_ESCAPER = new PercentEscaper(URL_FORM_PARAMETER_OTHER_SAFE_CHARS, false);
/**
* Resolve a relative URL - THIS METHOD WILL NOT FAIL but will log a warning and return theEndpoint if the input is invalid.
*/
@ -88,19 +96,24 @@ public class UrlUtil {
}
/**
* URL encode a value
* URL encode a value according to RFC 3986
* <p>
* This method is intended to be applied to an individual parameter
* name or value. For example, if you are creating the URL
* <code>http://example.com/fhir/Patient?key=føø</code>
* it would be appropriate to pass the string "føø" to this method,
* but not appropriate to pass the entire URL since characters
* such as "/" and "?" would also be escaped.
* </P>
*/
public static String escape(String theValue) {
if (theValue == null) {
public static String escapeUrlParam(String theUnescaped) {
if (theUnescaped == null) {
return null;
}
try {
return URLEncoder.encode(theValue, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new Error("UTF-8 not supported on this platform");
}
return PARAMETER_ESCAPER.escape(theUnescaped);
}
public static boolean isAbsolute(String theValue) {
String value = theValue.toLowerCase();
return value.startsWith("http://") || value.startsWith("https://");
@ -147,7 +160,7 @@ public class UrlUtil {
}
public static void main(String[] args) {
System.out.println(escape("http://snomed.info/sct?fhir_vs=isa/126851005"));
System.out.println(escapeUrlParam("http://snomed.info/sct?fhir_vs=isa/126851005"));
}
public static Map<String, String[]> parseQueryString(String theQueryString) {
@ -156,36 +169,20 @@ public class UrlUtil {
return toQueryStringMap(map);
}
public static Map<String, String[]> parseQueryStrings(String... theQueryString) {
HashMap<String, List<String>> map = new HashMap<String, List<String>>();
for (String next : theQueryString) {
parseQueryString(next, map);
}
return toQueryStringMap(map);
}
private static Map<String, String[]> toQueryStringMap(HashMap<String, List<String>> map) {
HashMap<String, String[]> retVal = new HashMap<String, String[]>();
for (Entry<String, List<String>> nextEntry : map.entrySet()) {
retVal.put(nextEntry.getKey(), nextEntry.getValue().toArray(new String[nextEntry.getValue().size()]));
}
return retVal;
}
private static void parseQueryString(String theQueryString, HashMap<String, List<String>> map) {
String query = theQueryString;
if (query.startsWith("?")) {
query = query.substring(1);
}
StringTokenizer tok = new StringTokenizer(query, "&");
while (tok.hasMoreTokens()) {
String nextToken = tok.nextToken();
if (isBlank(nextToken)) {
continue;
}
int equalsIndex = nextToken.indexOf('=');
String nextValue;
String nextKey;
@ -196,21 +193,28 @@ public class UrlUtil {
nextKey = nextToken.substring(0, equalsIndex);
nextValue = nextToken.substring(equalsIndex + 1);
}
nextKey = unescape(nextKey);
nextValue = unescape(nextValue);
List<String> list = map.get(nextKey);
if (list == null) {
list = new ArrayList<String>();
list = new ArrayList<>();
map.put(nextKey, list);
}
list.add(nextValue);
}
}
//@formatter:off
/**
public static Map<String, String[]> parseQueryStrings(String... theQueryString) {
HashMap<String, List<String>> map = new HashMap<String, List<String>>();
for (String next : theQueryString) {
parseQueryString(next, map);
}
return toQueryStringMap(map);
}
/**
* Parse a URL in one of the following forms:
* <ul>
* <li>[Resource Type]?[Search Params]
@ -278,6 +282,16 @@ public class UrlUtil {
}
//@formatter:off
private static Map<String, String[]> toQueryStringMap(HashMap<String, List<String>> map) {
HashMap<String, String[]> retVal = new HashMap<String, String[]>();
for (Entry<String, List<String>> nextEntry : map.entrySet()) {
retVal.put(nextEntry.getKey(), nextEntry.getValue().toArray(new String[nextEntry.getValue().size()]));
}
return retVal;
}
public static String unescape(String theString) {
if (theString == null) {
return null;
@ -305,30 +319,30 @@ public class UrlUtil {
return myParams;
}
public String getResourceId() {
return myResourceId;
}
public String getResourceType() {
return myResourceType;
}
public String getVersionId() {
return myVersionId;
}
public void setParams(String theParams) {
myParams = theParams;
}
public String getResourceId() {
return myResourceId;
}
public void setResourceId(String theResourceId) {
myResourceId = theResourceId;
}
public String getResourceType() {
return myResourceType;
}
public void setResourceType(String theResourceType) {
myResourceType = theResourceType;
}
public String getVersionId() {
return myVersionId;
}
public void setVersionId(String theVersionId) {
myVersionId = theVersionId;
}

View File

@ -1,20 +1,42 @@
package ca.uhn.fhir.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import static org.junit.Assert.*;
public class UrlUtilTest {
@Test
public void testConstructAbsoluteUrl() {
assertEquals("http://foo/bar/baz", UrlUtil.constructAbsoluteUrl(null, "http://foo/bar/baz"));
assertEquals("http://foo/bar/baz", UrlUtil.constructAbsoluteUrl("http://foo/bar/", "baz"));
assertEquals("http://foo/bar/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/", "baz/"));
assertEquals("http://foo/bar/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/", "./baz/"));
assertEquals("http://foo/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/", "../baz/"));
assertEquals("http://foo/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/", "/baz/"));
}
@Test
public void testConstructRelativeUrl() {
assertEquals("http://foo/bar/baz", UrlUtil.constructRelativeUrl("http://boo/far/faz", "http://foo/bar/baz"));
assertEquals("http://foo/bar/baz", UrlUtil.constructRelativeUrl("http://foo/far/faz", "http://foo/bar/baz"));
assertEquals("baz", UrlUtil.constructRelativeUrl("http://foo/bar/boo", "http://foo/bar/baz"));
}
@Test
public void testEscape() {
assertEquals("A%20B", UrlUtil.escapeUrlParam("A B"));
assertEquals("A%2BB", UrlUtil.escapeUrlParam("A+B"));
}
@Test
public void testIsValid() {
assertTrue(UrlUtil.isValid("http://foo"));
assertTrue(UrlUtil.isValid("https://foo"));
assertTrue(UrlUtil.isValid("HTTP://Foo"));
assertTrue(UrlUtil.isValid("HTTPS://Foo"));
assertFalse(UrlUtil.isValid("file://foo"));
assertFalse(UrlUtil.isValid("://foo"));
assertFalse(UrlUtil.isValid("http:/ss"));
@ -24,7 +46,7 @@ public class UrlUtilTest {
assertFalse(UrlUtil.isValid(""));
assertFalse(UrlUtil.isValid(null));
}
@Test
public void testParseUrl() {
assertEquals("ConceptMap", UrlUtil.parseUrl("http://hl7.org/fhir/ConceptMap/ussgfht-loincde").getResourceType());
@ -36,25 +58,5 @@ public class UrlUtilTest {
assertEquals("a=b", UrlUtil.parseUrl("ConceptMap/ussgfht-loincde?a=b").getParams());
}
@Test
public void testConstructAbsoluteUrl() {
assertEquals("http://foo/bar/baz", UrlUtil.constructAbsoluteUrl(null, "http://foo/bar/baz"));
assertEquals("http://foo/bar/baz", UrlUtil.constructAbsoluteUrl("http://foo/bar/","baz"));
assertEquals("http://foo/bar/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/","baz/"));
assertEquals("http://foo/bar/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/","./baz/"));
assertEquals("http://foo/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/","../baz/"));
assertEquals("http://foo/baz/", UrlUtil.constructAbsoluteUrl("http://foo/bar/","/baz/"));
}
@Test
public void testConstructRelativeUrl() {
assertEquals("http://foo/bar/baz", UrlUtil.constructRelativeUrl("http://boo/far/faz", "http://foo/bar/baz"));
assertEquals("http://foo/bar/baz", UrlUtil.constructRelativeUrl("http://foo/far/faz", "http://foo/bar/baz"));
assertEquals("baz", UrlUtil.constructRelativeUrl("http://foo/bar/boo", "http://foo/bar/baz"));
}
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-cli</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -20,13 +20,6 @@ package ca.uhn.fhir.rest.client.impl;
* #L%
*/
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
@ -34,6 +27,12 @@ import ca.uhn.fhir.rest.client.api.Header;
import ca.uhn.fhir.rest.client.api.IHttpClient;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IRestfulClientFactory;
import ca.uhn.fhir.util.UrlUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
public abstract class BaseHttpClientInvocation {
@ -115,13 +114,9 @@ public abstract class BaseHttpClientInvocation {
} else {
theUrlBuilder.append('&');
}
try {
theUrlBuilder.append(URLEncoder.encode(next.getKey(), "UTF-8"));
theUrlBuilder.append('=');
theUrlBuilder.append(URLEncoder.encode(nextValue, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new Error("UTF-8 not supported - This should not happen");
}
theUrlBuilder.append(UrlUtil.escapeUrlParam(next.getKey()));
theUrlBuilder.append('=');
theUrlBuilder.append(UrlUtil.escapeUrlParam(nextValue));
}
}
}

View File

@ -354,7 +354,7 @@ public class GenericClient extends BaseClient implements IGenericClient {
case '?':
case '$':
case ':':
b.append(UrlUtil.escape(Character.toString(nextChar)));
b.append(UrlUtil.escapeUrlParam(Character.toString(nextChar)));
break;
default:
b.append(nextChar);

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.rest.client.interceptor;
/*-
* #%L
* HAPI FHIR - Client Framework
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;

View File

@ -20,21 +20,18 @@ package ca.uhn.fhir.rest.client.method;
* #L%
*/
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.commons.lang3.StringUtils;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.RequestTypeEnum;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.impl.BaseHttpClientInvocation;
import ca.uhn.fhir.util.UrlUtil;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* @author James Agnew
@ -51,51 +48,40 @@ public class HttpGetClientInvocation extends BaseHttpClientInvocation {
myUrlPath = StringUtils.join(theUrlFragments, '/');
}
public HttpGetClientInvocation(FhirContext theContext, Map<String, List<String>> theParameters, List<String> theUrlFragments) {
super(theContext);
myParameters = theParameters;
myUrlPath = StringUtils.join(theUrlFragments, '/');
}
public HttpGetClientInvocation(FhirContext theContext, String theUrlPath) {
super(theContext);
myParameters = new HashMap<String, List<String>>();
myParameters = new HashMap<>();
myUrlPath = theUrlPath;
}
public HttpGetClientInvocation(FhirContext theContext, String... theUrlFragments) {
super(theContext);
myParameters = new HashMap<String, List<String>>();
myUrlPath = StringUtils.join(theUrlFragments, '/');
}
private boolean addQueryParameter(StringBuilder b, boolean first, String nextKey, String nextValue) {
boolean retVal = first;
if (retVal) {
b.append('?');
retVal = false;
} else {
b.append('&');
}
b.append(UrlUtil.escapeUrlParam(nextKey));
b.append('=');
b.append(UrlUtil.escapeUrlParam(nextValue));
public HttpGetClientInvocation(FhirContext theContext, List<String> theUrlFragments) {
super(theContext);
myParameters = new HashMap<String, List<String>>();
myUrlPath = StringUtils.join(theUrlFragments, '/');
}
public Map<String, List<String>> getParameters() {
return myParameters;
}
public String getUrlPath() {
return myUrlPath;
return retVal;
}
@Override
public IHttpRequest asHttpRequest(String theUrlBase, Map<String, List<String>> theExtraParams, EncodingEnum theEncoding, Boolean thePrettyPrint) {
StringBuilder b = new StringBuilder();
if (!myUrlPath.contains("://")) {
b.append(theUrlBase);
if (!theUrlBase.endsWith("/") && !myUrlPath.startsWith("/")) {
b.append('/');
}
}
b.append(myUrlPath);
b.append(theUrlBase);
if (!theUrlBase.endsWith("/") && !myUrlPath.startsWith("/")) {
b.append('/');
}
}
b.append(myUrlPath);
boolean first = b.indexOf("?") == -1;
for (Entry<String, List<String>> next : myParameters.entrySet()) {
if (next.getValue() == null || next.getValue().isEmpty()) {
@ -112,22 +98,12 @@ public class HttpGetClientInvocation extends BaseHttpClientInvocation {
return super.createHttpRequest(b.toString(), theEncoding, RequestTypeEnum.GET);
}
private boolean addQueryParameter(StringBuilder b, boolean first, String nextKey, String nextValue) {
boolean retVal = first;
if (retVal) {
b.append('?');
retVal = false;
} else {
b.append('&');
}
try {
b.append(URLEncoder.encode(nextKey, "UTF-8"));
b.append('=');
b.append(URLEncoder.encode(nextValue, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new ConfigurationException("Could not find UTF-8 encoding. This shouldn't happen.", e);
}
return retVal;
public Map<String, List<String>> getParameters() {
return myParameters;
}
public String getUrlPath() {
return myUrlPath;
}
}

View File

@ -209,9 +209,9 @@ public class MethodUtil {
for (String nextValue : nextEntry.getValue()) {
b.append(haveQuestionMark ? '&' : '?');
haveQuestionMark = true;
b.append(UrlUtil.escape(nextEntry.getKey()));
b.append(UrlUtil.escapeUrlParam(nextEntry.getKey()));
b.append('=');
b.append(UrlUtil.escape(nextValue));
b.append(UrlUtil.escapeUrlParam(nextValue));
}
}
return b;

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -26,42 +26,42 @@
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2.1</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<optional>true</optional>
</dependency>
</dependencies>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<artifactId>hapi-deployable-pom</artifactId>
<groupId>ca.uhn.hapi.fhir</groupId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@ -11,7 +11,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>
@ -51,11 +51,26 @@
<artifactId>hapi-fhir-structures-dstu2</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-hl7org-dstu2</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu2.1</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-dstu3</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-structures-r4</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>

View File

@ -66,8 +66,11 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
private RestulfulServerConfiguration serverConfiguration = new RestulfulServerConfiguration();
/** the conformance. It is created once during startup */
private org.hl7.fhir.r4.model.CapabilityStatement myR4CapabilityStatement;
private CapabilityStatement myDstu3CapabilityStatement;
private org.hl7.fhir.dstu2016may.model.Conformance myDstu2_1Conformance;
private ca.uhn.fhir.model.dstu2.resource.Conformance myDstu2Conformance;
private org.hl7.fhir.instance.model.Conformance myDstu2Hl7OrgConformance;
/**
* Constructor allowing the description, servername and server to be set
@ -124,14 +127,26 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
HardcodedServerAddressStrategy hardcodedServerAddressStrategy = new HardcodedServerAddressStrategy();
hardcodedServerAddressStrategy.setValue(getBaseForServer());
serverConfiguration.setServerAddressStrategy(hardcodedServerAddressStrategy);
if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.R4)) {
org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider serverCapabilityStatementProvider = new org.hl7.fhir.r4.hapi.rest.server.ServerCapabilityStatementProvider(serverConfiguration);
serverCapabilityStatementProvider.initializeOperations();
myR4CapabilityStatement = serverCapabilityStatementProvider.getServerConformance(null);
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
ServerCapabilityStatementProvider serverCapabilityStatementProvider = new ServerCapabilityStatementProvider(serverConfiguration);
serverCapabilityStatementProvider.initializeOperations();
myDstu3CapabilityStatement = serverCapabilityStatementProvider.getServerConformance(null);
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2_1)) {
org.hl7.fhir.dstu2016may.hapi.rest.server.ServerConformanceProvider serverCapabilityStatementProvider = new org.hl7.fhir.dstu2016may.hapi.rest.server.ServerConformanceProvider(serverConfiguration);
serverCapabilityStatementProvider.initializeOperations();
myDstu2_1Conformance = serverCapabilityStatementProvider.getServerConformance(null);
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2)) {
ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider serverCapabilityStatementProvider = new ca.uhn.fhir.rest.server.provider.dstu2.ServerConformanceProvider(serverConfiguration);
serverCapabilityStatementProvider.initializeOperations();
myDstu2Conformance = serverCapabilityStatementProvider.getServerConformance(null);
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
org.hl7.fhir.instance.conf.ServerConformanceProvider serverCapabilityStatementProvider = new org.hl7.fhir.instance.conf.ServerConformanceProvider(serverConfiguration);
serverCapabilityStatementProvider.initializeOperations();
myDstu2Hl7OrgConformance = serverCapabilityStatementProvider.getServerConformance(null);
}
}
@ -167,12 +182,19 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
response.addHeader(Constants.HEADER_CORS_ALLOW_ORIGIN, "*");
IBaseResource conformance = null;
if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.R4)) {
conformance = myR4CapabilityStatement;
// return (Response) response.returnResponse(ParseAction.create(myDstu3CapabilityStatement), Constants.STATUS_HTTP_200_OK, true, null, getResourceType().getSimpleName());
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
conformance = myDstu3CapabilityStatement;
// return (Response) response.returnResponse(ParseAction.create(myDstu3CapabilityStatement), Constants.STATUS_HTTP_200_OK, true, null, getResourceType().getSimpleName());
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2_1)) {
conformance = myDstu2_1Conformance;
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2)) {
conformance = myDstu2Conformance;
// return (Response) response.returnResponse(ParseAction.create(myDstu2CapabilityStatement), Constants.STATUS_HTTP_200_OK, true, null, getResourceType().getSimpleName());
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
conformance = myDstu2Hl7OrgConformance;
}
if (conformance != null) {
@ -257,10 +279,16 @@ public abstract class AbstractJaxRsConformanceProvider extends AbstractJaxRsProv
@SuppressWarnings("unchecked")
@Override
public Class<IBaseResource> getResourceType() {
if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.R4)) {
return Class.class.cast(org.hl7.fhir.r4.model.CapabilityStatement.class);
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU3)) {
return Class.class.cast(CapabilityStatement.class);
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2_1)) {
return Class.class.cast(org.hl7.fhir.dstu2016may.model.Conformance.class);
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2)) {
return Class.class.cast(ca.uhn.fhir.model.dstu2.resource.Conformance.class);
} else if (super.getFhirContext().getVersion().getVersion().equals(FhirVersionEnum.DSTU2_HL7ORG)) {
return Class.class.cast(org.hl7.fhir.instance.model.Conformance.class);
}
return null;
}

View File

@ -26,8 +26,6 @@ import java.util.Map.Entry;
import javax.ws.rs.core.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.api.AddProfileTagEnum;
@ -35,12 +33,9 @@ import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsResponseException;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest;
import ca.uhn.fhir.jaxrs.server.util.JaxRsRequest.Builder;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.server.*;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.util.OperationOutcomeUtil;
/**
* This is the abstract superclass for all jaxrs providers. It contains some defaults implementing
@ -57,11 +52,11 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
private final FhirContext CTX;
/** the http headers */
@Context
private HttpHeaders theHeaders;
private HttpHeaders myHeaders;
/** the uri info */
@Context
private UriInfo theUriInfo;
private UriInfo myUriInfo;
/**
* Default is DSTU2. Use {@link AbstractJaxRsProvider#AbstractJaxRsProvider(FhirContext)} to specify a DSTU3 context.
@ -79,13 +74,6 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
CTX = ctx;
}
private IBaseOperationOutcome createOutcome(final DataFormatException theException) {
final IBaseOperationOutcome oo = OperationOutcomeUtil.newInstance(getFhirContext());
final String detailsValue = theException.getMessage() + "\n\n" + ExceptionUtils.getStackTrace(theException);
OperationOutcomeUtil.addIssue(getFhirContext(), oo, ERROR, detailsValue, null, PROCESSING);
return oo;
}
/**
* DEFAULT = AddProfileTagEnum.NEVER
*/
@ -142,7 +130,7 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
* @return the headers
*/
public HttpHeaders getHeaders() {
return this.theHeaders;
return this.myHeaders;
}
/**
@ -203,7 +191,7 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
* @return the requestbuilder
*/
public Builder getRequest(final RequestTypeEnum requestType, final RestOperationTypeEnum restOperation, final String theResourceName) {
return new JaxRsRequest.Builder(this, requestType, restOperation, theUriInfo.getRequestUri().toString(), theResourceName);
return new JaxRsRequest.Builder(this, requestType, restOperation, myUriInfo.getRequestUri().toString(), theResourceName);
}
/**
@ -224,7 +212,7 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
* @return the uri info
*/
public UriInfo getUriInfo() {
return this.theUriInfo;
return this.myUriInfo;
}
/**
@ -241,9 +229,6 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
throws IOException {
if (theException instanceof JaxRsResponseException) {
return new JaxRsExceptionInterceptor().convertExceptionIntoResponse(theRequest, (JaxRsResponseException) theException);
} else if (theException instanceof DataFormatException) {
return new JaxRsExceptionInterceptor().convertExceptionIntoResponse(theRequest, new JaxRsResponseException(
new InvalidRequestException(theException.getMessage(), createOutcome((DataFormatException) theException))));
} else {
return new JaxRsExceptionInterceptor().convertExceptionIntoResponse(theRequest,
new JaxRsExceptionInterceptor().convertException(this, theException));
@ -273,7 +258,7 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
* the headers to set
*/
public void setHeaders(final HttpHeaders headers) {
this.theHeaders = headers;
this.myHeaders = headers;
}
/**
@ -283,7 +268,7 @@ public abstract class AbstractJaxRsProvider implements IRestfulServerDefaults {
* the uri info
*/
public void setUriInfo(final UriInfo uriInfo) {
this.theUriInfo = uriInfo;
this.myUriInfo = uriInfo;
}
/**

View File

@ -0,0 +1,109 @@
package ca.uhn.fhir.jaxrs.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.net.URI;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.*;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProviderDstu2Hl7Org;
import org.glassfish.jersey.internal.MapPropertiesDelegate;
import org.glassfish.jersey.server.ContainerRequest;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProviderDstu2Hl7Org;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider;
public class AbstractJaxRsConformanceProviderDstu2Hl7OrgTest {
private static final String BASEURI = "http://basiuri";
private static final String REQUESTURI = BASEURI + "/metadata";
AbstractJaxRsConformanceProvider provider;
private ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> providers;
private ContainerRequest headers;
private MultivaluedHashMap<String, String> queryParameters;
@Before
public void setUp() throws Exception {
// headers
headers = new ContainerRequest(new URI(BASEURI), new URI(REQUESTURI), HttpMethod.GET, null,
new MapPropertiesDelegate());
// uri info
queryParameters = new MultivaluedHashMap<String, String>();
providers = new ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider>();
provider = createConformanceProvider(providers);
}
@Test
public void testConformance() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsDummyPatientProviderDstu2Hl7Org.class, new TestJaxRsDummyPatientProviderDstu2Hl7Org());
Response response = createConformanceProvider(providers).conformance();
System.out.println(response);
}
@Test
public void testConformanceUsingOptions() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsDummyPatientProviderDstu2Hl7Org.class, new TestJaxRsDummyPatientProviderDstu2Hl7Org());
Response response = createConformanceProvider(providers).conformanceUsingOptions();
System.out.println(response);
}
@Test
public void testConformanceWithMethods() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsMockPatientRestProviderDstu2Hl7Org.class, new TestJaxRsMockPatientRestProviderDstu2Hl7Org());
Response response = createConformanceProvider(providers).conformance();
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus());
assertTrue(response.getEntity().toString().contains("\"type\": \"Patient\""));
assertTrue(response.getEntity().toString().contains("someCustomOperation"));
System.out.println(response);
System.out.println(response.getEntity());
}
@Test
public void testConformanceInXml() throws Exception {
queryParameters.put(Constants.PARAM_FORMAT, Arrays.asList(Constants.CT_XML));
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsMockPatientRestProviderDstu2Hl7Org.class, new TestJaxRsMockPatientRestProviderDstu2Hl7Org());
Response response = createConformanceProvider(providers).conformance();
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus());
System.out.println(response.getEntity());
assertTrue(response.getEntity().toString().contains(" <type value=\"Patient\"/>"));
assertTrue(response.getEntity().toString().contains("someCustomOperation"));
System.out.println(response.getEntity());
}
private AbstractJaxRsConformanceProvider createConformanceProvider(final ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> providers)
throws Exception {
AbstractJaxRsConformanceProvider result = new AbstractJaxRsConformanceProvider(FhirContext.forDstu2Hl7Org(), null, null, null) {
@Override
protected ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders() {
return providers;
}
};
// mocks
UriInfo uriInfo = mock(UriInfo.class);
when(uriInfo.getQueryParameters()).thenReturn(queryParameters);
when(uriInfo.getBaseUri()).thenReturn(new URI(BASEURI));
when(uriInfo.getRequestUri()).thenReturn(new URI(BASEURI + "/foo"));
result.setUriInfo(uriInfo);
result.setHeaders(headers);
result.setUpPostConstruct();
return result;
}
}

View File

@ -0,0 +1,108 @@
package ca.uhn.fhir.jaxrs.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.net.URI;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.*;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProviderDstu2_1;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProviderDstu2_1;
import org.glassfish.jersey.internal.MapPropertiesDelegate;
import org.glassfish.jersey.server.ContainerRequest;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider;
public class AbstractJaxRsConformanceProviderDstu2_1Test {
private static final String BASEURI = "http://basiuri";
private static final String REQUESTURI = BASEURI + "/metadata";
AbstractJaxRsConformanceProvider provider;
private ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> providers;
private ContainerRequest headers;
private MultivaluedHashMap<String, String> queryParameters;
@Before
public void setUp() throws Exception {
// headers
headers = new ContainerRequest(new URI(BASEURI), new URI(REQUESTURI), HttpMethod.GET, null,
new MapPropertiesDelegate());
// uri info
queryParameters = new MultivaluedHashMap<String, String>();
providers = new ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider>();
provider = createConformanceProvider(providers);
}
@Test
public void testConformance() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsDummyPatientProviderDstu2_1.class, new TestJaxRsDummyPatientProviderDstu2_1());
Response response = createConformanceProvider(providers).conformance();
System.out.println(response);
}
@Test
public void testConformanceUsingOptions() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsDummyPatientProviderDstu2_1.class, new TestJaxRsDummyPatientProviderDstu2_1());
Response response = createConformanceProvider(providers).conformanceUsingOptions();
System.out.println(response);
}
@Test
public void testConformanceWithMethods() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsMockPatientRestProviderDstu2_1.class, new TestJaxRsMockPatientRestProviderDstu2_1());
Response response = createConformanceProvider(providers).conformance();
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus());
assertTrue(response.getEntity().toString().contains("\"type\": \"Patient\""));
assertTrue(response.getEntity().toString().contains("\"someCustomOperation"));
System.out.println(response);
System.out.println(response.getEntity());
}
@Test
public void testConformanceInXml() throws Exception {
queryParameters.put(Constants.PARAM_FORMAT, Arrays.asList(Constants.CT_XML));
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsMockPatientRestProviderDstu2_1.class, new TestJaxRsMockPatientRestProviderDstu2_1());
Response response = createConformanceProvider(providers).conformance();
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus());
System.out.println(response.getEntity());
assertTrue(response.getEntity().toString().contains(" <type value=\"Patient\"/>"));
assertTrue(response.getEntity().toString().contains("\"someCustomOperation"));
System.out.println(response.getEntity());
}
private AbstractJaxRsConformanceProvider createConformanceProvider(final ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> providers)
throws Exception {
AbstractJaxRsConformanceProvider result = new AbstractJaxRsConformanceProvider(FhirContext.forDstu2_1(), null, null, null) {
@Override
protected ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders() {
return providers;
}
};
// mocks
UriInfo uriInfo = mock(UriInfo.class);
when(uriInfo.getQueryParameters()).thenReturn(queryParameters);
when(uriInfo.getBaseUri()).thenReturn(new URI(BASEURI));
when(uriInfo.getRequestUri()).thenReturn(new URI(BASEURI + "/foo"));
result.setUriInfo(uriInfo);
result.setHeaders(headers);
result.setUpPostConstruct();
return result;
}
}

View File

@ -0,0 +1,108 @@
package ca.uhn.fhir.jaxrs.server;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.net.URI;
import java.util.Arrays;
import java.util.concurrent.ConcurrentHashMap;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.core.*;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsDummyPatientProviderR4;
import ca.uhn.fhir.jaxrs.server.test.TestJaxRsMockPatientRestProviderR4;
import org.glassfish.jersey.internal.MapPropertiesDelegate;
import org.glassfish.jersey.server.ContainerRequest;
import org.junit.Before;
import org.junit.Test;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.IResourceProvider;
public class AbstractJaxRsConformanceProviderR4Test {
private static final String BASEURI = "http://basiuri";
private static final String REQUESTURI = BASEURI + "/metadata";
AbstractJaxRsConformanceProvider provider;
private ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> providers;
private ContainerRequest headers;
private MultivaluedHashMap<String, String> queryParameters;
@Before
public void setUp() throws Exception {
// headers
headers = new ContainerRequest(new URI(BASEURI), new URI(REQUESTURI), HttpMethod.GET, null,
new MapPropertiesDelegate());
// uri info
queryParameters = new MultivaluedHashMap<String, String>();
providers = new ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider>();
provider = createConformanceProvider(providers);
}
@Test
public void testConformance() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsDummyPatientProviderR4.class, new TestJaxRsDummyPatientProviderR4());
Response response = createConformanceProvider(providers).conformance();
System.out.println(response);
}
@Test
public void testConformanceUsingOptions() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsDummyPatientProviderR4.class, new TestJaxRsDummyPatientProviderR4());
Response response = createConformanceProvider(providers).conformanceUsingOptions();
System.out.println(response);
}
@Test
public void testConformanceWithMethods() throws Exception {
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsMockPatientRestProviderR4.class, new TestJaxRsMockPatientRestProviderR4());
Response response = createConformanceProvider(providers).conformance();
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus());
assertTrue(response.getEntity().toString().contains("\"type\": \"Patient\""));
assertTrue(response.getEntity().toString().contains("\"someCustomOperation"));
System.out.println(response);
System.out.println(response.getEntity());
}
@Test
public void testConformanceInXml() throws Exception {
queryParameters.put(Constants.PARAM_FORMAT, Arrays.asList(Constants.CT_XML));
providers.put(AbstractJaxRsConformanceProvider.class, provider);
providers.put(TestJaxRsMockPatientRestProviderR4.class, new TestJaxRsMockPatientRestProviderR4());
Response response = createConformanceProvider(providers).conformance();
assertEquals(Constants.STATUS_HTTP_200_OK, response.getStatus());
System.out.println(response.getEntity());
assertTrue(response.getEntity().toString().contains(" <type value=\"Patient\"/>"));
assertTrue(response.getEntity().toString().contains("\"someCustomOperation"));
System.out.println(response.getEntity());
}
private AbstractJaxRsConformanceProvider createConformanceProvider(final ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> providers)
throws Exception {
AbstractJaxRsConformanceProvider result = new AbstractJaxRsConformanceProvider(FhirContext.forR4(), null, null, null) {
@Override
protected ConcurrentHashMap<Class<? extends IResourceProvider>, IResourceProvider> getProviders() {
return providers;
}
};
// mocks
UriInfo uriInfo = mock(UriInfo.class);
when(uriInfo.getQueryParameters()).thenReturn(queryParameters);
when(uriInfo.getBaseUri()).thenReturn(new URI(BASEURI));
when(uriInfo.getRequestUri()).thenReturn(new URI(BASEURI + "/foo"));
result.setUriInfo(uriInfo);
result.setHeaders(headers);
result.setUpPostConstruct();
return result;
}
}

View File

@ -6,6 +6,7 @@ import static org.mockito.Mockito.*;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import javax.ws.rs.core.*;
@ -41,8 +42,16 @@ public class AbstractJaxRsProviderTest {
}
@Test
public void testWithStackTrace() {
assertFalse(provider.withStackTrace());
public void testHandleExceptionDataFormatException() throws IOException, URISyntaxException {
final DataFormatException theException = new DataFormatException();
UriInfo uriInfo = mock(UriInfo.class);
when(uriInfo.getRequestUri()).thenReturn(new URI("http://example.com"));
when(uriInfo.getBaseUri()).thenReturn(new URI("http://example.com"));
when(uriInfo.getQueryParameters()).thenReturn(new MultivaluedHashMap<String, String>());
provider.setUriInfo(uriInfo);
final Response result = provider.handleException(theRequest, theException);
assertNotNull(result);
assertEquals(Constants.STATUS_HTTP_400_BAD_REQUEST, result.getStatus());
}
@Test
@ -54,14 +63,6 @@ public class AbstractJaxRsProviderTest {
assertEquals(base.getStatusCode(), result.getStatus());
}
@Test
public void testHandleExceptionDataFormatException() throws IOException {
final DataFormatException theException = new DataFormatException();
final Response result = provider.handleException(theRequest, theException);
assertNotNull(result);
assertEquals(Constants.STATUS_HTTP_400_BAD_REQUEST, result.getStatus());
}
@Test
public void testHandleExceptionRuntimeException() throws IOException, URISyntaxException {
final RuntimeException theException = new RuntimeException();
@ -76,4 +77,9 @@ public class AbstractJaxRsProviderTest {
assertNotNull(result);
assertEquals(Constants.STATUS_HTTP_500_INTERNAL_ERROR, result.getStatus());
}
@Test
public void testWithStackTrace() {
assertFalse(provider.withStackTrace());
}
}

View File

@ -0,0 +1,15 @@
package ca.uhn.fhir.jaxrs.server.test;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import org.hl7.fhir.instance.model.Patient;
/**
* A dummy patient provider exposing no methods
*/
public class TestJaxRsDummyPatientProviderDstu2Hl7Org extends AbstractJaxRsResourceProvider<Patient> {
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
}

View File

@ -0,0 +1,15 @@
package ca.uhn.fhir.jaxrs.server.test;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import org.hl7.fhir.dstu2016may.model.Patient;
/**
* A dummy patient provider exposing no methods
*/
public class TestJaxRsDummyPatientProviderDstu2_1 extends AbstractJaxRsResourceProvider<Patient> {
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
}

View File

@ -0,0 +1,15 @@
package ca.uhn.fhir.jaxrs.server.test;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import org.hl7.fhir.r4.model.Patient;
/**
* A dummy patient provider exposing no methods
*/
public class TestJaxRsDummyPatientProviderR4 extends AbstractJaxRsResourceProvider<Patient> {
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
}

View File

@ -0,0 +1,128 @@
package ca.uhn.fhir.jaxrs.server.test;
import java.util.List;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.hl7.fhir.instance.model.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.mockito.Mockito;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IPagingProvider;
/**
* A test server delegating each call to a mock
*/
@Path(TestJaxRsMockPatientRestProviderDstu2Hl7Org.PATH)
@Stateless
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML, Constants.CT_FHIR_JSON_NEW, Constants.CT_FHIR_XML_NEW })
@Interceptors(JaxRsExceptionInterceptor.class)
public class TestJaxRsMockPatientRestProviderDstu2Hl7Org extends AbstractJaxRsResourceProvider<Patient> {
static final String PATH = "/Patient";
public static final TestJaxRsMockPatientRestProviderDstu2Hl7Org mock = Mockito.mock(TestJaxRsMockPatientRestProviderDstu2Hl7Org.class);
public static final FifoMemoryPagingProvider PAGING_PROVIDER;
static
{
PAGING_PROVIDER = new FifoMemoryPagingProvider(10);
PAGING_PROVIDER.setDefaultPageSize(10);
PAGING_PROVIDER.setMaximumPageSize(100);
}
/**
* Constructor
*/
public TestJaxRsMockPatientRestProviderDstu2Hl7Org() {
super(FhirContext.forDstu2Hl7Org());
}
@Search
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name, @RequiredParam(name=Patient.SP_ADDRESS) StringAndListParam theAddressParts) {
return mock.search(name, theAddressParts);
}
@Update
public MethodOutcome update(@IdParam final IdType theId, @ResourceParam final Patient patient,@ConditionalUrlParam final String theConditional) throws Exception {
return mock.update(theId, patient, theConditional);
}
@Read
public Patient find(@IdParam final IdType theId) {
return mock.find(theId);
}
@Read(version = true)
public Patient findHistory(@IdParam final IdType theId) {
return mock.findHistory(theId);
}
@Create
public MethodOutcome create(@ResourceParam final Patient patient, @ConditionalUrlParam String theConditional)
throws Exception {
return mock.create(patient, theConditional);
}
@Delete
public MethodOutcome delete(@IdParam final IdType theId, @ConditionalUrlParam final String theConditional) {
return mock.delete(theId, theConditional);
}
@Search(compartmentName = "Condition")
public List<IBaseResource> searchCompartment(@IdParam IdType thePatientId) {
return mock.searchCompartment(thePatientId);
}
@GET
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingGet(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.GET, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@POST
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingPost(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.POST, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@Operation(name = "someCustomOperation", idempotent = true, returnParameters = {
@OperationParam(name = "return", type = StringType.class) })
public Parameters someCustomOperation(@IdParam IdType myId, @OperationParam(name = "dummy") StringType dummyInput) {
return mock.someCustomOperation(myId, dummyInput);
}
@Validate()
public MethodOutcome validate(@ResourceParam final Patient resource) {
MethodOutcome mO = new MethodOutcome();
mO.setOperationOutcome(new OperationOutcome());
return mO;
}
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Override
public IPagingProvider getPagingProvider() {
return PAGING_PROVIDER;
}
}

View File

@ -0,0 +1,128 @@
package ca.uhn.fhir.jaxrs.server.test;
import java.util.List;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.hl7.fhir.dstu2016may.model.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.mockito.Mockito;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IPagingProvider;
/**
* A test server delegating each call to a mock
*/
@Path(TestJaxRsMockPatientRestProviderDstu2_1.PATH)
@Stateless
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML, Constants.CT_FHIR_JSON_NEW, Constants.CT_FHIR_XML_NEW })
@Interceptors(JaxRsExceptionInterceptor.class)
public class TestJaxRsMockPatientRestProviderDstu2_1 extends AbstractJaxRsResourceProvider<Patient> {
static final String PATH = "/Patient";
public static final TestJaxRsMockPatientRestProviderDstu2_1 mock = Mockito.mock(TestJaxRsMockPatientRestProviderDstu2_1.class);
public static final FifoMemoryPagingProvider PAGING_PROVIDER;
static
{
PAGING_PROVIDER = new FifoMemoryPagingProvider(10);
PAGING_PROVIDER.setDefaultPageSize(10);
PAGING_PROVIDER.setMaximumPageSize(100);
}
/**
* Constructor
*/
public TestJaxRsMockPatientRestProviderDstu2_1() {
super(FhirContext.forDstu2_1());
}
@Search
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name, @RequiredParam(name=Patient.SP_ADDRESS) StringAndListParam theAddressParts) {
return mock.search(name, theAddressParts);
}
@Update
public MethodOutcome update(@IdParam final IdType theId, @ResourceParam final Patient patient,@ConditionalUrlParam final String theConditional) throws Exception {
return mock.update(theId, patient, theConditional);
}
@Read
public Patient find(@IdParam final IdType theId) {
return mock.find(theId);
}
@Read(version = true)
public Patient findHistory(@IdParam final IdType theId) {
return mock.findHistory(theId);
}
@Create
public MethodOutcome create(@ResourceParam final Patient patient, @ConditionalUrlParam String theConditional)
throws Exception {
return mock.create(patient, theConditional);
}
@Delete
public MethodOutcome delete(@IdParam final IdType theId, @ConditionalUrlParam final String theConditional) {
return mock.delete(theId, theConditional);
}
@Search(compartmentName = "Condition")
public List<IBaseResource> searchCompartment(@IdParam IdType thePatientId) {
return mock.searchCompartment(thePatientId);
}
@GET
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingGet(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.GET, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@POST
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingPost(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.POST, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@Operation(name = "someCustomOperation", idempotent = true, returnParameters = {
@OperationParam(name = "return", type = StringType.class) })
public Parameters someCustomOperation(@IdParam IdType myId, @OperationParam(name = "dummy") StringType dummyInput) {
return mock.someCustomOperation(myId, dummyInput);
}
@Validate()
public MethodOutcome validate(@ResourceParam final Patient resource) {
MethodOutcome mO = new MethodOutcome();
mO.setOperationOutcome(new OperationOutcome());
return mO;
}
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Override
public IPagingProvider getPagingProvider() {
return PAGING_PROVIDER;
}
}

View File

@ -0,0 +1,128 @@
package ca.uhn.fhir.jaxrs.server.test;
import java.util.List;
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.mockito.Mockito;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsResourceProvider;
import ca.uhn.fhir.jaxrs.server.interceptor.JaxRsExceptionInterceptor;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.param.StringAndListParam;
import ca.uhn.fhir.rest.param.StringParam;
import ca.uhn.fhir.rest.server.FifoMemoryPagingProvider;
import ca.uhn.fhir.rest.server.IPagingProvider;
/**
* A test server delegating each call to a mock
*/
@Path(TestJaxRsMockPatientRestProviderR4.PATH)
@Stateless
@Produces({ MediaType.APPLICATION_JSON, Constants.CT_FHIR_JSON, Constants.CT_FHIR_XML, Constants.CT_FHIR_JSON_NEW, Constants.CT_FHIR_XML_NEW })
@Interceptors(JaxRsExceptionInterceptor.class)
public class TestJaxRsMockPatientRestProviderR4 extends AbstractJaxRsResourceProvider<Patient> {
static final String PATH = "/Patient";
public static final TestJaxRsMockPatientRestProviderR4 mock = Mockito.mock(TestJaxRsMockPatientRestProviderR4.class);
public static final FifoMemoryPagingProvider PAGING_PROVIDER;
static
{
PAGING_PROVIDER = new FifoMemoryPagingProvider(10);
PAGING_PROVIDER.setDefaultPageSize(10);
PAGING_PROVIDER.setMaximumPageSize(100);
}
/**
* Constructor
*/
public TestJaxRsMockPatientRestProviderR4() {
super(FhirContext.forR4());
}
@Search
public List<Patient> search(@RequiredParam(name = Patient.SP_NAME) final StringParam name, @RequiredParam(name=Patient.SP_ADDRESS) StringAndListParam theAddressParts) {
return mock.search(name, theAddressParts);
}
@Update
public MethodOutcome update(@IdParam final IdType theId, @ResourceParam final Patient patient,@ConditionalUrlParam final String theConditional) throws Exception {
return mock.update(theId, patient, theConditional);
}
@Read
public Patient find(@IdParam final IdType theId) {
return mock.find(theId);
}
@Read(version = true)
public Patient findHistory(@IdParam final IdType theId) {
return mock.findHistory(theId);
}
@Create
public MethodOutcome create(@ResourceParam final Patient patient, @ConditionalUrlParam String theConditional)
throws Exception {
return mock.create(patient, theConditional);
}
@Delete
public MethodOutcome delete(@IdParam final IdType theId, @ConditionalUrlParam final String theConditional) {
return mock.delete(theId, theConditional);
}
@Search(compartmentName = "Condition")
public List<IBaseResource> searchCompartment(@IdParam IdType thePatientId) {
return mock.searchCompartment(thePatientId);
}
@GET
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingGet(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.GET, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@POST
@Path("/{id}/$someCustomOperation")
public Response someCustomOperationUsingPost(@PathParam("id") String id, String resource) throws Exception {
return customOperation(resource, RequestTypeEnum.POST, id, "$someCustomOperation",
RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE);
}
@Operation(name = "someCustomOperation", idempotent = true, returnParameters = {
@OperationParam(name = "return", type = StringType.class) })
public Parameters someCustomOperation(@IdParam IdType myId, @OperationParam(name = "dummy") StringType dummyInput) {
return mock.someCustomOperation(myId, dummyInput);
}
@Validate()
public MethodOutcome validate(@ResourceParam final Patient resource) {
MethodOutcome mO = new MethodOutcome();
mO.setOperationOutcome(new OperationOutcome());
return mO;
}
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Override
public IPagingProvider getPagingProvider() {
return PAGING_PROVIDER;
}
}

View File

@ -6,7 +6,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -32,7 +32,7 @@ public class JaxRsConformanceProvider extends AbstractJaxRsConformanceProvider {
* Standard Constructor
*/
public JaxRsConformanceProvider() {
super(SERVER_VERSION, SERVER_DESCRIPTION, SERVER_NAME);
super(SERVER_DESCRIPTION, SERVER_NAME, SERVER_VERSION);
}
@Override

View File

@ -33,7 +33,7 @@ public class JaxRsConformanceProviderDstu3 extends AbstractJaxRsConformanceProvi
* Standard Constructor
*/
public JaxRsConformanceProviderDstu3() {
super(FhirContext.forDstu3(), SERVER_VERSION, SERVER_DESCRIPTION, SERVER_NAME);
super(FhirContext.forDstu3(), SERVER_DESCRIPTION, SERVER_NAME, SERVER_VERSION);
}
@Override

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -35,7 +35,6 @@ import ca.uhn.fhir.model.base.composite.BaseResourceReferenceDt;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.primitive.StringDt;
import ca.uhn.fhir.model.primitive.XhtmlDt;
import ca.uhn.fhir.model.valueset.BundleEntryTransactionMethodEnum;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.parser.IParser;
@ -229,14 +228,14 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
ArrayList<String> nextChoicesList = new ArrayList<>();
partsChoices.add(nextChoicesList);
String key = UrlUtil.escape(nextCompositeOf.getName());
String key = UrlUtil.escapeUrlParam(nextCompositeOf.getName());
if (paramsListForCompositePart != null) {
for (BaseResourceIndexedSearchParam nextParam : paramsListForCompositePart) {
if (nextParam.getParamName().equals(nextCompositeOf.getName())) {
IQueryParameterType nextParamAsClientParam = nextParam.toQueryParameterType();
String value = nextParamAsClientParam.getValueAsQueryToken(getContext());
if (isNotBlank(value)) {
value = UrlUtil.escape(value);
value = UrlUtil.escapeUrlParam(value);
nextChoicesList.add(key + "=" + value);
}
}
@ -246,7 +245,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
for (ResourceLink nextLink : linksForCompositePart) {
String value = nextLink.getTargetResource().getIdDt().toUnqualifiedVersionless().getValue();
if (isNotBlank(value)) {
value = UrlUtil.escape(value);
value = UrlUtil.escapeUrlParam(value);
nextChoicesList.add(key + "=" + value);
}
}
@ -2015,7 +2014,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
/*
* The following block of code is used to strip out diacritical marks from latin script
* and also convert to upper case. E.g. "jåmes" becomes "JAMES".
* and also convert to upper case. E.g. "j?mes" becomes "JAMES".
*
* See http://www.unicode.org/charts/PDF/U0300.pdf for the logic
* behind stripping 0300-036F

View File

@ -256,6 +256,7 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
ResourceTable entity = myEntityManager.find(ResourceTable.class, pid);
deletedResources.add(entity);
T resourceToDelete = toResource(myResourceType, entity, false);
validateOkToDelete(deleteConflicts, entity);
// Notify interceptors
@ -268,9 +269,9 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
// Perform delete
Date updateTime = new Date();
updateEntity(null, entity, updateTime, updateTime);
resourceToDelete.setId(entity.getIdDt());
// Notify JPA interceptors
T resourceToDelete = toResource(myResourceType, entity, false);
if (theRequestDetails != null) {
theRequestDetails.getRequestOperationCallback().resourceDeleted(resourceToDelete);
ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, idToDelete.getResourceType(), idToDelete);

View File

@ -60,7 +60,6 @@ import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.query.Query;
import org.hl7.fhir.dstu3.model.BaseResource;
import org.hl7.fhir.dstu3.model.Reference;
import org.hl7.fhir.instance.model.api.IAnyResource;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
@ -175,7 +174,7 @@ public class SearchBuilder implements ISearchBuilder {
if (valueBuilder.length() > 0) {
valueBuilder.append(',');
}
valueBuilder.append(UrlUtil.escape(next.getValueAsQueryToken(myContext)));
valueBuilder.append(UrlUtil.escapeUrlParam(next.getValueAsQueryToken(myContext)));
targetResourceType = next.getTargetResourceType();
owningParameter = next.getOwningFieldName();
parameterName = next.getParameterName();
@ -185,7 +184,7 @@ public class SearchBuilder implements ISearchBuilder {
continue;
}
String matchUrl = targetResourceType + '?' + UrlUtil.escape(parameterName) + '=' + valueBuilder.toString();
String matchUrl = targetResourceType + '?' + UrlUtil.escapeUrlParam(parameterName) + '=' + valueBuilder.toString();
RuntimeResourceDefinition targetResourceDefinition;
try {
targetResourceDefinition = myContext.getResourceDefinition(targetResourceType);
@ -1272,13 +1271,13 @@ public class SearchBuilder implements ISearchBuilder {
List<List<String>> params = new ArrayList<>();
for (Entry<String, List<List<? extends IQueryParameterType>>> nextParamNameToValues : theParams.entrySet()) {
String nextParamName = nextParamNameToValues.getKey();
nextParamName = UrlUtil.escape(nextParamName);
nextParamName = UrlUtil.escapeUrlParam(nextParamName);
for (List<? extends IQueryParameterType> nextAnd : nextParamNameToValues.getValue()) {
ArrayList<String> nextValueList = new ArrayList<>();
params.add(nextValueList);
for (IQueryParameterType nextOr : nextAnd) {
String nextOrValue = nextOr.getValueAsQueryToken(myContext);
nextOrValue = UrlUtil.escape(nextOrValue);
nextOrValue = UrlUtil.escapeUrlParam(nextOrValue);
nextValueList.add(nextParamName + "=" + nextOrValue);
}
}
@ -2000,6 +1999,9 @@ public class SearchBuilder implements ISearchBuilder {
}
private static List<Long> filterResourceIdsByLastUpdated(EntityManager theEntityManager, final DateRangeParam theLastUpdated, Collection<Long> thePids) {
if (thePids.isEmpty()) {
return Collections.emptyList();
}
CriteriaBuilder builder = theEntityManager.getCriteriaBuilder();
CriteriaQuery<Long> cq = builder.createQuery(Long.class);
Root<ResourceTable> from = cq.from(ResourceTable.class);

View File

@ -1,6 +1,5 @@
package ca.uhn.fhir.jpa.dao;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
/*
@ -24,7 +23,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
*/
import java.util.*;
import ca.uhn.fhir.rest.param.ReferenceParam;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
@ -138,9 +136,9 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
addUrlParamSeparator(b);
b.append(paramName);
b.append('=');
b.append(UrlUtil.escape(nextInclude.getParamType()));
b.append(UrlUtil.escapeUrlParam(nextInclude.getParamType()));
b.append(':');
b.append(UrlUtil.escape(nextInclude.getParamName()));
b.append(UrlUtil.escapeUrlParam(nextInclude.getParamName()));
if (isNotBlank(nextInclude.getParamTargetType())) {
b.append(':');
b.append(nextInclude.getParamTargetType());
@ -314,7 +312,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
for (List<IQueryParameterType> nextValuesAnd : nextValuesAndsOut) {
addUrlParamSeparator(b);
IQueryParameterType firstValue = nextValuesAnd.get(0);
b.append(UrlUtil.escape(nextKey));
b.append(UrlUtil.escapeUrlParam(nextKey));
if (firstValue.getMissing() != null) {
b.append(Constants.PARAMQUALIFIER_MISSING);
@ -340,7 +338,7 @@ public class SearchParameterMap extends LinkedHashMap<String, List<List<? extend
}
String valueAsQueryToken = nextValueOr.getValueAsQueryToken(theCtx);
// b.append(ParameterUtil.escapeAndUrlEncode(valueAsQueryToken));
b.append(UrlUtil.escape(valueAsQueryToken));
b.append(UrlUtil.escapeUrlParam(valueAsQueryToken));
}
}

View File

@ -676,7 +676,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao<Bundle, Meta> {
String nextReplacementIdPart = nextReplacementId.getValueAsString();
if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) {
matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart);
matchUrl = matchUrl.replace(UrlUtil.escape(nextTemporaryIdPart), nextReplacementIdPart);
matchUrl = matchUrl.replace(UrlUtil.escapeUrlParam(nextTemporaryIdPart), nextReplacementIdPart);
}
}
}

View File

@ -667,7 +667,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao<Bundle, Meta> {
String nextReplacementIdPart = nextReplacementId.getValueAsString();
if (nextTemporaryId.isUrn() && nextTemporaryIdPart.length() > IdType.URN_PREFIX.length()) {
matchUrl = matchUrl.replace(nextTemporaryIdPart, nextReplacementIdPart);
matchUrl = matchUrl.replace(UrlUtil.escape(nextTemporaryIdPart), nextReplacementIdPart);
matchUrl = matchUrl.replace(UrlUtil.escapeUrlParam(nextTemporaryIdPart), nextReplacementIdPart);
}
}
}

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.jpa.search;
/*-
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.apache.lucene.analysis.core.LowerCaseFilterFactory;
import org.apache.lucene.analysis.core.StopFilterFactory;
import org.apache.lucene.analysis.core.WhitespaceTokenizerFactory;

View File

@ -593,6 +593,9 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
shouldSync = true;
}
// If no abort was requested, bail out
Validate.isTrue(myAbortRequested == false, "Abort has been requested");
if (shouldSync) {
saveUnsynced(theResultIterator);
}
@ -605,10 +608,11 @@ public class SearchCoordinatorSvcImpl implements ISearchCoordinatorSvc {
}
}
// Check if an abort got requested
Validate.isTrue(myAbortRequested == false, "Abort has been requested");
}
// If no abort was requested, bail out
Validate.isTrue(myAbortRequested == false, "Abort has been requested");
saveUnsynced(theResultIterator);
}

View File

@ -321,7 +321,7 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvc implements I
cs.setUrl(theSystem);
cs.setContent(CodeSystemContentMode.NOTPRESENT);
DaoMethodOutcome createOutcome = myCodeSystemResourceDao.create(cs, "CodeSystem?url=" + UrlUtil.escape(theSystem), theRequestDetails);
DaoMethodOutcome createOutcome = myCodeSystemResourceDao.create(cs, "CodeSystem?url=" + UrlUtil.escapeUrlParam(theSystem), theRequestDetails);
IIdType csId = createOutcome.getId().toUnqualifiedVersionless();
if (createOutcome.getCreated() != Boolean.TRUE) {
CodeSystem existing = myCodeSystemResourceDao.read(csId, theRequestDetails);

View File

@ -325,7 +325,7 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvc implements IVal
cs.setUrl(theSystem);
cs.setContent(CodeSystemContentMode.NOTPRESENT);
DaoMethodOutcome createOutcome = myCodeSystemResourceDao.create(cs, "CodeSystem?url=" + UrlUtil.escape(theSystem), theRequestDetails);
DaoMethodOutcome createOutcome = myCodeSystemResourceDao.create(cs, "CodeSystem?url=" + UrlUtil.escapeUrlParam(theSystem), theRequestDetails);
IIdType csId = createOutcome.getId().toUnqualifiedVersionless();
if (createOutcome.getCreated() != Boolean.TRUE) {
CodeSystem existing = myCodeSystemResourceDao.read(csId, theRequestDetails);

View File

@ -30,7 +30,7 @@ public class TestDstu2Config extends BaseJavaConfigDstu2 {
public DataSource dataSource() {
BasicDataSource retVal = new BasicDataSource();
retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver());
retVal.setUrl("jdbc:derby:memory:myUnitTestDB;create=true");
retVal.setUrl("jdbc:derby:memory:myUnitTestDBDstu2;create=true");
retVal.setUsername("");
retVal.setPassword("");
return retVal;

View File

@ -90,7 +90,7 @@ public class TestDstu3Config extends BaseJavaConfigDstu3 {
};
retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver());
retVal.setUrl("jdbc:derby:memory:myUnitTestDB;create=true");
retVal.setUrl("jdbc:derby:memory:myUnitTestDBDstu3;create=true");
retVal.setMaxWaitMillis(10000);
retVal.setUsername("");
retVal.setPassword("");

View File

@ -85,7 +85,7 @@ public class TestR4Config extends BaseJavaConfigR4 {
};
retVal.setDriver(new org.apache.derby.jdbc.EmbeddedDriver());
retVal.setUrl("jdbc:derby:memory:myUnitTestDB;create=true");
retVal.setUrl("jdbc:derby:memory:myUnitTestDBR4;create=true");
retVal.setMaxWaitMillis(10000);
retVal.setUsername("");
retVal.setPassword("");

View File

@ -1,38 +1,12 @@
package ca.uhn.fhir.jpa.dao;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.Callable;
import javax.persistence.EntityManager;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.model.dstu2.resource.Observation;
import org.apache.commons.io.IOUtils;
import org.hibernate.search.jpa.Search;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.instance.model.api.*;
import org.junit.AfterClass;
import org.junit.Before;
import org.mockito.Mockito;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test;
import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.model.dstu2.resource.Bundle;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
@ -41,6 +15,31 @@ import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import ca.uhn.fhir.util.BundleUtil;
import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.io.IOUtils;
import org.hibernate.search.jpa.Search;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Before;
import org.mockito.Mockito;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import javax.persistence.EntityManager;
import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.*;
import java.util.concurrent.Callable;
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public abstract class BaseJpaTest {
@ -53,13 +52,23 @@ public abstract class BaseJpaTest {
public void beforeCreateSrd() {
mySrd = mock(ServletRequestDetails.class, Mockito.RETURNS_DEEP_STUBS);
when(mySrd.getRequestOperationCallback()).thenReturn(myRequestOperationCallback);
myServerInterceptorList = new ArrayList<IServerInterceptor>();
myServerInterceptorList = new ArrayList<>();
when(mySrd.getServer().getInterceptors()).thenReturn(myServerInterceptorList);
when(mySrd.getUserData()).thenReturn(new HashMap<Object, Object>());
when(mySrd.getUserData()).thenReturn(new HashMap<>());
}
protected abstract FhirContext getContext();
/**
* Sleep until at least 1 ms has elapsed
*/
public void sleepUntilTimeChanges() throws InterruptedException {
StopWatch sw = new StopWatch();
while (sw.getMillis() == 0) {
Thread.sleep(10);
}
}
protected org.hl7.fhir.dstu3.model.Bundle toBundle(IBundleProvider theSearch) {
org.hl7.fhir.dstu3.model.Bundle bundle = new org.hl7.fhir.dstu3.model.Bundle();
for (IBaseResource next : theSearch.getResources(0, theSearch.size())) {
@ -76,11 +85,11 @@ public abstract class BaseJpaTest {
return bundle;
}
@SuppressWarnings({ "rawtypes" })
@SuppressWarnings({"rawtypes"})
protected List toList(IBundleProvider theSearch) {
return theSearch.getResources(0, theSearch.size());
}
protected List<String> toUnqualifiedIdValues(IBaseBundle theFound) {
List<String> retVal = new ArrayList<String>();
@ -214,9 +223,9 @@ public abstract class BaseJpaTest {
}
public static void purgeDatabase(final EntityManager entityManager, PlatformTransactionManager theTxManager, ISearchParamPresenceSvc theSearchParamPresenceSvc, ISearchCoordinatorSvc theSearchCoordinatorSvc, ISearchParamRegistry theSearchParamRegistry) {
theSearchCoordinatorSvc.cancelAllActiveSearches();
TransactionTemplate txTemplate = new TransactionTemplate(theTxManager);
txTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
txTemplate.execute(new TransactionCallback<Void>() {

View File

@ -461,7 +461,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture());
ActionRequestDetails details = detailsCapt.getValue();
assertNotNull(details.getId());
assertNull(details.getId());
assertEquals("Patient", details.getResourceType());
assertEquals(Patient.class, details.getResource().getClass());
@ -2346,7 +2346,7 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
}
@Test
public void testSortByLastUpdated() {
public void testSortByLastUpdated() throws InterruptedException {
String methodName = "testSortByLastUpdated";
Patient p = new Patient();
@ -2354,21 +2354,29 @@ public class FhirResourceDaoDstu2Test extends BaseJpaDstu2Test {
p.addName().addFamily(methodName);
IIdType id1 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
sleepUntilTimeChanges();
p = new Patient();
p.addIdentifier().setSystem("urn:system2").setValue(methodName);
p.addName().addFamily(methodName);
IIdType id2 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
sleepUntilTimeChanges();
p = new Patient();
p.addIdentifier().setSystem("urn:system3").setValue(methodName);
p.addName().addFamily(methodName);
IIdType id3 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
sleepUntilTimeChanges();
p = new Patient();
p.addIdentifier().setSystem("urn:system4").setValue(methodName);
p.addName().addFamily(methodName);
IIdType id4 = myPatientDao.create(p, mySrd).getId().toUnqualifiedVersionless();
sleepUntilTimeChanges();
SearchParameterMap pm;
List<IIdType> actual;

View File

@ -573,7 +573,7 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest {
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture());
details = detailsCapt.getValue();
assertNotNull(details.getId());
assertNull(details.getId());
assertEquals("Patient", details.getResourceType());
assertEquals(Patient.class, details.getResource().getClass());

View File

@ -1,14 +1,17 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import static ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.*;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.entity.Search;
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
import ca.uhn.fhir.jpa.util.StopWatch;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.util.AopTestUtils;
@ -16,10 +19,11 @@ import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.jpa.search.StaleSearchDeletingSvcImpl;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.StringParam;
import java.util.concurrent.atomic.AtomicLong;
import static ca.uhn.fhir.jpa.util.TestUtil.sleepAtLeast;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.*;
public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoDstu3SearchPageExpiryTest.class);
@ -104,14 +108,17 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
// Search just got used so it shouldn't be deleted
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
final AtomicLong search3timestamp = new AtomicLong();
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
assertNotNull(mySearchEntityDao.findByUuid(searchUuid3));
Search search3 = mySearchEntityDao.findByUuid(searchUuid3);
assertNotNull(search3);
search3timestamp.set(search3.getCreated().getTime());
}
});
StaleSearchDeletingSvcImpl.setNowForUnitTests(start + 1400);
StaleSearchDeletingSvcImpl.setNowForUnitTests(search3timestamp.get() + 1400);
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
@ -127,14 +134,14 @@ public class FhirResourceDaoDstu3SearchPageExpiryTest extends BaseJpaDstu3Test {
}
});
StaleSearchDeletingSvcImpl.setNowForUnitTests(start + 2200);
StaleSearchDeletingSvcImpl.setNowForUnitTests(search3timestamp.get() + 2200);
myStaleSearchDeletingSvc.pollForStaleSearchesAndDeleteThem();
newTxTemplate().execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theArg0) {
assertNull(mySearchEntityDao.findByUuid(searchUuid1));
assertNull(mySearchEntityDao.findByUuid(searchUuid3));
assertNull("Search 1 still exists", mySearchEntityDao.findByUuid(searchUuid1));
assertNull("Search 3 still exists", mySearchEntityDao.findByUuid(searchUuid3));
}
});

View File

@ -688,7 +688,7 @@ public class FhirResourceDaoDstu3Test extends BaseJpaDstu3Test {
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture());
ActionRequestDetails details = detailsCapt.getValue();
assertNotNull(details.getId());
assertNull(details.getId());
assertEquals("Patient", details.getResourceType());
assertEquals(Patient.class, details.getResource().getClass());

View File

@ -939,7 +939,7 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest {
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture());
details = detailsCapt.getValue();
assertNotNull(details.getId());
assertNull(details.getId());
assertEquals("Patient", details.getResourceType());
assertEquals(Patient.class, details.getResource().getClass());

View File

@ -730,7 +730,7 @@ public class FhirResourceDaoR4Test extends BaseJpaR4Test {
ArgumentCaptor<ActionRequestDetails> detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture());
ActionRequestDetails details = detailsCapt.getValue();
assertNotNull(details.getId());
assertNull(details.getId());
assertEquals("Patient", details.getResourceType());
assertEquals(Patient.class, details.getResource().getClass());

View File

@ -1018,7 +1018,7 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest {
detailsCapt = ArgumentCaptor.forClass(ActionRequestDetails.class);
verify(myInterceptor).incomingRequestPreHandled(eq(RestOperationTypeEnum.CREATE), detailsCapt.capture());
details = detailsCapt.getValue();
assertNotNull(details.getId());
assertNull(details.getId());
assertEquals("Patient", details.getResourceType());
assertEquals(Patient.class, details.getResource().getClass());

View File

@ -30,7 +30,6 @@ import ca.uhn.fhir.model.dstu2.resource.*;
import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry;
import ca.uhn.fhir.model.dstu2.valueset.*;
import ca.uhn.fhir.model.primitive.*;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.client.api.IGenericClient;
@ -528,7 +527,7 @@ public class ResourceProviderDstu2Test extends BaseResourceProviderDstu2Test {
pt.addName().addFamily("FOO");
resource = myFhirCtx.newXmlParser().encodeResourceToString(pt);
HttpPut put = new HttpPut(ourServerBase + "/Patient?identifier=" + ("http://general-hospital.co.uk/Identifiers|09832345234543876876".replace("|", UrlUtil.escape("|"))));
HttpPut put = new HttpPut(ourServerBase + "/Patient?identifier=" + ("http://general-hospital.co.uk/Identifiers|09832345234543876876".replace("|", UrlUtil.escapeUrlParam("|"))));
put.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
IdDt id2;

View File

@ -1,39 +1,39 @@
package ca.uhn.fhir.jpa.provider.dstu3;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor;
import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule;
import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum;
import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder;
import ca.uhn.fhir.util.TestUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Observation;
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
import org.hl7.fhir.dstu3.model.Patient;
import org.hl7.fhir.dstu3.model.Reference;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Observation.ObservationStatus;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.auth.*;
import ca.uhn.fhir.util.TestUtil;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.*;
public class AuthorizationInterceptorResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Override
public void before() throws Exception {
super.before();
@ -41,92 +41,66 @@ public class AuthorizationInterceptorResourceProviderDstu3Test extends BaseResou
unregisterInterceptors();
}
private void unregisterInterceptors() {
for (IServerInterceptor next : new ArrayList<IServerInterceptor>(ourRestServer.getInterceptors())) {
if (next instanceof AuthorizationInterceptor) {
ourRestServer.unregisterInterceptor(next);
}
}
}
/**
* See #503
* See #667
*/
@Test
public void testDeleteIsBlocked() {
public void testBlockUpdatingPatientUserDoesnNotHaveAccessTo() throws IOException {
Patient pt1 = new Patient();
pt1.setActive(true);
final IIdType pid1 = ourClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless();
Patient pt2 = new Patient();
pt2.setActive(false);
final IIdType pid2 = ourClient.create().resource(pt2).execute().getId().toUnqualifiedVersionless();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.deny().delete().allResources().withAnyId().andThen()
.allowAll()
.build();
.allow().write().allResources().inCompartment("Patient", pid1).andThen()
.build();
}
});
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
IIdType id = ourClient.create().resource(patient).execute().getId();
try {
ourClient.delete().resourceById(id.toUnqualifiedVersionless()).execute();
fail();
} catch (ForbiddenOperationException e) {
// good
}
patient = ourClient.read().resource(Patient.class).withId(id.toUnqualifiedVersionless()).execute();
assertEquals(id.getValue(), patient.getId());
}
Observation obs = new Observation();
obs.setStatus(ObservationStatus.FINAL);
obs.setSubject(new Reference(pid1));
IIdType oid = ourClient.create().resource(obs).execute().getId().toUnqualified();
/**
* See #503
*/
@Test
public void testDeleteIsAllowedForCompartment() {
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
final IIdType id = ourClient.create().resource(patient).execute().getId();
Observation obsInCompartment = new Observation();
obsInCompartment.setStatus(ObservationStatus.FINAL);
obsInCompartment.getSubject().setReferenceElement(id.toUnqualifiedVersionless());
IIdType obsInCompartmentId = ourClient.create().resource(obsInCompartment).execute().getId().toUnqualifiedVersionless();
Observation obsNotInCompartment = new Observation();
obsNotInCompartment.setStatus(ObservationStatus.FINAL);
IIdType obsNotInCompartmentId = ourClient.create().resource(obsNotInCompartment).execute().getId().toUnqualifiedVersionless();
unregisterInterceptors();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().delete().resourcesOfType(Observation.class).inCompartment("Patient", id).andThen()
.deny().delete().allResources().withAnyId().andThen()
.allowAll()
.build();
.allow().write().allResources().inCompartment("Patient", pid2).andThen()
.build();
}
});
ourClient.delete().resourceById(obsInCompartmentId.toUnqualifiedVersionless()).execute();
/*
* Try to update to a new patient. The user has access to write to things in
* pid2's compartment, so this would normally be ok, but in this case they are overwriting
* a resource that is already in pid1's compartment, which shouldn't be allowed.
*/
obs = new Observation();
obs.setId(oid);
obs.setStatus(ObservationStatus.FINAL);
obs.setSubject(new Reference(pid2));
try {
ourClient.delete().resourceById(obsNotInCompartmentId.toUnqualifiedVersionless()).execute();
ourClient.update().resource(obs).execute();
fail();
} catch (ForbiddenOperationException e) {
// good
}
}
@Test
public void testCreateConditional() {
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
@ -135,15 +109,13 @@ public class AuthorizationInterceptorResourceProviderDstu3Test extends BaseResou
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
//@formatter:off
return new RuleBuilder()
.allow("Rule 2").write().allResources().inCompartment("Patient", new IdDt("Patient/" + output1.getId().getIdPart())).andThen()
.allow().updateConditional().allResources()
.build();
//@formatter:on
}
});
patient = new Patient();
patient.setId(output1.getId().toUnqualifiedVersionless());
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
@ -151,7 +123,7 @@ public class AuthorizationInterceptorResourceProviderDstu3Test extends BaseResou
MethodOutcome output2 = ourClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute();
assertEquals(output1.getId().getIdPart(), output2.getId().getIdPart());
patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
@ -176,62 +148,82 @@ public class AuthorizationInterceptorResourceProviderDstu3Test extends BaseResou
}
/**
* See #667
* See #503 #751
*/
@Test
public void testBlockUpdatingPatientUserDoesnNotHaveAccessTo() throws IOException {
Patient pt1 = new Patient();
pt1.setActive(true);
final IIdType pid1 = ourClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless();
public void testDeleteIsAllowedForCompartment() {
Patient pt2 = new Patient();
pt2.setActive(false);
final IIdType pid2 = ourClient.create().resource(pt2).execute().getId().toUnqualifiedVersionless();
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
final IIdType id = ourClient.create().resource(patient).execute().getId();
Observation obsInCompartment = new Observation();
obsInCompartment.setStatus(ObservationStatus.FINAL);
obsInCompartment.getSubject().setReferenceElement(id.toUnqualifiedVersionless());
IIdType obsInCompartmentId = ourClient.create().resource(obsInCompartment).execute().getId().toUnqualifiedVersionless();
// create a 2nd observation to be deleted by url Observation?patient=id
ourClient.create().resource(obsInCompartment).execute().getId().toUnqualifiedVersionless();
Observation obsNotInCompartment = new Observation();
obsNotInCompartment.setStatus(ObservationStatus.FINAL);
IIdType obsNotInCompartmentId = ourClient.create().resource(obsNotInCompartment).execute().getId().toUnqualifiedVersionless();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().write().allResources().inCompartment("Patient", pid1).andThen()
.allow().delete().resourcesOfType(Observation.class).inCompartment("Patient", id).andThen()
.deny().delete().allResources().withAnyId().andThen()
.allowAll()
.build();
}
});
Observation obs = new Observation();
obs.setStatus(ObservationStatus.FINAL);
obs.setSubject(new Reference(pid1));
IIdType oid = ourClient.create().resource(obs).execute().getId().toUnqualified();
unregisterInterceptors();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().write().allResources().inCompartment("Patient", pid2).andThen()
.build();
}
});
/*
* Try to update to a new patient. The user has access to write to things in
* pid2's compartment, so this would normally be ok, but in this case they are overwriting
* a resource that is already in pid1's compartment, which shouldn't be allowed.
*/
obs = new Observation();
obs.setId(oid);
obs.setStatus(ObservationStatus.FINAL);
obs.setSubject(new Reference(pid2));
ourClient.delete().resourceById(obsInCompartmentId.toUnqualifiedVersionless()).execute();
ourClient.delete().resourceConditionalByUrl("Observation?patient=" + id.toUnqualifiedVersionless()).execute();
try {
ourClient.update().resource(obs).execute();
ourClient.delete().resourceById(obsNotInCompartmentId.toUnqualifiedVersionless()).execute();
fail();
} catch (ForbiddenOperationException e) {
// good
}
}
/**
* See #503
*/
@Test
public void testDeleteIsBlocked() {
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.deny().delete().allResources().withAnyId().andThen()
.allowAll()
.build();
}
});
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
IIdType id = ourClient.create().resource(patient).execute().getId();
try {
ourClient.delete().resourceById(id.toUnqualifiedVersionless()).execute();
fail();
} catch (ForbiddenOperationException e) {
// good
}
patient = ourClient.read().resource(Patient.class).withId(id.toUnqualifiedVersionless()).execute();
assertEquals(id.getValue(), patient.getId());
}
@Test
public void testDeleteResourceConditional() throws IOException {
String methodName = "testDeleteResourceConditional";
@ -280,7 +272,7 @@ public class AuthorizationInterceptorResourceProviderDstu3Test extends BaseResou
//@formatter:on
}
});
HttpDelete delete = new HttpDelete(ourServerBase + "/Patient?name=" + methodName);
response = ourHttpClient.execute(delete);
try {
@ -299,4 +291,17 @@ public class AuthorizationInterceptorResourceProviderDstu3Test extends BaseResou
}
private void unregisterInterceptors() {
for (IServerInterceptor next : new ArrayList<IServerInterceptor>(ourRestServer.getInterceptors())) {
if (next instanceof AuthorizationInterceptor) {
ourRestServer.unregisterInterceptor(next);
}
}
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -1639,7 +1639,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
myObservationDao.create(obs, mySrd);
}
String uri = ourServerBase + "/Patient?_has:Observation:subject:identifier=" + UrlUtil.escape("urn:system|FOO");
String uri = ourServerBase + "/Patient?_has:Observation:subject:identifier=" + UrlUtil.escapeUrlParam("urn:system|FOO");
List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
assertThat(ids, contains(pid0.getValue()));
}
@ -2347,7 +2347,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
myPatientDao.create(p, mySrd);
}
String uri = ourServerBase + "/Patient?name=" + URLEncoder.encode("Jernelöv", "UTF-8") + "&_count=5&_pretty=true";
String uri = ourServerBase + "/Patient?name=" + UrlUtil.escapeUrlParam("Jernelöv") + "&_count=5&_pretty=true";
ourLog.info("URI: {}", uri);
HttpGet get = new HttpGet(uri);
CloseableHttpResponse resp = ourHttpClient.execute(get);
@ -3792,7 +3792,7 @@ public class ResourceProviderDstu3Test extends BaseResourceProviderDstu3Test {
pt.addName().setFamily("FOO");
resource = myFhirCtx.newXmlParser().encodeResourceToString(pt);
HttpPut put = new HttpPut(ourServerBase + "/Patient?identifier=" + ("http://general-hospital.co.uk/Identifiers|09832345234543876876".replace("|", UrlUtil.escape("|"))));
HttpPut put = new HttpPut(ourServerBase + "/Patient?identifier=" + ("http://general-hospital.co.uk/Identifiers|09832345234543876876".replace("|", UrlUtil.escapeUrlParam("|"))));
put.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
IdType id2;

View File

@ -1,41 +1,37 @@
package ca.uhn.fhir.jpa.provider.r4;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.*;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor;
import ca.uhn.fhir.rest.server.interceptor.auth.IAuthRule;
import ca.uhn.fhir.rest.server.interceptor.auth.PolicyEnum;
import ca.uhn.fhir.rest.server.interceptor.auth.RuleBuilder;
import ca.uhn.fhir.util.TestUtil;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.junit.AfterClass;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import ca.uhn.fhir.rest.server.exceptions.PreconditionFailedException;
import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Observation.ObservationStatus;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Test;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.auth.*;
import ca.uhn.fhir.util.TestUtil;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.*;
public class AuthorizationInterceptorResourceProviderR4Test extends BaseResourceProviderR4Test {
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Override
public void before() throws Exception {
super.before();
@ -43,45 +39,112 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
unregisterInterceptors();
}
private void unregisterInterceptors() {
for (IServerInterceptor next : new ArrayList<IServerInterceptor>(ourRestServer.getInterceptors())) {
if (next instanceof AuthorizationInterceptor) {
ourRestServer.unregisterInterceptor(next);
}
}
}
/**
* See #503
* See #667
*/
@Test
public void testDeleteIsBlocked() {
public void testBlockUpdatingPatientUserDoesnNotHaveAccessTo() throws IOException {
Patient pt1 = new Patient();
pt1.setActive(true);
final IIdType pid1 = myClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless();
Patient pt2 = new Patient();
pt2.setActive(false);
final IIdType pid2 = myClient.create().resource(pt2).execute().getId().toUnqualifiedVersionless();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.deny().delete().allResources().withAnyId().andThen()
.allowAll()
.build();
.allow().write().allResources().inCompartment("Patient", pid1).andThen()
.build();
}
});
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
IIdType id = myClient.create().resource(patient).execute().getId();
Observation obs = new Observation();
obs.setStatus(ObservationStatus.FINAL);
obs.setSubject(new Reference(pid1));
IIdType oid = myClient.create().resource(obs).execute().getId().toUnqualified();
unregisterInterceptors();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().write().allResources().inCompartment("Patient", pid2).andThen()
.build();
}
});
/*
* Try to update to a new patient. The user has access to write to things in
* pid2's compartment, so this would normally be ok, but in this case they are overwriting
* a resource that is already in pid1's compartment, which shouldn't be allowed.
*/
obs = new Observation();
obs.setId(oid);
obs.setStatus(ObservationStatus.FINAL);
obs.setSubject(new Reference(pid2));
try {
myClient.delete().resourceById(id.toUnqualifiedVersionless()).execute();
myClient.update().resource(obs).execute();
fail();
} catch (ForbiddenOperationException e) {
// good
}
patient = myClient.read().resource(Patient.class).withId(id.toUnqualifiedVersionless()).execute();
assertEquals(id.getValue(), patient.getId());
}
@Test
public void testCreateConditional() {
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
final MethodOutcome output1 = myClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
//@formatter:off
return new RuleBuilder()
.allow("Rule 2").write().allResources().inCompartment("Patient", new IdDt("Patient/" + output1.getId().getIdPart())).andThen()
.allow().updateConditional().allResources()
.build();
//@formatter:on
}
});
patient = new Patient();
patient.setId(output1.getId().toUnqualifiedVersionless());
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
MethodOutcome output2 = myClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute();
assertEquals(output1.getId().getIdPart(), output2.getId().getIdPart());
patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
try {
myClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|101").execute();
fail();
} catch (ForbiddenOperationException e) {
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());
}
patient = new Patient();
patient.setId("999");
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
try {
myClient.update().resource(patient).execute();
fail();
} catch (ForbiddenOperationException e) {
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());
}
}
/**
@ -139,32 +202,32 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
*/
@Test
public void testDeleteIsAllowedForCompartment() {
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
final IIdType id = myClient.create().resource(patient).execute().getId();
Observation obsInCompartment = new Observation();
obsInCompartment.setStatus(ObservationStatus.FINAL);
obsInCompartment.getSubject().setReferenceElement(id.toUnqualifiedVersionless());
IIdType obsInCompartmentId = myClient.create().resource(obsInCompartment).execute().getId().toUnqualifiedVersionless();
Observation obsNotInCompartment = new Observation();
obsNotInCompartment.setStatus(ObservationStatus.FINAL);
IIdType obsNotInCompartmentId = myClient.create().resource(obsNotInCompartment).execute().getId().toUnqualifiedVersionless();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().delete().resourcesOfType(Observation.class).inCompartment("Patient", id).andThen()
.deny().delete().allResources().withAnyId().andThen()
.allowAll()
.build();
.allow().delete().resourcesOfType(Observation.class).inCompartment("Patient", id).andThen()
.deny().delete().allResources().withAnyId().andThen()
.allowAll()
.build();
}
});
myClient.delete().resourceById(obsInCompartmentId.toUnqualifiedVersionless()).execute();
try {
@ -175,114 +238,38 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
}
}
/**
* See #503
*/
@Test
public void testCreateConditional() {
public void testDeleteIsBlocked() {
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.deny().delete().allResources().withAnyId().andThen()
.allowAll()
.build();
}
});
Patient patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
final MethodOutcome output1 = myClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute();
IIdType id = myClient.create().resource(patient).execute().getId();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
//@formatter:off
return new RuleBuilder()
.allow("Rule 2").write().allResources().inCompartment("Patient", new IdDt("Patient/" + output1.getId().getIdPart())).andThen()
.allow().updateConditional().allResources()
.build();
//@formatter:on
}
});
patient = new Patient();
patient.setId(output1.getId().toUnqualifiedVersionless());
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
MethodOutcome output2 = myClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|100").execute();
assertEquals(output1.getId().getIdPart(), output2.getId().getIdPart());
patient = new Patient();
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
try {
myClient.update().resource(patient).conditionalByUrl("Patient?identifier=http://uhn.ca/mrns|101").execute();
fail();
} catch (ForbiddenOperationException e) {
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());
}
patient = new Patient();
patient.setId("999");
patient.addIdentifier().setSystem("http://uhn.ca/mrns").setValue("100");
patient.addName().setFamily("Tester").addGiven("Raghad");
try {
myClient.update().resource(patient).execute();
fail();
} catch (ForbiddenOperationException e) {
assertEquals("HTTP 403 Forbidden: Access denied by default policy (no applicable rules)", e.getMessage());
}
}
/**
* See #667
*/
@Test
public void testBlockUpdatingPatientUserDoesnNotHaveAccessTo() throws IOException {
Patient pt1 = new Patient();
pt1.setActive(true);
final IIdType pid1 = myClient.create().resource(pt1).execute().getId().toUnqualifiedVersionless();
Patient pt2 = new Patient();
pt2.setActive(false);
final IIdType pid2 = myClient.create().resource(pt2).execute().getId().toUnqualifiedVersionless();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().write().allResources().inCompartment("Patient", pid1).andThen()
.build();
}
});
Observation obs = new Observation();
obs.setStatus(ObservationStatus.FINAL);
obs.setSubject(new Reference(pid1));
IIdType oid = myClient.create().resource(obs).execute().getId().toUnqualified();
unregisterInterceptors();
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow().write().allResources().inCompartment("Patient", pid2).andThen()
.build();
}
});
/*
* Try to update to a new patient. The user has access to write to things in
* pid2's compartment, so this would normally be ok, but in this case they are overwriting
* a resource that is already in pid1's compartment, which shouldn't be allowed.
*/
obs = new Observation();
obs.setId(oid);
obs.setStatus(ObservationStatus.FINAL);
obs.setSubject(new Reference(pid2));
try {
myClient.update().resource(obs).execute();
myClient.delete().resourceById(id.toUnqualifiedVersionless()).execute();
fail();
} catch (ForbiddenOperationException e) {
// good
}
patient = myClient.read().resource(Patient.class).withId(id.toUnqualifiedVersionless()).execute();
assertEquals(id.getValue(), patient.getId());
}
@Test
public void testDeleteResourceConditional() throws IOException {
String methodName = "testDeleteResourceConditional";
@ -331,7 +318,7 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
//@formatter:on
}
});
HttpDelete delete = new HttpDelete(ourServerBase + "/Patient?name=" + methodName);
response = ourHttpClient.execute(delete);
try {
@ -350,4 +337,144 @@ public class AuthorizationInterceptorResourceProviderR4Test extends BaseResource
}
/**
* See #762
*/
@Test
public void testInstanceRuleOkForResourceWithNoId() {
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.deny().write().instance("Patient/123").andThen()
.allowAll()
.build();
}
});
/*
* Create a transaction using linked IDs
*/
Bundle request = new Bundle();
request.setType(Bundle.BundleType.TRANSACTION);
Patient p = new Patient();
p.setActive(true);
p.setId(IdType.newRandomUuid());
request.addEntry().setResource(p).getRequest().setMethod(Bundle.HTTPVerb.POST).setUrl(p.getId());
Observation o = new Observation();
o.getCode().setText("Some Observation");
o.getSubject().setResource(p);
request.addEntry().setResource(o).getRequest().setMethod(Bundle.HTTPVerb.POST);
Bundle resp = myClient.transaction().withBundle(request).execute();
assertEquals(2, resp.getEntry().size());
}
/**
* See #762
*/
@Test
public void testInstanceRuleOkForResourceWithNoId2() {
ourRestServer.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("transactions").transaction().withAnyOperation().andApplyNormalRules().andThen()
.allow("write patient").write().resourcesOfType(Patient.class).withAnyId().andThen()
.allow("write encounter").write().resourcesOfType(Encounter.class).withAnyId().andThen()
.allow("write condition").write().resourcesOfType(Condition.class).withAnyId().andThen()
.denyAll("deny all")
.build();
}
});
// Create a bundle that will be used as a transaction
Bundle bundle = new Bundle();
bundle.setType(Bundle.BundleType.TRANSACTION);
String encounterId = "123-123";
String encounterSystem = "http://our.internal.code.system/encounter";
Encounter encounter = new Encounter();
encounter.addIdentifier(new Identifier().setValue(encounterId)
.setSystem(encounterSystem));
encounter.setStatus(Encounter.EncounterStatus.FINISHED);
Patient p = new Patient()
.addIdentifier(new Identifier().setValue("321-321").setSystem("http://our.internal.code.system/patient"));
p.setId(IdDt.newRandomUuid());
// add patient to bundle so its created
bundle.addEntry()
.setFullUrl(p.getId())
.setResource(p)
.getRequest()
.setUrl("Patient")
.setMethod(Bundle.HTTPVerb.POST);
Reference patientRef = new Reference(p.getId());
encounter.setSubject(patientRef);
Condition condition = new Condition()
.setCode(new CodeableConcept().addCoding(
new Coding("http://hl7.org/fhir/icd-10", "S53.40", "FOREARM SPRAIN / STRAIN")))
.setSubject(patientRef);
condition.setId(IdDt.newRandomUuid());
// add condition to bundle so its created
bundle.addEntry()
.setFullUrl(condition.getId())
.setResource(condition)
.getRequest()
.setUrl("Condition")
.setMethod(Bundle.HTTPVerb.POST);
Encounter.DiagnosisComponent dc = new Encounter.DiagnosisComponent();
dc.setCondition(new Reference(condition.getId()));
encounter.addDiagnosis(dc);
CodeableConcept reason = new CodeableConcept();
reason.setText("SLIPPED ON FLOOR,PAIN L) ELBOW");
encounter.addReason(reason);
// add encounter to bundle so its created
bundle.addEntry()
.setResource(encounter)
.getRequest()
.setUrl("Encounter")
.setIfNoneExist("identifier=" + encounterSystem + "|" + encounterId)
.setMethod(Bundle.HTTPVerb.POST);
Bundle resp = myClient.transaction().withBundle(bundle).execute();
assertEquals(3, resp.getEntry().size());
}
private void unregisterInterceptors() {
for (IServerInterceptor next : new ArrayList<IServerInterceptor>(ourRestServer.getInterceptors())) {
if (next instanceof AuthorizationInterceptor) {
ourRestServer.unregisterInterceptor(next);
}
}
}
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -25,7 +25,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
initTestPatients();
String query = "{name{family,given}}";
HttpGet httpGet = new HttpGet(ourServerBase + "/Patient/" + myPatientId0.getIdPart() + "/$graphql?query=" + UrlUtil.escape(query));
HttpGet httpGet = new HttpGet(ourServerBase + "/Patient/" + myPatientId0.getIdPart() + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
CloseableHttpResponse response = ourHttpClient.execute(httpGet);
try {
@ -50,7 +50,7 @@ public class GraphQLProviderR4Test extends BaseResourceProviderR4Test {
initTestPatients();
String query = "{PatientList(given:\"given\"){name{family,given}}}";
HttpGet httpGet = new HttpGet(ourServerBase + "/$graphql?query=" + UrlUtil.escape(query));
HttpGet httpGet = new HttpGet(ourServerBase + "/$graphql?query=" + UrlUtil.escapeUrlParam(query));
CloseableHttpResponse response = ourHttpClient.execute(httpGet);
try {

View File

@ -61,7 +61,6 @@ import java.math.BigDecimal;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
@ -1647,7 +1646,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myObservationDao.create(obs, mySrd);
}
String uri = ourServerBase + "/Patient?_has:Observation:subject:identifier=" + UrlUtil.escape("urn:system|FOO");
String uri = ourServerBase + "/Patient?_has:Observation:subject:identifier=" + UrlUtil.escapeUrlParam("urn:system|FOO");
List<String> ids = searchAndReturnUnqualifiedVersionlessIdValues(uri);
assertThat(ids, contains(pid0.getValue()));
}
@ -2355,7 +2354,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
myPatientDao.create(p, mySrd);
}
String uri = ourServerBase + "/Patient?name=" + URLEncoder.encode("Jernelöv", "UTF-8") + "&_count=5&_pretty=true";
String uri = ourServerBase + "/Patient?name=" + UrlUtil.escapeUrlParam("Jernelöv") + "&_count=5&_pretty=true";
ourLog.info("URI: {}", uri);
HttpGet get = new HttpGet(uri);
CloseableHttpResponse resp = ourHttpClient.execute(get);
@ -2558,6 +2557,59 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
}
/**
* https://chat.fhir.org/#narrow/stream/implementers/topic/Internal.20error.2C.20when.20executing.20the.20following.20query.20on.20HAPI
*/
@Test
public void testSearchByLastUpdatedGe() throws Exception {
Patient p2 = new Patient();
p2.setId("P2");
p2.addName().setFamily("P2");
myClient.update().resource(p2).execute().getId().toUnqualifiedVersionless();
Practitioner pract = new Practitioner();
pract.setId("PRAC");
pract.addName().setFamily("PRACT");
myClient.update().resource(pract).execute().getId().toUnqualifiedVersionless();
Encounter enc = new Encounter();
enc.setId("E2");
enc.setStatus(EncounterStatus.ARRIVED);
enc.setPeriod(new Period().setStart(new Date()).setEnd(new Date()));
enc.getSubject().setReference("Patient/P2");
enc.addParticipant().getIndividual().setReference("Practitioner/PRAC");
myClient.update().resource(enc).execute().getId().toUnqualifiedVersionless();
HttpGet get = new HttpGet(ourServerBase + "/Encounter?patient=P2&date=ge2017-01-01&_include:recurse=Encounter:practitioner&_lastUpdated=ge2017-11-10");
CloseableHttpResponse response = ourHttpClient.execute(get);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(response.getEntity().getContent());
ourLog.info(output);
List<String> ids = toUnqualifiedVersionlessIdValues(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
ourLog.info(ids.toString());
assertThat(ids, containsInAnyOrder("Practitioner/PRAC", "Encounter/E2"));
} finally {
response.close();
}
get = new HttpGet(ourServerBase + "/Encounter?patient=P2&date=ge2017-01-01&_include:recurse=Encounter:practitioner&_lastUpdated=ge2099-11-10");
response = ourHttpClient.execute(get);
try {
assertEquals(200, response.getStatusLine().getStatusCode());
String output = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
IOUtils.closeQuietly(response.getEntity().getContent());
ourLog.info(output);
List<String> ids = toUnqualifiedVersionlessIdValues(myFhirCtx.newXmlParser().parseResource(Bundle.class, output));
ourLog.info(ids.toString());
assertThat(ids, empty());
} finally {
response.close();
}
}
@Test
public void testSearchByReferenceIds() {
Organization o1 = new Organization();
@ -3219,7 +3271,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
// If this fails under load, try increasing the throttle above
assertEquals(null, found.getTotalElement().getValue());
assertEquals(1, found.getEntry().size());
assertThat(sw.getMillis(), lessThan(1000L));
assertThat(sw.getMillis(), lessThan(1500L));
}
@Test
@ -3885,7 +3937,7 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
pt.addName().setFamily("FOO");
resource = myFhirCtx.newXmlParser().encodeResourceToString(pt);
HttpPut put = new HttpPut(ourServerBase + "/Patient?identifier=" + ("http://general-hospital.co.uk/Identifiers|09832345234543876876".replace("|", UrlUtil.escape("|"))));
HttpPut put = new HttpPut(ourServerBase + "/Patient?identifier=" + ("http://general-hospital.co.uk/Identifiers|09832345234543876876".replace("|", UrlUtil.escapeUrlParam("|"))));
put.setEntity(new StringEntity(resource, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
IdType id2;
@ -4301,6 +4353,56 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test {
}
}
@Test
public void testParseAndEncodeExtensionWithValueWithExtension() throws IOException {
String input = "<Patient xmlns=\"http://hl7.org/fhir\">\n" +
" <extension url=\"https://purl.org/elab/fhir/network/StructureDefinition/1/BirthWeight\">\n" +
" <valueDecimal>\n" +
" <extension url=\"http://www.hl7.org/fhir/extension-data-absent-reason.html\">\n" +
" <valueCoding>\n" +
" <system value=\"http://hl7.org/fhir/ValueSet/birthweight\"/>\n" +
" <code value=\"Underweight\"/>\n" +
" <userSelected value=\"false\"/>\n" +
" </valueCoding>\n" +
" </extension>\n" +
" </valueDecimal>\n" +
" </extension>\n" +
" <identifier>\n" +
" <system value=\"https://purl.org/elab/fhir/network/StructureDefinition/1/EuroPrevallStudySubjects\"/>\n" +
" <value value=\"1\"/>\n" +
" </identifier>\n" +
" <gender value=\"female\"/>\n" +
"</Patient>";
HttpPost post = new HttpPost(ourServerBase + "/Patient");
post.setEntity(new StringEntity(input, ContentType.create(Constants.CT_FHIR_XML, "UTF-8")));
CloseableHttpResponse response = ourHttpClient.execute(post);
IdType id;
try {
assertEquals(201, response.getStatusLine().getStatusCode());
String newIdString = response.getFirstHeader(Constants.HEADER_LOCATION_LC).getValue();
assertThat(newIdString, startsWith(ourServerBase + "/Patient/"));
id = new IdType(newIdString);
} finally {
response.close();
}
HttpGet get = new HttpGet(ourServerBase + "/Patient/" + id.getIdPart() + "?_pretty=true");
response = ourHttpClient.execute(get);
try {
String resp = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8);
ourLog.info(resp);
assertEquals(200, response.getStatusLine().getStatusCode());
assertThat(resp, containsString("Underweight"));
} finally {
IOUtils.closeQuietly(response.getEntity().getContent());
response.close();
}
}
@Test
public void testValueSetExpandOperation() throws IOException {
CodeSystem cs = myFhirCtx.newXmlParser().parseResource(CodeSystem.class, new InputStreamReader(ResourceProviderR4Test.class.getResourceAsStream("/extensional-case-3-cs.xml")));

View File

@ -10,7 +10,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -80,13 +80,13 @@ public class AnalyticsInterceptor extends InterceptorAdapter {
b.append("&tid=").append(myAnalyticsTid);
b.append("&t=event");
b.append("&an=").append(UrlUtil.escape(myHostname)).append('+').append(UrlUtil.escape(next.getApplicationName()));
b.append("&an=").append(UrlUtil.escapeUrlParam(myHostname)).append('+').append(UrlUtil.escapeUrlParam(next.getApplicationName()));
b.append("&ec=").append(next.getResourceName());
b.append("&ea=").append(next.getRestOperation());
b.append("&cid=").append(next.getClientId());
b.append("&uip=").append(UrlUtil.escape(next.getSourceIp()));
b.append("&ua=").append(UrlUtil.escape(next.getUserAgent()));
b.append("&uip=").append(UrlUtil.escapeUrlParam(next.getSourceIp()));
b.append("&ua=").append(UrlUtil.escapeUrlParam(next.getUserAgent()));
b.append("\n");
}

View File

@ -4,7 +4,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../hapi-deployable-pom/pom.xml</relativePath>
</parent>

View File

@ -19,34 +19,38 @@ package ca.uhn.fhir.rest.server;
* limitations under the License.
* #L%
*/
import static org.apache.commons.lang3.StringUtils.*;
import java.io.*;
import java.net.URLEncoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import ca.uhn.fhir.util.BinaryUtil;
import org.hl7.fhir.instance.model.api.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.api.IResource;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.model.valueset.BundleTypeEnum;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.rest.api.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.api.PreferReturnEnum;
import ca.uhn.fhir.rest.api.SummaryEnum;
import ca.uhn.fhir.rest.api.server.IRestfulResponse;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.method.ElementsParameter;
import ca.uhn.fhir.rest.server.method.SummaryEnumParameter;
import ca.uhn.fhir.util.BinaryUtil;
import ca.uhn.fhir.util.DateUtils;
import ca.uhn.fhir.util.UrlUtil;
import org.hl7.fhir.instance.model.api.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.Writer;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static org.apache.commons.lang3.StringUtils.*;
public class RestfulServerUtils {
static final Pattern ACCEPT_HEADER_PATTERN = Pattern.compile("\\s*([a-zA-Z0-9+.*/-]+)\\s*(;\\s*([a-zA-Z]+)\\s*=\\s*([a-zA-Z0-9.]+)\\s*)?(,?)");
@ -127,74 +131,70 @@ public class RestfulServerUtils {
}
public static String createPagingLink(Set<Include> theIncludes, String theServerBase, String theSearchId, int theOffset, int theCount, Map<String, String[]> theRequestParameters, boolean thePrettyPrint,
BundleTypeEnum theBundleType) {
try {
StringBuilder b = new StringBuilder();
b.append(theServerBase);
b.append('?');
b.append(Constants.PARAM_PAGINGACTION);
b.append('=');
b.append(URLEncoder.encode(theSearchId, "UTF-8"));
BundleTypeEnum theBundleType) {
StringBuilder b = new StringBuilder();
b.append(theServerBase);
b.append('?');
b.append(Constants.PARAM_PAGINGACTION);
b.append('=');
b.append(UrlUtil.escapeUrlParam(theSearchId));
b.append('&');
b.append(Constants.PARAM_PAGINGOFFSET);
b.append('=');
b.append(theOffset);
b.append('&');
b.append(Constants.PARAM_COUNT);
b.append('=');
b.append(theCount);
String[] strings = theRequestParameters.get(Constants.PARAM_FORMAT);
if (strings != null && strings.length > 0) {
b.append('&');
b.append(Constants.PARAM_PAGINGOFFSET);
b.append(Constants.PARAM_FORMAT);
b.append('=');
b.append(theOffset);
b.append('&');
b.append(Constants.PARAM_COUNT);
b.append('=');
b.append(theCount);
String[] strings = theRequestParameters.get(Constants.PARAM_FORMAT);
if (strings != null && strings.length > 0) {
b.append('&');
b.append(Constants.PARAM_FORMAT);
b.append('=');
String format = strings[0];
format = replace(format, " ", "+");
b.append(UrlUtil.escape(format));
}
if (thePrettyPrint) {
b.append('&');
b.append(Constants.PARAM_PRETTY);
b.append('=');
b.append(Constants.PARAM_PRETTY_VALUE_TRUE);
}
if (theIncludes != null) {
for (Include nextInclude : theIncludes) {
if (isNotBlank(nextInclude.getValue())) {
b.append('&');
b.append(Constants.PARAM_INCLUDE);
b.append('=');
b.append(URLEncoder.encode(nextInclude.getValue(), "UTF-8"));
}
}
}
if (theBundleType != null) {
b.append('&');
b.append(Constants.PARAM_BUNDLETYPE);
b.append('=');
b.append(theBundleType.getCode());
}
String paramName = Constants.PARAM_ELEMENTS;
String[] params = theRequestParameters.get(paramName);
if (params != null) {
for (String nextValue : params) {
if (isNotBlank(nextValue)) {
b.append('&');
b.append(paramName);
b.append('=');
b.append(UrlUtil.escape(nextValue));
}
}
}
return b.toString();
} catch (UnsupportedEncodingException e) {
throw new Error("UTF-8 not supported", e);// should not happen
String format = strings[0];
format = replace(format, " ", "+");
b.append(UrlUtil.escapeUrlParam(format));
}
if (thePrettyPrint) {
b.append('&');
b.append(Constants.PARAM_PRETTY);
b.append('=');
b.append(Constants.PARAM_PRETTY_VALUE_TRUE);
}
if (theIncludes != null) {
for (Include nextInclude : theIncludes) {
if (isNotBlank(nextInclude.getValue())) {
b.append('&');
b.append(Constants.PARAM_INCLUDE);
b.append('=');
b.append(UrlUtil.escapeUrlParam(nextInclude.getValue()));
}
}
}
if (theBundleType != null) {
b.append('&');
b.append(Constants.PARAM_BUNDLETYPE);
b.append('=');
b.append(theBundleType.getCode());
}
String paramName = Constants.PARAM_ELEMENTS;
String[] params = theRequestParameters.get(paramName);
if (params != null) {
for (String nextValue : params) {
if (isNotBlank(nextValue)) {
b.append('&');
b.append(paramName);
b.append('=');
b.append(UrlUtil.escapeUrlParam(nextValue));
}
}
}
return b.toString();
}
/**
@ -392,15 +392,15 @@ public class RestfulServerUtils {
try {
NarrativeModeEnum narrativeMode = NarrativeModeEnum.valueOfCaseInsensitive(narrative[0]);
switch (narrativeMode) {
case NORMAL:
retVal = Collections.singleton(SummaryEnum.FALSE);
break;
case ONLY:
retVal = Collections.singleton(SummaryEnum.TEXT);
break;
case SUPPRESS:
retVal = Collections.singleton(SummaryEnum.DATA);
break;
case NORMAL:
retVal = Collections.singleton(SummaryEnum.FALSE);
break;
case ONLY:
retVal = Collections.singleton(SummaryEnum.TEXT);
break;
case SUPPRESS:
retVal = Collections.singleton(SummaryEnum.DATA);
break;
}
} catch (IllegalArgumentException e) {
ourLog.debug("Invalid {} parameter: {}", Constants.PARAM_NARRATIVE, narrative[0]);
@ -461,13 +461,13 @@ public class RestfulServerUtils {
EncodingEnum responseEncoding = RestfulServerUtils.determineResponseEncodingWithDefault(theRequestDetails).getEncoding();
IParser parser;
switch (responseEncoding) {
case JSON:
parser = theContext.newJsonParser();
break;
case XML:
default:
parser = theContext.newXmlParser();
break;
case JSON:
parser = theContext.newJsonParser();
break;
case XML:
default:
parser = theContext.newXmlParser();
break;
}
configureResponseParser(theRequestDetails, parser);
@ -581,13 +581,13 @@ public class RestfulServerUtils {
}
public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set<SummaryEnum> theSummaryMode, int stausCode, boolean theAddContentLocationHeader,
boolean respondGzip, RequestDetails theRequestDetails) throws IOException {
boolean respondGzip, RequestDetails theRequestDetails) throws IOException {
return streamResponseAsResource(theServer, theResource, theSummaryMode, stausCode, null, theAddContentLocationHeader, respondGzip, theRequestDetails, null, null);
}
public static Object streamResponseAsResource(IRestfulServerDefaults theServer, IBaseResource theResource, Set<SummaryEnum> theSummaryMode, int theStausCode, String theStatusMessage,
boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails, IIdType theOperationResourceId, IPrimitiveType<Date> theOperationResourceLastUpdated)
throws IOException {
boolean theAddContentLocationHeader, boolean respondGzip, RequestDetails theRequestDetails, IIdType theOperationResourceId, IPrimitiveType<Date> theOperationResourceLastUpdated)
throws IOException {
IRestfulResponse response = theRequestDetails.getResponse();
// Determine response encoding
@ -706,7 +706,7 @@ public class RestfulServerUtils {
try {
return Integer.parseInt(retVal[0]);
} catch (NumberFormatException e) {
ourLog.debug("Failed to parse {} value '{}': {}", new Object[] { theParamName, retVal[0], e });
ourLog.debug("Failed to parse {} value '{}': {}", new Object[]{theParamName, retVal[0], e});
return null;
}
}
@ -752,7 +752,7 @@ public class RestfulServerUtils {
if (theContentType.equals(EncodingEnum.JSON_PLAIN_STRING) || theContentType.equals(EncodingEnum.XML_PLAIN_STRING)) {
FhirVersionEnum ctxtEnum = theCtx.getVersion().getVersion();
myNonLegacy = ctxtEnum.isNewerThan(FhirVersionEnum.DSTU3)
|| (ctxtEnum.isEquivalentTo(FhirVersionEnum.DSTU3) && !"1.4.0".equals(theCtx.getVersion().getVersion().getFhirVersionString()));
|| (ctxtEnum.isEquivalentTo(FhirVersionEnum.DSTU3) && !"1.4.0".equals(theCtx.getVersion().getVersion().getFhirVersionString()));
} else {
myNonLegacy = EncodingEnum.isNonLegacy(theContentType);
}

View File

@ -32,6 +32,8 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.hl7.fhir.instance.model.api.IBaseOperationOutcome;
@ -99,7 +101,10 @@ public class ExceptionHandlingInterceptor extends InterceptorAdapter {
@Override
public BaseServerResponseException preProcessOutgoingException(RequestDetails theRequestDetails, Throwable theException, HttpServletRequest theServletRequest) throws ServletException {
BaseServerResponseException retVal;
if (!(theException instanceof BaseServerResponseException)) {
if (theException instanceof DataFormatException) {
// Wrapping the DataFormatException as an InvalidRequestException so that it gets sent back to the client as a 400 response.
retVal = new InvalidRequestException(theException);
} else if (!(theException instanceof BaseServerResponseException)) {
retVal = new InternalErrorException(theException);
} else {
retVal = (BaseServerResponseException) theException;

View File

@ -41,6 +41,8 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException;
import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails;
import static org.apache.commons.lang3.StringUtils.isBlank;
/**
* Provides methods to intercept requests and responses. Note that implementations of this interface may wish to use
* {@link InterceptorAdapter} in order to not need to implement every method.
@ -329,9 +331,9 @@ public interface IServerInterceptor {
public static class ActionRequestDetails {
private final FhirContext myContext;
private final IIdType myId;
private final String myResourceType;
private RequestDetails myRequestDetails;
private IBaseResource myResource;
private final String myResourceType;
public ActionRequestDetails(RequestDetails theRequestDetails) {
myId = theRequestDetails.getId();
@ -346,7 +348,11 @@ public interface IServerInterceptor {
}
public ActionRequestDetails(RequestDetails theRequestDetails, FhirContext theContext, String theResourceType, IIdType theId) {
myId = theId;
if (theId != null && isBlank(theId.getValue())) {
myId = null;
} else {
myId = theId;
}
myResourceType = theResourceType;
myContext = theContext;
myRequestDetails = theRequestDetails;
@ -409,6 +415,13 @@ public interface IServerInterceptor {
return myResource;
}
/**
* This method should not be called by client code
*/
public void setResource(IBaseResource theObject) {
myResource = theObject;
}
/**
* Returns the resource type this request pertains to, or <code>null</code> if this request is not type specific
* (e.g. server-history)
@ -450,13 +463,6 @@ public interface IServerInterceptor {
}
}
/**
* This method should not be called by client code
*/
public void setResource(IBaseResource theObject) {
myResource = theObject;
}
}
}

View File

@ -25,7 +25,6 @@ import java.io.IOException;
*/
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Map.Entry;
@ -33,6 +32,7 @@ import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import ca.uhn.fhir.util.UrlUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.text.StrLookup;
@ -277,13 +277,9 @@ public class LoggingInterceptor extends InterceptorAdapter {
} else {
b.append('&');
}
try {
b.append(URLEncoder.encode(next.getKey(), "UTF-8"));
b.append('=');
b.append(URLEncoder.encode(nextValue, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new ca.uhn.fhir.context.ConfigurationException("UTF-8 not supported", e);
}
b.append(UrlUtil.escapeUrlParam(next.getKey()));
b.append('=');
b.append(UrlUtil.escapeUrlParam(nextValue));
}
}
return b.toString();

View File

@ -92,9 +92,9 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
} else {
rawB.append('&');
}
rawB.append(UrlUtil.escape(next));
rawB.append(UrlUtil.escapeUrlParam(next));
rawB.append('=');
rawB.append(UrlUtil.escape(nextValue));
rawB.append(UrlUtil.escapeUrlParam(nextValue));
}
}
if (rawB.length() == 0) {

View File

@ -20,18 +20,6 @@ package ca.uhn.fhir.rest.server.interceptor.auth;
* #L%
*/
import static org.apache.commons.lang3.StringUtils.defaultString;
import java.util.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.*;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.TagList;
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
@ -40,6 +28,21 @@ import ca.uhn.fhir.rest.server.exceptions.AuthenticationException;
import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException;
import ca.uhn.fhir.rest.server.interceptor.ServerOperationInterceptorAdapter;
import ca.uhn.fhir.util.CoverageIgnore;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseParameters;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IIdType;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import static org.apache.commons.lang3.StringUtils.defaultString;
/**
* This class is a base class for interceptors which can be used to
@ -66,9 +69,8 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter
/**
* Constructor
*
* @param theDefaultPolicy
* The default policy if no rules apply (must not be null)
*
* @param theDefaultPolicy The default policy if no rules apply (must not be null)
*/
public AuthorizationInterceptor(PolicyEnum theDefaultPolicy) {
this();
@ -76,7 +78,7 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter
}
private void applyRulesAndFailIfDeny(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId,
IBaseResource theOutputResource) {
IBaseResource theOutputResource) {
Verdict decision = applyRulesAndReturnDecision(theOperation, theRequestDetails, theInputResource, theInputResourceId, theOutputResource);
if (decision.getDecision() == PolicyEnum.ALLOW) {
@ -88,7 +90,7 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter
@Override
public Verdict applyRulesAndReturnDecision(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IIdType theInputResourceId,
IBaseResource theOutputResource) {
IBaseResource theOutputResource) {
List<IAuthRule> rules = buildRuleList(theRequestDetails);
ourLog.trace("Applying {} rules to render an auth decision for operation {}", rules.size(), theOperation);
@ -117,9 +119,8 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter
* out who the current user is and then using a {@link RuleBuilder} to create
* an appropriate rule chain.
* </p>
*
* @param theRequestDetails
* The individual request currently being applied
*
* @param theRequestDetails The individual request currently being applied
*/
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new ArrayList<IAuthRule>();
@ -127,63 +128,63 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter
private OperationExamineDirection determineOperationDirection(RestOperationTypeEnum theOperation, IBaseResource theRequestResource) {
switch (theOperation) {
case ADD_TAGS:
case DELETE_TAGS:
case GET_TAGS:
// These are DSTU1 operations and not relevant
return OperationExamineDirection.NONE;
case ADD_TAGS:
case DELETE_TAGS:
case GET_TAGS:
// These are DSTU1 operations and not relevant
return OperationExamineDirection.NONE;
case EXTENDED_OPERATION_INSTANCE:
case EXTENDED_OPERATION_SERVER:
case EXTENDED_OPERATION_TYPE:
return OperationExamineDirection.BOTH;
case EXTENDED_OPERATION_INSTANCE:
case EXTENDED_OPERATION_SERVER:
case EXTENDED_OPERATION_TYPE:
return OperationExamineDirection.BOTH;
case METADATA:
// Security does not apply to these operations
return OperationExamineDirection.IN;
case METADATA:
// Security does not apply to these operations
return OperationExamineDirection.IN;
case DELETE:
// Delete is a special case
return OperationExamineDirection.NONE;
case DELETE:
// Delete is a special case
return OperationExamineDirection.NONE;
case CREATE:
case UPDATE:
case PATCH:
// if (theRequestResource != null) {
// if (theRequestResource.getIdElement() != null) {
// if (theRequestResource.getIdElement().hasIdPart() == false) {
// return OperationExamineDirection.IN_UNCATEGORIZED;
// }
// }
// }
return OperationExamineDirection.IN;
case CREATE:
case UPDATE:
case PATCH:
// if (theRequestResource != null) {
// if (theRequestResource.getIdElement() != null) {
// if (theRequestResource.getIdElement().hasIdPart() == false) {
// return OperationExamineDirection.IN_UNCATEGORIZED;
// }
// }
// }
return OperationExamineDirection.IN;
case META:
case META_ADD:
case META_DELETE:
// meta operations do not apply yet
return OperationExamineDirection.NONE;
case META:
case META_ADD:
case META_DELETE:
// meta operations do not apply yet
return OperationExamineDirection.NONE;
case GET_PAGE:
case HISTORY_INSTANCE:
case HISTORY_SYSTEM:
case HISTORY_TYPE:
case READ:
case SEARCH_SYSTEM:
case SEARCH_TYPE:
case VREAD:
return OperationExamineDirection.OUT;
case GET_PAGE:
case HISTORY_INSTANCE:
case HISTORY_SYSTEM:
case HISTORY_TYPE:
case READ:
case SEARCH_SYSTEM:
case SEARCH_TYPE:
case VREAD:
return OperationExamineDirection.OUT;
case TRANSACTION:
return OperationExamineDirection.BOTH;
case TRANSACTION:
return OperationExamineDirection.BOTH;
case VALIDATE:
// Nothing yet
return OperationExamineDirection.NONE;
case VALIDATE:
// Nothing yet
return OperationExamineDirection.NONE;
default:
// Should not happen
throw new IllegalStateException("Unable to apply security to event of type " + theOperation);
default:
// Should not happen
throw new IllegalStateException("Unable to apply security to event of type " + theOperation);
}
}
@ -195,6 +196,16 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter
return myDefaultPolicy;
}
/**
* The default policy if no rules have been found to apply. Default value for this setting is {@link PolicyEnum#DENY}
*
* @param theDefaultPolicy The policy (must not be <code>null</code>)
*/
public void setDefaultPolicy(PolicyEnum theDefaultPolicy) {
Validate.notNull(theDefaultPolicy, "theDefaultPolicy must not be null");
myDefaultPolicy = theDefaultPolicy;
}
/**
* Handle an access control verdict of {@link PolicyEnum#DENY}.
* <p>
@ -221,17 +232,17 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter
IIdType inputResourceId = null;
switch (determineOperationDirection(theOperation, theProcessedRequest.getResource())) {
case IN:
case BOTH:
inputResource = theProcessedRequest.getResource();
inputResourceId = theProcessedRequest.getId();
break;
case OUT:
// inputResource = null;
inputResourceId = theProcessedRequest.getId();
break;
case NONE:
return;
case IN:
case BOTH:
inputResource = theProcessedRequest.getResource();
inputResourceId = theProcessedRequest.getId();
break;
case OUT:
// inputResource = null;
inputResourceId = theProcessedRequest.getId();
break;
case NONE:
return;
}
RequestDetails requestDetails = theProcessedRequest.getRequestDetails();
@ -241,43 +252,39 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter
@Override
public boolean outgoingResponse(RequestDetails theRequestDetails, IBaseResource theResponseObject) {
switch (determineOperationDirection(theRequestDetails.getRestOperationType(), null)) {
case IN:
case NONE:
return true;
case BOTH:
case OUT:
break;
case IN:
case NONE:
return true;
case BOTH:
case OUT:
break;
}
FhirContext fhirContext = theRequestDetails.getServer().getFhirContext();
List<IBaseResource> resources = Collections.emptyList();
switch (theRequestDetails.getRestOperationType()) {
case SEARCH_SYSTEM:
case SEARCH_TYPE:
case HISTORY_INSTANCE:
case HISTORY_SYSTEM:
case HISTORY_TYPE:
case TRANSACTION:
case GET_PAGE:
case EXTENDED_OPERATION_SERVER:
case EXTENDED_OPERATION_TYPE:
case EXTENDED_OPERATION_INSTANCE: {
if (theResponseObject != null) {
if (theResponseObject instanceof IBaseBundle) {
resources = toListOfResourcesAndExcludeContainer(theResponseObject, fhirContext);
} else if (theResponseObject instanceof IBaseParameters) {
case SEARCH_SYSTEM:
case SEARCH_TYPE:
case HISTORY_INSTANCE:
case HISTORY_SYSTEM:
case HISTORY_TYPE:
case TRANSACTION:
case GET_PAGE:
case EXTENDED_OPERATION_SERVER:
case EXTENDED_OPERATION_TYPE:
case EXTENDED_OPERATION_INSTANCE: {
if (theResponseObject != null) {
resources = toListOfResourcesAndExcludeContainer(theResponseObject, fhirContext);
}
break;
}
break;
}
default: {
if (theResponseObject != null) {
resources = Collections.singletonList(theResponseObject);
default: {
if (theResponseObject != null) {
resources = Collections.singletonList(theResponseObject);
}
break;
}
break;
}
}
for (IBaseResource nextResponse : resources) {
@ -296,7 +303,7 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter
@CoverageIgnore
@Override
public boolean outgoingResponse(RequestDetails theRequestDetails, TagList theResponseObject, HttpServletRequest theServletRequest, HttpServletResponse theServletResponse)
throws AuthenticationException {
throws AuthenticationException {
throw failForDstu1();
}
@ -318,33 +325,38 @@ public class AuthorizationInterceptor extends ServerOperationInterceptorAdapter
handleUserOperation(theRequest, theNewResource, RestOperationTypeEnum.UPDATE);
}
/**
* The default policy if no rules have been found to apply. Default value for this setting is {@link PolicyEnum#DENY}
*
* @param theDefaultPolicy
* The policy (must not be <code>null</code>)
*/
public void setDefaultPolicy(PolicyEnum theDefaultPolicy) {
Validate.notNull(theDefaultPolicy, "theDefaultPolicy must not be null");
myDefaultPolicy = theDefaultPolicy;
}
private List<IBaseResource> toListOfResourcesAndExcludeContainer(IBaseResource theResponseObject, FhirContext fhirContext) {
List<IBaseResource> resources;
resources = fhirContext.newTerser().getAllPopulatedChildElementsOfType(theResponseObject, IBaseResource.class);
// Exclude the container
if (resources.size() > 0 && resources.get(0) == theResponseObject) {
resources = resources.subList(1, resources.size());
}
return resources;
}
private static UnsupportedOperationException failForDstu1() {
return new UnsupportedOperationException("Use of this interceptor on DSTU1 servers is not supportd");
}
static List<IBaseResource> toListOfResourcesAndExcludeContainer(IBaseResource theResponseObject, FhirContext fhirContext) {
if (theResponseObject == null) {
return Collections.emptyList();
}
List<IBaseResource> retVal;
boolean isContainer = false;
if (theResponseObject instanceof IBaseBundle) {
isContainer = true;
} else if (theResponseObject instanceof IBaseParameters) {
isContainer = true;
}
if (!isContainer) {
return Collections.singletonList(theResponseObject);
}
retVal = fhirContext.newTerser().getAllPopulatedChildElementsOfType(theResponseObject, IBaseResource.class);
// Exclude the container
if (retVal.size() > 0 && retVal.get(0) == theResponseObject) {
retVal = retVal.subList(1, retVal.size());
}
return retVal;
}
private enum OperationExamineDirection {
BOTH,
IN,

View File

@ -165,13 +165,15 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
}
return verdict;
} else if (theOutputResource != null) {
List<BundleEntryParts> inputResources = BundleUtil.toListOfEntries(ctx, (IBaseBundle) theInputResource);
List<IBaseResource> outputResources = AuthorizationInterceptor.toListOfResourcesAndExcludeContainer(theOutputResource, theRequestDetails.getFhirContext());
Verdict verdict = null;
for (BundleEntryParts nextPart : inputResources) {
if (nextPart.getResource() == null) {
for (IBaseResource nextResource : outputResources) {
if (nextResource == null) {
continue;
}
Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(RestOperationTypeEnum.READ, theRequestDetails, null, null, nextPart.getResource());
Verdict newVerdict = theRuleApplier.applyRulesAndReturnDecision(RestOperationTypeEnum.READ, theRequestDetails, null, null, nextResource);
if (newVerdict == null) {
continue;
} else if (verdict == null) {
@ -225,7 +227,7 @@ class RuleImplOp extends BaseRule /* implements IAuthRule */ {
return null;
}
}
if (appliesToResourceId != null) {
if (appliesToResourceId != null && appliesToResourceId.hasResourceType()) {
Class<? extends IBaseResource> type = theRequestDetails.getServer().getFhirContext().getResourceDefinition(appliesToResourceId.getResourceType()).getImplementingClass();
if (myAppliesToTypes.contains(type) == false) {
return null;

View File

@ -186,9 +186,9 @@ public abstract class BaseResourceReturningMethodBinding extends BaseMethodBindi
} else {
b.append('&');
}
b.append(UrlUtil.escape(nextParamName));
b.append(UrlUtil.escapeUrlParam(nextParamName));
b.append('=');
b.append(UrlUtil.escape(nextParamValue));
b.append(UrlUtil.escapeUrlParam(nextParamValue));
}
}
}

View File

@ -1,118 +1,119 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-autoconfigure</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Compile dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- Optional dependencies -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client-okhttp</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<optional>true</optional>
</dependency>
<!-- @ConfigurationProperties annotation processing (metadata for IDEs) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2.1</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-spring-boot-autoconfigure</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Compile dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<!-- Optional dependencies -->
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-base</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-server</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jpaserver-base</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-client-okhttp</artifactId>
<version>${project.version}</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<optional>true</optional>
</dependency>
<!-- @ConfigurationProperties annotation processing (metadata for IDEs) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu2.1</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -1,287 +1,308 @@
package ca.uhn.fhir.spring.boot.autoconfigure;
import java.util.List;
import javax.servlet.ServletException;
import javax.sql.DataSource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2;
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.BaseJpaProvider;
import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider;
import ca.uhn.fhir.okhttp.client.OkHttpRestfulClientFactory;
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.IRestfulClientFactory;
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor;
import okhttp3.OkHttpClient;
import org.apache.http.client.HttpClient;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ResourceCondition;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.util.CollectionUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for HAPI FHIR.
*
* @author Mathieu Ouellet
*/
@Configuration
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
@EnableConfigurationProperties(FhirProperties.class)
public class FhirAutoConfiguration {
private final FhirProperties properties;
public FhirAutoConfiguration(FhirProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public FhirContext fhirContext() {
FhirContext fhirContext = new FhirContext(properties.getVersion());
return fhirContext;
}
@Configuration
@ConditionalOnClass(AbstractJaxRsProvider.class)
@EnableConfigurationProperties(FhirProperties.class)
@ConfigurationProperties("hapi.fhir.rest")
@SuppressWarnings("serial")
static class FhirRestfulServerConfiguration extends RestfulServer {
private final FhirProperties properties;
private final FhirContext fhirContext;
private final List<IResourceProvider> resourceProviders;
private final IPagingProvider pagingProvider;
private final List<IServerInterceptor> interceptors;
private final List<FhirRestfulServerCustomizer> customizers;
public FhirRestfulServerConfiguration(
FhirProperties properties,
FhirContext fhirContext,
ObjectProvider<List<IResourceProvider>> resourceProviders,
ObjectProvider<IPagingProvider> pagingProvider,
ObjectProvider<List<IServerInterceptor>> interceptors,
ObjectProvider<List<FhirRestfulServerCustomizer>> customizers) {
this.properties = properties;
this.fhirContext = fhirContext;
this.resourceProviders = resourceProviders.getIfAvailable();
this.pagingProvider = pagingProvider.getIfAvailable();
this.interceptors = interceptors.getIfAvailable();
this.customizers = customizers.getIfAvailable();
}
@Bean
public ServletRegistrationBean fhirServerRegistrationBean() {
ServletRegistrationBean registration = new ServletRegistrationBean(this, this.properties.getServer().getPath());
registration.setLoadOnStartup(1);
return registration;
}
@Override
protected void initialize() throws ServletException {
super.initialize();
setFhirContext(this.fhirContext);
setResourceProviders(this.resourceProviders);
setPagingProvider(this.pagingProvider);
setInterceptors(this.interceptors);
setServerAddressStrategy(new HardcodedServerAddressStrategy(this.properties.getServer().getPath()));
customize();
}
private void customize() {
if (this.customizers != null) {
AnnotationAwareOrderComparator.sort(this.customizers);
for (FhirRestfulServerCustomizer customizer : this.customizers) {
customizer.customize(this);
}
}
}
}
@Configuration
@ConditionalOnClass(BaseJpaProvider.class)
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(FhirProperties.class)
static class FhirJpaServerConfiguration {
@Configuration
@EntityScan("ca.uhn.fhir.jpa.entity")
@EnableJpaRepositories(basePackages = "ca.uhn.fhir.jpa.dao.data")
static class FhirJpaDaoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConfigurationProperties("hapi.fhir.jpa")
public DaoConfig fhirDaoConfig() {
DaoConfig fhirDaoConfig = new DaoConfig();
return fhirDaoConfig;
}
}
@Configuration
@ConditionalOnBean({ DaoConfig.class, RestfulServer.class })
@SuppressWarnings("rawtypes")
static class RestfulServerCustomizer implements FhirRestfulServerCustomizer {
private final BaseJpaSystemProvider systemProviders;
public RestfulServerCustomizer(ObjectProvider<BaseJpaSystemProvider> systemProviders) {
this.systemProviders = systemProviders.getIfAvailable();
}
@Override
public void customize(RestfulServer server) {
server.setPlainProviders(systemProviders);
}
}
@Configuration
@ConditionalOnMissingBean(type = "ca.uhn.fhir.jpa.config.BaseConfig")
@ConditionalOnProperty(name = "hapi.fhir.version", havingValue = "DSTU3")
static class Dstu3 extends BaseJavaConfigDstu3 {
}
@Configuration
@ConditionalOnMissingBean(type = "ca.uhn.fhir.jpa.config.BaseConfig")
@ConditionalOnProperty(name = "hapi.fhir.version", havingValue = "DSTU2")
static class Dstu2 extends BaseJavaConfigDstu2 {
}
}
@Configuration
@Conditional(FhirValidationConfiguration.SchemaAvailableCondition.class)
@ConditionalOnProperty(name = "hapi.fhir.validation.enabled", matchIfMissing = true)
static class FhirValidationConfiguration {
@Bean
@ConditionalOnMissingBean
public RequestValidatingInterceptor requestValidatingInterceptor() {
return new RequestValidatingInterceptor();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "hapi.fhir.validation.request-only", havingValue = "false")
public ResponseValidatingInterceptor responseValidatingInterceptor() {
return new ResponseValidatingInterceptor();
}
static class SchemaAvailableCondition extends ResourceCondition {
SchemaAvailableCondition() {
super("ValidationSchema",
"hapi.fhir.validation",
"schema-location",
"classpath:/org/hl7/fhir/instance/model/schema",
"classpath:/org/hl7/fhir/dstu2016may/model/schema",
"classpath:/org/hl7/fhir/dstu3/model/schema");
}
}
}
@Configuration
@ConditionalOnProperty("hapi.fhir.server.url")
@EnableConfigurationProperties(FhirProperties.class)
static class FhirRestfulClientConfiguration {
private final FhirProperties properties;
private final List<IClientInterceptor> clientInterceptors;
public FhirRestfulClientConfiguration(FhirProperties properties, ObjectProvider<List<IClientInterceptor>> clientInterceptors) {
this.properties = properties;
this.clientInterceptors = clientInterceptors.getIfAvailable();
}
@Bean
@ConditionalOnBean(IRestfulClientFactory.class)
public IGenericClient fhirClient(final IRestfulClientFactory clientFactory) {
IGenericClient fhirClient = clientFactory.newGenericClient(this.properties.getServer().getUrl());
if (!CollectionUtils.isEmpty(this.clientInterceptors)) {
for (IClientInterceptor interceptor : this.clientInterceptors) {
fhirClient.registerInterceptor(interceptor);
}
}
return fhirClient;
}
@Configuration
@ConditionalOnClass(HttpClient.class)
@ConditionalOnMissingClass("okhttp3.OkHttpClient")
static class Apache {
private final FhirContext context;
public Apache(FhirContext context) {
this.context = context;
}
@Bean
@ConditionalOnMissingBean
@ConfigurationProperties("hapi.fhir.rest.client.apache")
public IRestfulClientFactory fhirRestfulClientFactory() {
ApacheRestfulClientFactory restfulClientFactory = new ApacheRestfulClientFactory(this.context);
return restfulClientFactory;
}
}
@Configuration
@ConditionalOnClass(OkHttpClient.class)
static class OkHttp {
private final FhirContext context;
public OkHttp(FhirContext context) {
this.context = context;
}
@Bean
@ConditionalOnMissingBean
@ConfigurationProperties("hapi.fhir.rest.client.okhttp")
public IRestfulClientFactory fhirRestfulClientFactory() {
OkHttpRestfulClientFactory restfulClientFactory = new OkHttpRestfulClientFactory(this.context);
return restfulClientFactory;
}
}
}
}
package ca.uhn.fhir.spring.boot.autoconfigure;
/*-
* #%L
* hapi-fhir-spring-boot-autoconfigure
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.List;
import javax.servlet.ServletException;
import javax.sql.DataSource;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jaxrs.server.AbstractJaxRsProvider;
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu2;
import ca.uhn.fhir.jpa.config.BaseJavaConfigDstu3;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.provider.BaseJpaProvider;
import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider;
import ca.uhn.fhir.okhttp.client.OkHttpRestfulClientFactory;
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.IRestfulClientFactory;
import ca.uhn.fhir.rest.server.HardcodedServerAddressStrategy;
import ca.uhn.fhir.rest.server.IPagingProvider;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor;
import ca.uhn.fhir.rest.server.interceptor.ResponseValidatingInterceptor;
import okhttp3.OkHttpClient;
import org.apache.http.client.HttpClient;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ResourceCondition;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.util.CollectionUtils;
/**
* {@link EnableAutoConfiguration Auto-configuration} for HAPI FHIR.
*
* @author Mathieu Ouellet
*/
@Configuration
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
@EnableConfigurationProperties(FhirProperties.class)
public class FhirAutoConfiguration {
private final FhirProperties properties;
public FhirAutoConfiguration(FhirProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public FhirContext fhirContext() {
FhirContext fhirContext = new FhirContext(properties.getVersion());
return fhirContext;
}
@Configuration
@ConditionalOnClass(AbstractJaxRsProvider.class)
@EnableConfigurationProperties(FhirProperties.class)
@ConfigurationProperties("hapi.fhir.rest")
@SuppressWarnings("serial")
static class FhirRestfulServerConfiguration extends RestfulServer {
private final FhirProperties properties;
private final FhirContext fhirContext;
private final List<IResourceProvider> resourceProviders;
private final IPagingProvider pagingProvider;
private final List<IServerInterceptor> interceptors;
private final List<FhirRestfulServerCustomizer> customizers;
public FhirRestfulServerConfiguration(
FhirProperties properties,
FhirContext fhirContext,
ObjectProvider<List<IResourceProvider>> resourceProviders,
ObjectProvider<IPagingProvider> pagingProvider,
ObjectProvider<List<IServerInterceptor>> interceptors,
ObjectProvider<List<FhirRestfulServerCustomizer>> customizers) {
this.properties = properties;
this.fhirContext = fhirContext;
this.resourceProviders = resourceProviders.getIfAvailable();
this.pagingProvider = pagingProvider.getIfAvailable();
this.interceptors = interceptors.getIfAvailable();
this.customizers = customizers.getIfAvailable();
}
@Bean
public ServletRegistrationBean fhirServerRegistrationBean() {
ServletRegistrationBean registration = new ServletRegistrationBean(this, this.properties.getServer().getPath());
registration.setLoadOnStartup(1);
return registration;
}
@Override
protected void initialize() throws ServletException {
super.initialize();
setFhirContext(this.fhirContext);
setResourceProviders(this.resourceProviders);
setPagingProvider(this.pagingProvider);
setInterceptors(this.interceptors);
setServerAddressStrategy(new HardcodedServerAddressStrategy(this.properties.getServer().getPath()));
customize();
}
private void customize() {
if (this.customizers != null) {
AnnotationAwareOrderComparator.sort(this.customizers);
for (FhirRestfulServerCustomizer customizer : this.customizers) {
customizer.customize(this);
}
}
}
}
@Configuration
@ConditionalOnClass(BaseJpaProvider.class)
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(FhirProperties.class)
static class FhirJpaServerConfiguration {
@Configuration
@EntityScan("ca.uhn.fhir.jpa.entity")
@EnableJpaRepositories(basePackages = "ca.uhn.fhir.jpa.dao.data")
static class FhirJpaDaoConfiguration {
@Bean
@ConditionalOnMissingBean
@ConfigurationProperties("hapi.fhir.jpa")
public DaoConfig fhirDaoConfig() {
DaoConfig fhirDaoConfig = new DaoConfig();
return fhirDaoConfig;
}
}
@Configuration
@ConditionalOnBean({ DaoConfig.class, RestfulServer.class })
@SuppressWarnings("rawtypes")
static class RestfulServerCustomizer implements FhirRestfulServerCustomizer {
private final BaseJpaSystemProvider systemProviders;
public RestfulServerCustomizer(ObjectProvider<BaseJpaSystemProvider> systemProviders) {
this.systemProviders = systemProviders.getIfAvailable();
}
@Override
public void customize(RestfulServer server) {
server.setPlainProviders(systemProviders);
}
}
@Configuration
@ConditionalOnMissingBean(type = "ca.uhn.fhir.jpa.config.BaseConfig")
@ConditionalOnProperty(name = "hapi.fhir.version", havingValue = "DSTU3")
static class Dstu3 extends BaseJavaConfigDstu3 {
}
@Configuration
@ConditionalOnMissingBean(type = "ca.uhn.fhir.jpa.config.BaseConfig")
@ConditionalOnProperty(name = "hapi.fhir.version", havingValue = "DSTU2")
static class Dstu2 extends BaseJavaConfigDstu2 {
}
}
@Configuration
@Conditional(FhirValidationConfiguration.SchemaAvailableCondition.class)
@ConditionalOnProperty(name = "hapi.fhir.validation.enabled", matchIfMissing = true)
static class FhirValidationConfiguration {
@Bean
@ConditionalOnMissingBean
public RequestValidatingInterceptor requestValidatingInterceptor() {
return new RequestValidatingInterceptor();
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(name = "hapi.fhir.validation.request-only", havingValue = "false")
public ResponseValidatingInterceptor responseValidatingInterceptor() {
return new ResponseValidatingInterceptor();
}
static class SchemaAvailableCondition extends ResourceCondition {
SchemaAvailableCondition() {
super("ValidationSchema",
"hapi.fhir.validation",
"schema-location",
"classpath:/org/hl7/fhir/instance/model/schema",
"classpath:/org/hl7/fhir/dstu2016may/model/schema",
"classpath:/org/hl7/fhir/dstu3/model/schema");
}
}
}
@Configuration
@ConditionalOnProperty("hapi.fhir.server.url")
@EnableConfigurationProperties(FhirProperties.class)
static class FhirRestfulClientConfiguration {
private final FhirProperties properties;
private final List<IClientInterceptor> clientInterceptors;
public FhirRestfulClientConfiguration(FhirProperties properties, ObjectProvider<List<IClientInterceptor>> clientInterceptors) {
this.properties = properties;
this.clientInterceptors = clientInterceptors.getIfAvailable();
}
@Bean
@ConditionalOnBean(IRestfulClientFactory.class)
public IGenericClient fhirClient(final IRestfulClientFactory clientFactory) {
IGenericClient fhirClient = clientFactory.newGenericClient(this.properties.getServer().getUrl());
if (!CollectionUtils.isEmpty(this.clientInterceptors)) {
for (IClientInterceptor interceptor : this.clientInterceptors) {
fhirClient.registerInterceptor(interceptor);
}
}
return fhirClient;
}
@Configuration
@ConditionalOnClass(HttpClient.class)
@ConditionalOnMissingClass("okhttp3.OkHttpClient")
static class Apache {
private final FhirContext context;
public Apache(FhirContext context) {
this.context = context;
}
@Bean
@ConditionalOnMissingBean
@ConfigurationProperties("hapi.fhir.rest.client.apache")
public IRestfulClientFactory fhirRestfulClientFactory() {
ApacheRestfulClientFactory restfulClientFactory = new ApacheRestfulClientFactory(this.context);
return restfulClientFactory;
}
}
@Configuration
@ConditionalOnClass(OkHttpClient.class)
static class OkHttp {
private final FhirContext context;
public OkHttp(FhirContext context) {
this.context = context;
}
@Bean
@ConditionalOnMissingBean
@ConfigurationProperties("hapi.fhir.rest.client.okhttp")
public IRestfulClientFactory fhirRestfulClientFactory() {
OkHttpRestfulClientFactory restfulClientFactory = new OkHttpRestfulClientFactory(this.context);
return restfulClientFactory;
}
}
}
}

View File

@ -1,85 +1,106 @@
package ca.uhn.fhir.spring.boot.autoconfigure;
import ca.uhn.fhir.context.FhirVersionEnum;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "hapi.fhir")
public class FhirProperties {
private FhirVersionEnum version = FhirVersionEnum.DSTU2;
private Server server = new Server();
private Validation validation = new Validation();
public FhirVersionEnum getVersion() {
return version;
}
public void setVersion(FhirVersionEnum version) {
this.version = version;
}
public Server getServer() {
return server;
}
public void setServer(Server server) {
this.server = server;
}
public Validation getValidation() {
return validation;
}
public void setValidation(Validation validation) {
this.validation = validation;
}
public static class Server {
private String url;
private String path = "/fhir/*";
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
public static class Validation {
private boolean enabled = true;
private boolean requestOnly = true;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isRequestOnly() {
return requestOnly;
}
public void setRequestOnly(boolean requestOnly) {
this.requestOnly = requestOnly;
}
}
}
package ca.uhn.fhir.spring.boot.autoconfigure;
/*-
* #%L
* hapi-fhir-spring-boot-autoconfigure
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.context.FhirVersionEnum;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "hapi.fhir")
public class FhirProperties {
private FhirVersionEnum version = FhirVersionEnum.DSTU2;
private Server server = new Server();
private Validation validation = new Validation();
public FhirVersionEnum getVersion() {
return version;
}
public void setVersion(FhirVersionEnum version) {
this.version = version;
}
public Server getServer() {
return server;
}
public void setServer(Server server) {
this.server = server;
}
public Validation getValidation() {
return validation;
}
public void setValidation(Validation validation) {
this.validation = validation;
}
public static class Server {
private String url;
private String path = "/fhir/*";
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
}
public static class Validation {
private boolean enabled = true;
private boolean requestOnly = true;
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isRequestOnly() {
return requestOnly;
}
public void setRequestOnly(boolean requestOnly) {
this.requestOnly = requestOnly;
}
}
}

View File

@ -1,5 +1,25 @@
package ca.uhn.fhir.spring.boot.autoconfigure;
/*-
* #%L
* hapi-fhir-spring-boot-autoconfigure
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.rest.server.RestfulServer;
@FunctionalInterface

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-client-apache</artifactId>

View File

@ -1,5 +1,25 @@
package sample.fhir.client;
/*-
* #%L
* hapi-fhir-spring-boot-sample-client-apache
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import org.hl7.fhir.dstu3.model.CapabilityStatement;

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-client-okhttp</artifactId>

View File

@ -1,5 +1,25 @@
package sample.fhir.client;
/*-
* #%L
* hapi-fhir-spring-boot-sample-client-okhttp
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import org.hl7.fhir.dstu3.model.CapabilityStatement;

View File

@ -1,67 +1,85 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-server-jersey</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-starter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Optional -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.2.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-server-jersey</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Compile -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-starter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-jaxrsserver-base</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-validation-resources-dstu3</artifactId>
<version>${project.version}</version>
</dependency>
<!-- Optional -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
<plugin>
<groupId>org.basepom.maven</groupId>
<artifactId>duplicate-finder-maven-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,5 +1,25 @@
package sample.fhir.server.jersey;
/*-
* #%L
* hapi-fhir-spring-boot-sample-server-jersey
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.rest.server.interceptor.LoggingInterceptor;
import org.springframework.boot.SpringApplication;

View File

@ -1,5 +1,25 @@
package sample.fhir.server.jersey.provider;
/*-
* #%L
* hapi-fhir-spring-boot-sample-server-jersey
* %%
* Copyright (C) 2014 - 2017 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.util.concurrent.ConcurrentHashMap;
import ca.uhn.fhir.context.FhirContext;

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-sample-server-jpa</artifactId>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot</artifactId>
<version>3.1.0-SNAPSHOT</version>
<version>3.2.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-samples</artifactId>

View File

@ -1,27 +1,28 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot</artifactId>
<version>3.1.0-SNAPSHOT</version>
</parent>
<artifactId>hapi-fhir-spring-boot-starter</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-autoconfigure</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-deployable-pom</artifactId>
<version>3.2.0-SNAPSHOT</version>
<relativePath>../../hapi-deployable-pom/pom.xml</relativePath>
</parent>
<artifactId>hapi-fhir-spring-boot-starter</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>hapi-fhir-spring-boot-autoconfigure</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</project>

Some files were not shown because too many files have changed in this diff Show More