Merge remote-tracking branch 'origin/master'

This commit is contained in:
patrick-werner 2018-05-16 09:44:23 +02:00
commit 13243956f7
18 changed files with 3053 additions and 2915 deletions

View File

@ -113,6 +113,7 @@ public class LoincHandler implements IRecordHandler {
ourLog.warn("Unable to find part code with TYPE[{}] and NAME[{}]", key.getPartType(), key.getPartName());
}
break;
case DECIMAL:
case CODE:
case INTEGER:
case BOOLEAN:

View File

@ -87,8 +87,8 @@ public class FhirTesterConfig {
.addServer()
.withId("spark2")
.withFhirVersion(FhirVersionEnum.DSTU2)
.withBaseUrl("http://spark-dstu2.furore.com/fhir")
.withName("Spark - Furore (DSTU2 FHIR)");
.withBaseUrl("http://vonk.furore.com/")
.withName("Vonk - Furore (STU3 FHIR)");
return retVal;
}

View File

@ -34,24 +34,24 @@ public class ResourceBinding {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceBinding.class);
private String resourceName;
private List<BaseMethodBinding<?>> methods = new ArrayList<BaseMethodBinding<?>>();
private List<BaseMethodBinding<?>> myMethodBindings = new ArrayList<>();
public ResourceBinding() {
}
public ResourceBinding(String resourceName, List<BaseMethodBinding<?>> methods) {
this.resourceName = resourceName;
this.methods = methods;
this.myMethodBindings = methods;
}
public BaseMethodBinding<?> getMethod(RequestDetails theRequest) {
if (null == methods) {
if (null == myMethodBindings) {
ourLog.warn("No methods exist for resource: {}", resourceName);
return null;
}
ourLog.debug("Looking for a handler for {}", theRequest);
for (BaseMethodBinding<?> rm : methods) {
for (BaseMethodBinding<?> rm : myMethodBindings) {
if (rm.incomingServerRequestMatchesMethod(theRequest)) {
ourLog.debug("Handler {} matches", rm);
return rm;
@ -70,15 +70,15 @@ public class ResourceBinding {
}
public List<BaseMethodBinding<?>> getMethodBindings() {
return methods;
return myMethodBindings;
}
public void setMethods(List<BaseMethodBinding<?>> methods) {
this.methods = methods;
this.myMethodBindings = methods;
}
public void addMethod(BaseMethodBinding<?> method) {
this.methods.add(method);
this.myMethodBindings.add(method);
}
@Override

View File

@ -28,6 +28,8 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
@ -220,6 +222,30 @@ public class OperationMethodBinding extends BaseResourceReturningMethodBinding {
}
@Override
public RestOperationTypeEnum getRestOperationType(RequestDetails theRequestDetails) {
RestOperationTypeEnum retVal = super.getRestOperationType(theRequestDetails);
if (retVal == RestOperationTypeEnum.EXTENDED_OPERATION_INSTANCE) {
if (theRequestDetails.getId() == null) {
retVal = RestOperationTypeEnum.EXTENDED_OPERATION_TYPE;
}
}
return retVal;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("name", myName)
.append("methodName", getMethod().getDeclaringClass().getSimpleName() + "." + getMethod().getName())
.append("serverLevel", myCanOperateAtServerLevel)
.append("typeLevel", myCanOperateAtTypeLevel)
.append("instanceLevel", myCanOperateAtInstanceLevel)
.toString();
}
@Override
public Object invokeServer(IRestfulServer<?> theServer, RequestDetails theRequest) throws BaseServerResponseException, IOException {
if (theRequest.getRequestType() == RequestTypeEnum.POST) {

View File

@ -366,9 +366,15 @@ public class StructureMapUtilities {
private static void renderGroup(StringBuilder b, StructureMapGroupComponent g) {
b.append("group ");
switch (g.getTypeMode()) {
case TYPES: b.append("for types");
case TYPEANDTYPES: b.append("for type+types ");
default: // NONE, NULL
case TYPES:
b.append("for types");
break;
case TYPEANDTYPES:
b.append("for type+types ");
break;
case NONE:
case NULL:
break;
}
b.append("for types ");
b.append(g.getName());

View File

@ -6,13 +6,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import org.apache.commons.codec.Charsets;
import org.apache.commons.lang3.StringUtils;
@ -250,7 +244,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
return laterVersion(newParts[i], oldParts[i]);
}
// This should never happen
throw new Error("Delimited versions have exact match for delimiter '"+delimiter+"' : "+newParts+" vs "+oldParts);
throw new Error("Delimited versions have exact match for delimiter '"+delimiter+"' : "+ Arrays.asList(newParts)+" vs "+Arrays.asList(oldParts));
}
protected <T extends MetadataResource> void seeMetadataResource(T r, Map<String, T> map, boolean addId) throws FHIRException {
@ -1045,6 +1039,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
return (T) maps.get(uri);
if (transforms.containsKey(uri))
return (T) transforms.get(uri);
if (questionnaires.containsKey(uri))
return (T) questionnaires.get(uri);
return null;
} else if (class_ == StructureDefinition.class) {
return (T) structures.get(uri);
@ -1052,6 +1048,8 @@ public abstract class BaseWorkerContext implements IWorkerContext {
return (T) valueSets.get(uri);
} else if (class_ == CodeSystem.class) {
return (T) codeSystems.get(uri);
} else if (class_ == ConceptMap.class) {
return (T) maps.get(uri);
} else if (class_ == OperationDefinition.class) {
OperationDefinition od = operations.get(uri);
return (T) od;
@ -1069,7 +1067,7 @@ public abstract class BaseWorkerContext implements IWorkerContext {
}
}
if (class_ == Questionnaire.class)
return null;
return (T) questionnaires.get(uri);
if (class_ == null) {
if (uri.matches(Constants.URI_REGEX) && !uri.contains("ValueSet"))
return null;

View File

@ -1,10 +1,11 @@
package org.hl7.fhir.r4.utils;
//import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.model.api.TemporalPrecisionEnum;
import ca.uhn.fhir.util.ElementUtil;
import org.apache.commons.lang3.NotImplementedException;
import org.apache.http.protocol.ExecutionContext;
import org.fhir.ucum.Decimal;
import org.fhir.ucum.Pair;
import org.fhir.ucum.UcumException;
@ -24,8 +25,13 @@ import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext.FunctionDetails;
import org.hl7.fhir.utilities.Utilities;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import static org.apache.commons.lang3.StringUtils.length;
/**
*
* @author Grahame Grieve
@ -731,7 +737,7 @@ public class FHIRPathEngine {
String s = lexer.take();
if (s.equals("year") || s.equals("years"))
ucum = "a";
else if (s.equals("month") || s.equals("month"))
else if (s.equals("month") || s.equals("months"))
ucum = "mo";
else if (s.equals("week") || s.equals("weeks"))
ucum = "wk";
@ -2072,8 +2078,9 @@ public class FHIRPathEngine {
result.add(item);
} else
getChildrenByName(item, exp.getName(), result);
// todo: GG 1st April 201 - why do this?
if (result.size() == 0 && atEntry && context.appInfo != null) {
// well, we didn't get a match on the name - we'll see if the name matches a constant known by the context.
// (if the name does match, and the user wants to get the constant value, they'll have to try harder...
Base temp = hostServices.resolveConstant(context.appInfo, exp.getName());
if (temp != null) {
result.add(temp);
@ -3280,7 +3287,7 @@ public class FHIRPathEngine {
return Quantity.fromUcum(v, s.substring(1, s.length()-1));
if (s.equals("year") || s.equals("years"))
return Quantity.fromUcum(v, "a");
else if (s.equals("month") || s.equals("month"))
else if (s.equals("month") || s.equals("months"))
return Quantity.fromUcum(v, "mo");
else if (s.equals("week") || s.equals("weeks"))
return Quantity.fromUcum(v, "wk");

View File

@ -97,6 +97,13 @@ public class AuthorizationInterceptorR4Test {
return retVal;
}
private Organization createOrganization(int theIndex) {
Organization retVal = new Organization();
retVal.setId("" + theIndex);
retVal.setName("Org " + theIndex);
return retVal;
}
private Resource createPatient(Integer theId) {
Patient retVal = new Patient();
if (theId != null) {
@ -820,6 +827,118 @@ public class AuthorizationInterceptorR4Test {
}
@Test
public void testOperationAppliesAtAnyLevel() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").atAnyLevel().andThen()
.build();
}
});
HttpGet httpGet;
HttpResponse status;
String response;
// Server
ourHitMethod = false;
ourReturn = Collections.singletonList(createObservation(10, "Patient/2"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
// Type
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
// Instance
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
// Instance Version
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/_history/2/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
}
@Test
public void testOperationAppliesAtAnyLevelWrongOpName() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opNameBadOp").atAnyLevel().andThen()
.build();
}
});
HttpGet httpGet;
HttpResponse status;
String response;
// Server
ourHitMethod = false;
ourReturn = Collections.singletonList(createObservation(10, "Patient/2"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
// Type
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
// Instance
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
// Instance Version
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/_history/2/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
}
@Test
public void testOperationByInstanceOfTypeAllowed() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@ -1158,119 +1277,6 @@ public class AuthorizationInterceptorR4Test {
assertFalse(ourHitMethod);
}
@Test
public void testOperationAppliesAtAnyLevel() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").atAnyLevel().andThen()
.build();
}
});
HttpGet httpGet;
HttpResponse status;
String response;
// Server
ourHitMethod = false;
ourReturn = Collections.singletonList(createObservation(10, "Patient/2"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
// Type
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
// Instance
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
// Instance Version
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/_history/2/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
}
@Test
public void testOperationAppliesAtAnyLevelWrongOpName() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opNameBadOp").atAnyLevel().andThen()
.build();
}
});
HttpGet httpGet;
HttpResponse status;
String response;
// Server
ourHitMethod = false;
ourReturn = Collections.singletonList(createObservation(10, "Patient/2"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
// Type
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
// Instance
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
// Instance Version
ourHitMethod = false;
ourReturn = Collections.singletonList(createPatient(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/_history/2/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
}
@Test
public void testOperationTypeLevel() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@ -1408,6 +1414,64 @@ public class AuthorizationInterceptorR4Test {
assertFalse(ourHitMethod);
}
@Test
public void testOperationTypeLevelWithOperationMethodHavingOptionalIdParam() throws Exception {
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
@Override
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
return new RuleBuilder()
.allow("RULE 1").operation().named("opName").onType(Organization.class).andThen()
.build();
}
});
HttpGet httpGet;
HttpResponse status;
String response;
// Server
ourHitMethod = false;
ourReturn = Collections.singletonList(createObservation(10, "Organization/2"));
httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
assertThat(response, containsString("Access denied by default policy"));
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
// Type
ourHitMethod = false;
ourReturn = Collections.singletonList(createOrganization(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Organization/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertEquals(200, status.getStatusLine().getStatusCode());
assertTrue(ourHitMethod);
// Wrong type
ourHitMethod = false;
ourReturn = Collections.singletonList(createOrganization(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Observation/1/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertThat(response, containsString("Access denied by default policy"));
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
// Instance
ourHitMethod = false;
ourReturn = Collections.singletonList(createOrganization(2));
httpGet = new HttpGet("http://localhost:" + ourPort + "/Organization/1/$opName");
status = ourClient.execute(httpGet);
response = extractResponseAndClose(status);
ourLog.info(response);
assertThat(response, containsString("Access denied by default policy"));
assertEquals(403, status.getStatusLine().getStatusCode());
assertFalse(ourHitMethod);
}
@Test
public void testOperationTypeLevelWithTenant() throws Exception {
ourServlet.setTenantIdentificationStrategy(new UrlBaseTenantIdentificationStrategy());
@ -2480,6 +2544,7 @@ public class AuthorizationInterceptorR4Test {
DummyPatientResourceProvider patProvider = new DummyPatientResourceProvider();
DummyObservationResourceProvider obsProv = new DummyObservationResourceProvider();
DummyOrganizationResourceProvider orgProv = new DummyOrganizationResourceProvider();
DummyEncounterResourceProvider encProv = new DummyEncounterResourceProvider();
DummyCarePlanResourceProvider cpProv = new DummyCarePlanResourceProvider();
PlainProvider plainProvider = new PlainProvider();
@ -2487,7 +2552,7 @@ public class AuthorizationInterceptorR4Test {
ServletHandler proxyHandler = new ServletHandler();
ourServlet = new RestfulServer(ourCtx);
ourServlet.setFhirContext(ourCtx);
ourServlet.setResourceProviders(patProvider, obsProv, encProv, cpProv);
ourServlet.setResourceProviders(patProvider, obsProv, encProv, cpProv, orgProv);
ourServlet.setPlainProviders(plainProvider);
ourServlet.setPagingProvider(new FifoMemoryPagingProvider(100));
ServletHolder servletHolder = new ServletHolder(ourServlet);
@ -2541,6 +2606,25 @@ public class AuthorizationInterceptorR4Test {
}
}
public static class DummyOrganizationResourceProvider implements IResourceProvider {
@Override
public Class<? extends IBaseResource> getResourceType() {
return Organization.class;
}
/**
* This should come before operation1
*/
@Operation(name = "opName", idempotent = true)
public Parameters operation0(@IdParam(optional = true) IdType theId) {
ourHitMethod = true;
return (Parameters) new Parameters().setId("1");
}
}
@SuppressWarnings("unused")
public static class DummyObservationResourceProvider implements IResourceProvider {
@ -2565,14 +2649,20 @@ public class AuthorizationInterceptorR4Test {
return Observation.class;
}
/**
* This should come before operation1
*/
@Operation(name = "opName", idempotent = true)
public Parameters operation() {
public Parameters operation0(@IdParam IdType theId) {
ourHitMethod = true;
return (Parameters) new Parameters().setId("1");
}
/**
* This should come after operation0
*/
@Operation(name = "opName", idempotent = true)
public Parameters operation(@IdParam IdType theId) {
public Parameters operation1() {
ourHitMethod = true;
return (Parameters) new Parameters().setId("1");
}
@ -2670,13 +2760,17 @@ public class AuthorizationInterceptorR4Test {
}
@Operation(name = "opName", idempotent = true)
public Parameters operation() {
public Parameters operation0(@IdParam IdType theId) {
ourHitMethod = true;
return (Parameters) new Parameters().setId("1");
}
/**
* More generic method second to make sure that the
* other method takes precedence
*/
@Operation(name = "opName", idempotent = true)
public Parameters operation(@IdParam IdType theId) {
public Parameters operation1() {
ourHitMethod = true;
return (Parameters) new Parameters().setId("1");
}

27
pom.xml
View File

@ -1538,19 +1538,20 @@
</goals>
</execution>
</executions>
<configuration>
<rules>
<requireMavenVersion>
<version>3.5</version>
</requireMavenVersion>
<requireJavaVersion>
<version>[1.8,)</version>
<message>
The hapi-fhir Maven build requires JDK version 1.8 or higher.
</message>
</requireJavaVersion>
</rules>
</configuration>
<configuration>
<rules>
<requireMavenVersion>
<version>3.3</version>
</requireMavenVersion>
<requireJavaVersion>
<!--<version>[1.8,)</version>-->
<version>1.8</version>
<message>
The hapi-fhir Maven build requires JDK version 1.8.x.
</message>
</requireJavaVersion>
</rules>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>

View File

@ -167,6 +167,11 @@
to occasionally consume two database connections, which could lead to deadlocks
under heavy load. This has been fixed.
</action>
<action type="fix">
AuthorizationInterceptor sometimes incorrectly identified an operation
invocation at the type level as being at the instance level if the method
indicated that the IdParam parameter was optional. This has been fixed.
</action>
</release>
<release version="3.3.0" date="2018-03-29">
<action type="add">