More work on terminology services, and add support to operations to AuthorizationInterceptor
This commit is contained in:
parent
36505c60d8
commit
2e8c20dc83
|
@ -194,7 +194,7 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
|
|||
public void validateServerBaseIfConfiguredToDoSo(String theServerBase, IHttpClient theHttpClient, BaseClient theClient) {
|
||||
String serverBase = normalizeBaseUrlForMap(theServerBase);
|
||||
|
||||
switch (myServerValidationMode) {
|
||||
switch (getServerValidationMode()) {
|
||||
case NEVER:
|
||||
break;
|
||||
case ONCE:
|
||||
|
@ -268,22 +268,6 @@ public abstract class RestfulClientFactory implements IRestfulClientFactory {
|
|||
resetHttpClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new client invocation handler
|
||||
* @param theClient
|
||||
* the client which will invoke the call
|
||||
* @param theUrlBase
|
||||
* the url base
|
||||
* @param theMethodToReturnValue
|
||||
* @param theBindings
|
||||
* @param theMethodToLambda
|
||||
* @return a newly created client invocation handler
|
||||
*/
|
||||
ClientInvocationHandler newInvocationHandler(IHttpClient theClient, String theUrlBase, Map<Method, Object> theMethodToReturnValue, Map<Method, BaseMethodBinding<?>> theBindings, Map<Method, ClientInvocationHandlerFactory.ILambda> theMethodToLambda) {
|
||||
return new ClientInvocationHandler(theClient, getFhirContext(), theUrlBase.toString(), theMethodToReturnValue,
|
||||
theBindings, theMethodToLambda, this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void validateServerBase(String theServerBase, IHttpClient theHttpClient, BaseClient theClient) {
|
||||
|
|
|
@ -340,19 +340,21 @@ public class ResponseHighlighterInterceptor extends InterceptorAdapter {
|
|||
|
||||
b.append("<p>");
|
||||
b.append("This result is being rendered in HTML for easy viewing. ");
|
||||
b.append("You may view this content as ");
|
||||
b.append("You may access this content as ");
|
||||
|
||||
b.append("<a href=\"");
|
||||
b.append(createLinkHref(parameters, Constants.FORMAT_JSON));
|
||||
b.append("\">Raw JSON</a>, ");
|
||||
b.append("\">Raw JSON</a> or ");
|
||||
|
||||
b.append("<a href=\"");
|
||||
b.append(createLinkHref(parameters, Constants.FORMAT_XML));
|
||||
b.append("\">Raw XML</a>, ");
|
||||
|
||||
b.append(" or view this content in ");
|
||||
|
||||
b.append("<a href=\"");
|
||||
b.append(createLinkHref(parameters, Constants.FORMATS_HTML_JSON));
|
||||
b.append("\">HTML JSON</a>, ");
|
||||
b.append("\">HTML JSON</a> ");
|
||||
|
||||
b.append("or ");
|
||||
b.append("<a href=\"");
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor.auth;
|
||||
|
||||
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict;
|
||||
|
||||
abstract class BaseRule implements IAuthRule {
|
||||
private String myName;
|
||||
private PolicyEnum myMode;
|
||||
|
||||
BaseRule(String theRuleName) {
|
||||
myName = theRuleName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
public void setMode(PolicyEnum theRuleMode) {
|
||||
myMode = theRuleMode;
|
||||
}
|
||||
|
||||
Verdict newVerdict() {
|
||||
return new Verdict(myMode, this);
|
||||
}
|
||||
|
||||
public PolicyEnum getMode() {
|
||||
return myMode;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor.auth;
|
||||
|
||||
public interface IAuthRuleBuilderOperation {
|
||||
|
||||
/**
|
||||
* This rule applies to the operation with the given name
|
||||
*
|
||||
* @param The operation name, e.g. "validate" or "$validate" (either form may be used here)
|
||||
*/
|
||||
IAuthRuleBuilderOperationNamed named(String theOperationName);
|
||||
|
||||
/**
|
||||
* This rule applies to any operation
|
||||
*/
|
||||
IAuthRuleBuilderOperationNamed withAnyName();
|
||||
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor.auth;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
public interface IAuthRuleBuilderOperationNamed {
|
||||
|
||||
/**
|
||||
* Rule applies to invocations of this operation at the <code>server</code> level
|
||||
*/
|
||||
IAuthRuleBuilderRuleOpClassifierFinished onServer();
|
||||
|
||||
/**
|
||||
* Rule applies to invocations of this operation at the <code>type</code> level
|
||||
*/
|
||||
IAuthRuleBuilderRuleOpClassifierFinished onType(Class<? extends IBaseResource> theType);
|
||||
|
||||
/**
|
||||
* Rule applies to invocations of this operation at the <code>instance</code> level
|
||||
*/
|
||||
IAuthRuleBuilderRuleOpClassifierFinished onInstance(IIdType theInstanceId);
|
||||
|
||||
}
|
|
@ -55,4 +55,9 @@ public interface IAuthRuleBuilderRule {
|
|||
*/
|
||||
IAuthRuleBuilderRuleOp write();
|
||||
|
||||
/**
|
||||
* This rule applies to a FHIR operation (e.g. <code>$validate</code>)
|
||||
*/
|
||||
IAuthRuleBuilderOperation operation();
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
package ca.uhn.fhir.rest.server.interceptor.auth;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.api.RestOperationTypeEnum;
|
||||
import ca.uhn.fhir.rest.method.RequestDetails;
|
||||
import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor.Verdict;
|
||||
|
||||
class OperationRule extends BaseRule implements IAuthRule {
|
||||
|
||||
public OperationRule(String theRuleName) {
|
||||
super(theRuleName);
|
||||
}
|
||||
|
||||
private String myOperationName;
|
||||
private boolean myAppliesToServer;
|
||||
private HashSet<Class<? extends IBaseResource>> myAppliesToTypes;
|
||||
private List<IIdType> myAppliesToIds;
|
||||
|
||||
/**
|
||||
* Must include the leading $
|
||||
*/
|
||||
public void setOperationName(String theOperationName) {
|
||||
myOperationName = theOperationName;
|
||||
}
|
||||
|
||||
public String getOperationName() {
|
||||
return myOperationName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Verdict applyRule(RestOperationTypeEnum theOperation, RequestDetails theRequestDetails, IBaseResource theInputResource, IBaseResource theOutputResource, IRuleApplier theRuleApplier) {
|
||||
FhirContext ctx = theRequestDetails.getServer().getFhirContext();
|
||||
|
||||
boolean applies = false;
|
||||
switch (theOperation) {
|
||||
case EXTENDED_OPERATION_SERVER:
|
||||
if (myAppliesToServer) {
|
||||
applies = true;
|
||||
}
|
||||
break;
|
||||
case EXTENDED_OPERATION_TYPE:
|
||||
if (myAppliesToTypes != null) {
|
||||
for (Class<? extends IBaseResource> next : myAppliesToTypes) {
|
||||
String resName = ctx.getResourceDefinition(theRequestDetails.getResourceName()).getName();
|
||||
if (resName.equals(theRequestDetails.getResourceName())) {
|
||||
applies = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EXTENDED_OPERATION_INSTANCE:
|
||||
if (myAppliesToIds != null) {
|
||||
String instanceId = theRequestDetails.getId().toUnqualifiedVersionless().getValue();
|
||||
for (IIdType next : myAppliesToIds) {
|
||||
if (next.toUnqualifiedVersionless().getValue().equals(instanceId)) {
|
||||
applies = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!applies) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (myOperationName != null && !myOperationName.equals(theRequestDetails.getOperation())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return newVerdict();
|
||||
}
|
||||
|
||||
public void appliesToServer() {
|
||||
myAppliesToServer = true;
|
||||
}
|
||||
|
||||
public void appliesToTypes(HashSet<Class<? extends IBaseResource>> theAppliesToTypes) {
|
||||
myAppliesToTypes = theAppliesToTypes;
|
||||
}
|
||||
|
||||
public void appliesToInstances(List<IIdType> theAppliesToIds) {
|
||||
myAppliesToIds = theAppliesToIds;
|
||||
}
|
||||
|
||||
}
|
|
@ -24,7 +24,6 @@ import java.util.Collection;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
@ -40,20 +39,18 @@ import ca.uhn.fhir.util.BundleUtil;
|
|||
import ca.uhn.fhir.util.BundleUtil.BundleEntryParts;
|
||||
import ca.uhn.fhir.util.FhirTerser;
|
||||
|
||||
class Rule implements IAuthRule {
|
||||
class Rule extends BaseRule implements IAuthRule {
|
||||
|
||||
private AppliesTypeEnum myAppliesTo;
|
||||
private Set<?> myAppliesToTypes;
|
||||
private String myClassifierCompartmentName;
|
||||
private Collection<? extends IIdType> myClassifierCompartmentOwners;
|
||||
private ClassifierTypeEnum myClassifierType;
|
||||
private PolicyEnum myMode;
|
||||
private String myName;
|
||||
private RuleOpEnum myOp;
|
||||
private TransactionAppliesToEnum myTransactionAppliesToOp;
|
||||
|
||||
public Rule(String theRuleName) {
|
||||
myName = theRuleName;
|
||||
super(theRuleName);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,7 +74,7 @@ class Rule implements IAuthRule {
|
|||
case DELETE:
|
||||
if (theOperation == RestOperationTypeEnum.DELETE) {
|
||||
if (theInputResource == null) {
|
||||
return new Verdict(myMode, this);
|
||||
return newVerdict();
|
||||
} else {
|
||||
appliesTo = theInputResource;
|
||||
}
|
||||
|
@ -88,7 +85,7 @@ class Rule implements IAuthRule {
|
|||
case BATCH:
|
||||
case TRANSACTION:
|
||||
if (theInputResource != null && requestAppliesToTransaction(ctx, myOp, theInputResource)) {
|
||||
if (myMode == PolicyEnum.DENY) {
|
||||
if (getMode() == PolicyEnum.DENY) {
|
||||
return new Verdict(PolicyEnum.DENY, this);
|
||||
} else {
|
||||
List<BundleEntryParts> inputResources = BundleUtil.toListOfEntries(ctx, (IBaseBundle) theInputResource);
|
||||
|
@ -155,7 +152,7 @@ class Rule implements IAuthRule {
|
|||
return new Verdict(PolicyEnum.DENY, this);
|
||||
case METADATA:
|
||||
if (theOperation == RestOperationTypeEnum.METADATA) {
|
||||
return new Verdict(myMode, this);
|
||||
return newVerdict();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
@ -196,9 +193,10 @@ class Rule implements IAuthRule {
|
|||
throw new IllegalStateException("Unable to apply security to event of applies to type " + myAppliesTo);
|
||||
}
|
||||
|
||||
return new Verdict(myMode, this);
|
||||
return newVerdict();
|
||||
}
|
||||
|
||||
|
||||
private boolean requestAppliesToTransaction(FhirContext theContext, RuleOpEnum theOp, IBaseResource theInputResource) {
|
||||
if (!"Bundle".equals(theContext.getResourceDefinition(theInputResource).getName())) {
|
||||
return false;
|
||||
|
@ -216,11 +214,6 @@ class Rule implements IAuthRule {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
public TransactionAppliesToEnum getTransactionAppliesToOp() {
|
||||
return myTransactionAppliesToOp;
|
||||
}
|
||||
|
@ -245,9 +238,6 @@ class Rule implements IAuthRule {
|
|||
myClassifierType = theClassifierType;
|
||||
}
|
||||
|
||||
public void setMode(PolicyEnum theRuleMode) {
|
||||
myMode = theRuleMode;
|
||||
}
|
||||
|
||||
public Rule setOp(RuleOpEnum theRuleOp) {
|
||||
myOp = theRuleOp;
|
||||
|
@ -257,4 +247,5 @@ class Rule implements IAuthRule {
|
|||
public void setTransactionAppliesToOp(TransactionAppliesToEnum theOp) {
|
||||
myTransactionAppliesToOp = theOp;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import java.util.ArrayList;
|
|||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -243,6 +244,80 @@ public class RuleBuilder implements IAuthRuleBuilder {
|
|||
|
||||
}
|
||||
|
||||
private class RuleBuilderRuleOperation implements IAuthRuleBuilderOperation {
|
||||
|
||||
private class RuleBuilderRuleOperationNamed implements IAuthRuleBuilderOperationNamed {
|
||||
|
||||
private String myOperationName;
|
||||
|
||||
public RuleBuilderRuleOperationNamed(String theOperationName) {
|
||||
if (theOperationName != null && !theOperationName.startsWith("$")) {
|
||||
myOperationName = '$' + theOperationName;
|
||||
} else {
|
||||
myOperationName = theOperationName;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAuthRuleBuilderRuleOpClassifierFinished onServer() {
|
||||
OperationRule rule = createRule();
|
||||
rule.appliesToServer();
|
||||
myRules.add(rule);
|
||||
return new RuleBuilderFinished();
|
||||
}
|
||||
|
||||
private OperationRule createRule() {
|
||||
OperationRule rule = new OperationRule(myRuleName);
|
||||
rule.setOperationName(myOperationName);
|
||||
rule.setMode(myRuleMode);
|
||||
return rule;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAuthRuleBuilderRuleOpClassifierFinished onType(Class<? extends IBaseResource> theType) {
|
||||
Validate.notNull(theType, "theType must not be null");
|
||||
|
||||
OperationRule rule = createRule();
|
||||
HashSet<Class<? extends IBaseResource>> appliesToTypes = new HashSet<Class<? extends IBaseResource>>();
|
||||
appliesToTypes.add(theType);
|
||||
rule.appliesToTypes(appliesToTypes);
|
||||
myRules.add(rule);
|
||||
return new RuleBuilderFinished();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAuthRuleBuilderRuleOpClassifierFinished onInstance(IIdType theInstanceId) {
|
||||
Validate.notNull(theInstanceId, "theInstanceId must not be null");
|
||||
Validate.notBlank(theInstanceId.getResourceType(), "theInstanceId does not have a resource type");
|
||||
Validate.notBlank(theInstanceId.getIdPart(), "theInstanceId does not have an ID part");
|
||||
|
||||
OperationRule rule = createRule();
|
||||
ArrayList<IIdType> ids = new ArrayList<IIdType>();
|
||||
ids.add(theInstanceId);
|
||||
rule.appliesToInstances(ids);
|
||||
myRules.add(rule);
|
||||
return new RuleBuilderFinished();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAuthRuleBuilderOperationNamed named(String theOperationName) {
|
||||
Validate.notBlank(theOperationName, "theOperationName must not be null or empty");
|
||||
return new RuleBuilderRuleOperationNamed(theOperationName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAuthRuleBuilderOperationNamed withAnyName() {
|
||||
return new RuleBuilderRuleOperationNamed(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public IAuthRuleBuilderOperation operation() {
|
||||
return new RuleBuilderRuleOperation();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,5 +28,6 @@ enum RuleOpEnum {
|
|||
TRANSACTION,
|
||||
METADATA,
|
||||
BATCH,
|
||||
DELETE
|
||||
DELETE,
|
||||
OPERATION
|
||||
}
|
||||
|
|
|
@ -1656,10 +1656,6 @@ public class XmlUtil {
|
|||
ourHaveLoggedStaxImplementation = true;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws FactoryConfigurationError, XMLStreamException {
|
||||
createXmlWriter(new StringWriter());
|
||||
}
|
||||
|
||||
private static final class ExtendedEntityReplacingXmlResolver implements XMLResolver {
|
||||
@Override
|
||||
public Object resolveEntity(String thePublicID, String theSystemID, String theBaseURI, String theNamespace) throws XMLStreamException {
|
||||
|
|
|
@ -26,7 +26,7 @@ public class FhirDbConfig {
|
|||
extraProperties.put("hibernate.search.default.directory_provider", "filesystem");
|
||||
extraProperties.put("hibernate.search.default.indexBase", "target/lucenefiles");
|
||||
extraProperties.put("hibernate.search.lucene_version", "LUCENE_CURRENT");
|
||||
// extraProperties.put("hibernate.search.default.worker.execution", "async");
|
||||
extraProperties.put("hibernate.search.default.worker.execution", "async");
|
||||
|
||||
if (System.getProperty("lowmem") != null) {
|
||||
extraProperties.put("hibernate.search.autoregister_listeners", "false");
|
||||
|
|
|
@ -51,7 +51,7 @@ public class DaoConfig {
|
|||
// ***
|
||||
// update setter javadoc if default changes
|
||||
// ***
|
||||
private int myDeferIndexingForCodesystemsOfSize = 100;
|
||||
private int myDeferIndexingForCodesystemsOfSize = 2000;
|
||||
// ***
|
||||
// update setter javadoc if default changes
|
||||
// ***
|
||||
|
@ -90,7 +90,7 @@ public class DaoConfig {
|
|||
* the code system will be indexed later in an incremental process in order to
|
||||
* avoid overwhelming Lucene with a huge number of codes in a single operation.
|
||||
* <p>
|
||||
* Defaults to 100
|
||||
* Defaults to 2000
|
||||
* </p>
|
||||
*/
|
||||
public int getDeferIndexingForCodesystemsOfSize() {
|
||||
|
@ -273,7 +273,7 @@ public class DaoConfig {
|
|||
* the code system will be indexed later in an incremental process in order to
|
||||
* avoid overwhelming Lucene with a huge number of codes in a single operation.
|
||||
* <p>
|
||||
* Defaults to 100
|
||||
* Defaults to 2000
|
||||
* </p>
|
||||
*/
|
||||
public void setDeferIndexingForCodesystemsOfSize(int theDeferIndexingForCodesystemsOfSize) {
|
||||
|
|
|
@ -55,14 +55,12 @@ import org.hibernate.search.annotations.Field;
|
|||
import org.hibernate.search.annotations.Fields;
|
||||
import org.hibernate.search.annotations.Indexed;
|
||||
import org.hibernate.search.annotations.Store;
|
||||
import org.hibernate.search.indexes.interceptor.DontInterceptEntityInterceptor;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
|
||||
import ca.uhn.fhir.jpa.search.DeferConceptIndexingInterceptor;
|
||||
|
||||
//@formatter:off
|
||||
@Entity
|
||||
@Indexed(interceptor=DeferConceptIndexingInterceptor.class)
|
||||
@Indexed()
|
||||
@Table(name="TRM_CONCEPT", uniqueConstraints= {
|
||||
@UniqueConstraint(name="IDX_CONCEPT_CS_CODE", columnNames= {"CODESYSTEM_PID", "CODE"})
|
||||
}, indexes= {
|
||||
|
|
|
@ -66,6 +66,10 @@ public class TermConceptParentChildLink implements Serializable {
|
|||
return myChild;
|
||||
}
|
||||
|
||||
public RelationshipTypeEnum getRelationshipType() {
|
||||
return myRelationshipType;
|
||||
}
|
||||
|
||||
public TermCodeSystemVersion getCodeSystem() {
|
||||
return myCodeSystem;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
@ -85,7 +84,9 @@ public class TerminologyUploaderProviderDstu3 extends BaseJpaProvider {
|
|||
} else if (thePackage == null || thePackage.getData() == null || thePackage.getData().length == 0) {
|
||||
throw new InvalidRequestException("No 'localfile' or 'package' parameter, or package had no data");
|
||||
} else {
|
||||
data = Arrays.asList(thePackage.getData());
|
||||
data = new ArrayList<byte[]>();
|
||||
data.add(thePackage.getData());
|
||||
thePackage.setData(null);
|
||||
}
|
||||
|
||||
String url = theUrl != null ? theUrl.getValueAsString() : null;
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
package ca.uhn.fhir.jpa.search;
|
||||
|
||||
import org.hibernate.search.indexes.interceptor.EntityIndexingInterceptor;
|
||||
import org.hibernate.search.indexes.interceptor.IndexingOverride;
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
|
||||
public class DeferConceptIndexingInterceptor implements EntityIndexingInterceptor<TermConcept> {
|
||||
|
||||
@Override
|
||||
public IndexingOverride onAdd(TermConcept theEntity) {
|
||||
if (theEntity.getIndexStatus() == null) {
|
||||
return IndexingOverride.SKIP;
|
||||
}
|
||||
return IndexingOverride.APPLY_DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexingOverride onUpdate(TermConcept theEntity) {
|
||||
return onAdd(theEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexingOverride onDelete(TermConcept theEntity) {
|
||||
return IndexingOverride.APPLY_DEFAULT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IndexingOverride onCollectionUpdate(TermConcept theEntity) {
|
||||
return IndexingOverride.APPLY_DEFAULT;
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ import javax.persistence.PersistenceContext;
|
|||
import javax.persistence.PersistenceContextType;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
@ -67,9 +68,13 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
|
|||
@Autowired
|
||||
protected ITermConceptDao myConceptDao;
|
||||
|
||||
private List<TermConceptParentChildLink> myConceptLinksToSaveLater = new ArrayList<TermConceptParentChildLink>();
|
||||
|
||||
@Autowired
|
||||
private ITermConceptParentChildLinkDao myConceptParentChildLinkDao;
|
||||
|
||||
private List<TermConcept> myConceptsToSaveLater = new ArrayList<TermConcept>();
|
||||
|
||||
@Autowired
|
||||
protected FhirContext myContext;
|
||||
|
||||
|
@ -79,6 +84,8 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
|
|||
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
|
||||
protected EntityManager myEntityManager;
|
||||
|
||||
private boolean myProcessDeferred = true;
|
||||
|
||||
private boolean addToSet(Set<TermConcept> theSetToPopulate, TermConcept theConcept) {
|
||||
boolean retVal = theSetToPopulate.add(theConcept);
|
||||
if (retVal) {
|
||||
|
@ -197,39 +204,103 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
|
|||
TermCodeSystemVersion csv = cs.getCurrentVersion();
|
||||
return csv;
|
||||
}
|
||||
|
||||
private TermCodeSystem getCodeSystem(String theSystem) {
|
||||
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(theSystem);
|
||||
return cs;
|
||||
}
|
||||
|
||||
private void parentPids(TermConcept theNextConcept, Set<Long> theParentPids) {
|
||||
for (TermConceptParentChildLink nextParentLink : theNextConcept.getParents()){
|
||||
TermConcept parent = nextParentLink.getParent();
|
||||
if (parent != null && theParentPids.add(parent.getId())) {
|
||||
parentPids(parent, theParentPids);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void persistChildren(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack, int theTotalConcepts) {
|
||||
if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (theConceptsStack.size() % 1000 == 0) {
|
||||
if (theConceptsStack.size() == 1 || theConceptsStack.size() % 10000 == 0) {
|
||||
float pct = (float) theConceptsStack.size() / (float) theTotalConcepts;
|
||||
ourLog.info("Have saved {}/{} concepts ({}%), flushing", theConceptsStack.size(), theTotalConcepts, (int)( pct*100.0f));
|
||||
myConceptDao.flush();
|
||||
myConceptParentChildLinkDao.flush();
|
||||
ourLog.info("Have processed {}/{} concepts ({}%)", theConceptsStack.size(), theTotalConcepts, (int)( pct*100.0f));
|
||||
}
|
||||
|
||||
theConcept.setCodeSystem(theCodeSystem);
|
||||
if (theTotalConcepts < myDaoConfig.getDeferIndexingForCodesystemsOfSize()) {
|
||||
theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED);
|
||||
|
||||
Set<Long> parentPids = new HashSet<Long>();
|
||||
parentPids(theConcept, parentPids);
|
||||
theConcept.setParentPids(parentPids);
|
||||
|
||||
if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) {
|
||||
myConceptDao.save(theConcept);
|
||||
} else {
|
||||
myConceptsToSaveLater.add(theConcept);
|
||||
}
|
||||
|
||||
myConceptDao.save(theConcept);
|
||||
for (TermConceptParentChildLink next : theConcept.getChildren()) {
|
||||
persistChildren(next.getChild(), theCodeSystem, theConceptsStack, theTotalConcepts);
|
||||
}
|
||||
|
||||
for (TermConceptParentChildLink next : theConcept.getChildren()) {
|
||||
if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) {
|
||||
myConceptParentChildLinkDao.save(next);
|
||||
} else {
|
||||
myConceptLinksToSaveLater.add(next);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) {
|
||||
if (theNext.getCodeSystem() != null) {
|
||||
return;
|
||||
}
|
||||
theNext.setCodeSystem(theCodeSystemVersion);
|
||||
for (TermConceptParentChildLink next : theNext.getChildren()) {
|
||||
populateVersion(next.getChild(), theCodeSystemVersion);
|
||||
}
|
||||
}
|
||||
|
||||
@Scheduled(fixedRate=5000)
|
||||
@Transactional(propagation=Propagation.REQUIRED)
|
||||
@Override
|
||||
public synchronized void saveDeferred() {
|
||||
if (!myProcessDeferred || ((myConceptsToSaveLater.isEmpty() && myConceptLinksToSaveLater.isEmpty()))) {
|
||||
return;
|
||||
}
|
||||
|
||||
int codeCount = 0, relCount = 0;
|
||||
|
||||
int count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptsToSaveLater.size());
|
||||
ourLog.info("Saving {} deferred concepts...", count);
|
||||
while (codeCount < count && myConceptsToSaveLater.size() > 0) {
|
||||
TermConcept next = myConceptsToSaveLater.remove(0);
|
||||
myConceptDao.save(next);
|
||||
codeCount++;
|
||||
}
|
||||
|
||||
if (codeCount == 0) {
|
||||
count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptLinksToSaveLater.size());
|
||||
ourLog.info("Saving {} deferred concept relationships...", count);
|
||||
while (relCount < count && myConceptLinksToSaveLater.size() > 0) {
|
||||
TermConceptParentChildLink next = myConceptLinksToSaveLater.remove(0);
|
||||
myConceptParentChildLinkDao.save(next);
|
||||
relCount++;
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.info("Saved {} deferred concepts ({} remain) and {} deferred relationships ({} remain)", new Object[] {codeCount, myConceptsToSaveLater.size(), relCount, myConceptLinksToSaveLater.size()});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProcessDeferred(boolean theProcessDeferred) {
|
||||
myProcessDeferred = theProcessDeferred;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRED)
|
||||
public void storeNewCodeSystemVersion(Long theCodeSystemResourcePid, String theSystemUri, TermCodeSystemVersion theCodeSystemVersion) {
|
||||
|
@ -265,7 +336,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
|
|||
ourLog.info("Validating all codes in CodeSystem for storage (this can take some time for large sets)");
|
||||
|
||||
// Validate the code system
|
||||
IdentityHashMap<TermConcept, Object> conceptsStack = new IdentityHashMap<TermConcept, Object>();
|
||||
ArrayList<String> conceptsStack = new ArrayList<String>();
|
||||
IdentityHashMap<TermConcept, Object> allConcepts = new IdentityHashMap<TermConcept, Object>();
|
||||
int totalCodeCount = 0;
|
||||
for (TermConcept next : theCodeSystemVersion.getConcepts()) {
|
||||
|
@ -281,11 +352,17 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
|
|||
codeSystem.setCurrentVersion(theCodeSystemVersion);
|
||||
codeSystem = myCodeSystemDao.saveAndFlush(codeSystem);
|
||||
|
||||
ourLog.info("Setting codesystemversion on {} concepts...", totalCodeCount);
|
||||
|
||||
for (TermConcept next : theCodeSystemVersion.getConcepts()) {
|
||||
populateVersion(next, codeSystemVersion);
|
||||
}
|
||||
|
||||
ourLog.info("Saving {} concepts...", totalCodeCount);
|
||||
|
||||
conceptsStack = new IdentityHashMap<TermConcept, Object>();
|
||||
IdentityHashMap<TermConcept, Object> conceptsStack2 = new IdentityHashMap<TermConcept, Object>();
|
||||
for (TermConcept next : theCodeSystemVersion.getConcepts()) {
|
||||
persistChildren(next, codeSystemVersion, conceptsStack, totalCodeCount);
|
||||
persistChildren(next, codeSystemVersion, conceptsStack2, totalCodeCount);
|
||||
}
|
||||
|
||||
ourLog.info("Done saving concepts, flushing to database");
|
||||
|
@ -293,27 +370,6 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
|
|||
myConceptDao.flush();
|
||||
myConceptParentChildLinkDao.flush();
|
||||
|
||||
ourLog.info("Building multi-axial hierarchy...");
|
||||
|
||||
int index = 0;
|
||||
int totalParents = 0;
|
||||
for (TermConcept nextConcept : conceptsStack.keySet()) {
|
||||
|
||||
if (index++ % 1000 == 0) {
|
||||
float pct = (float) index / (float) totalCodeCount;
|
||||
ourLog.info("Have built hierarchy for {}/{} concepts - {}%", index, totalCodeCount, (int)( pct*100.0f));
|
||||
}
|
||||
|
||||
Set<Long> parentPids = new HashSet<Long>();
|
||||
parentPids(nextConcept, parentPids);
|
||||
nextConcept.setParentPids(parentPids);
|
||||
totalParents += parentPids.size();
|
||||
|
||||
myConceptDao.save(nextConcept);
|
||||
}
|
||||
|
||||
ourLog.info("Done building hierarchy, found {} parents", totalParents);
|
||||
|
||||
/*
|
||||
* For now we always delete old versions.. At some point it would be nice to allow configuration to keep old versions
|
||||
*/
|
||||
|
@ -326,14 +382,9 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
|
|||
}
|
||||
|
||||
ourLog.info("Done deleting old code system versions");
|
||||
}
|
||||
|
||||
private void parentPids(TermConcept theNextConcept, Set<Long> theParentPids) {
|
||||
for (TermConceptParentChildLink nextParentLink : theNextConcept.getParents()){
|
||||
TermConcept parent = nextParentLink.getParent();
|
||||
if (parent != null && theParentPids.add(parent.getId())) {
|
||||
parentPids(parent, theParentPids);
|
||||
}
|
||||
if (myConceptsToSaveLater.size() > 0 || myConceptLinksToSaveLater.size() > 0) {
|
||||
ourLog.info("Note that some concept saving was deferred - still have {} concepts and {} relationships", myConceptsToSaveLater.size(), myConceptLinksToSaveLater.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -351,15 +402,16 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
private int validateConceptForStorage(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap<TermConcept, Object> theConceptsStack,
|
||||
private int validateConceptForStorage(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, ArrayList<String> theConceptsStack,
|
||||
IdentityHashMap<TermConcept, Object> theAllConcepts) {
|
||||
ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystem() != null, "CodesystemValue is null");
|
||||
ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystem() == theCodeSystem, "CodeSystems are not equal");
|
||||
ValidateUtil.isNotBlankOrThrowInvalidRequest(theConcept.getCode(), "Codesystem contains a code with no code value");
|
||||
|
||||
if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) {
|
||||
if (theConceptsStack.contains(theConcept.getCode())) {
|
||||
throw new InvalidRequestException("CodeSystem contains circular reference around code " + theConcept.getCode());
|
||||
}
|
||||
theConceptsStack.add(theConcept.getCode());
|
||||
|
||||
int retVal = 0;
|
||||
if (theAllConcepts.put(theConcept, theAllConcepts) == null) {
|
||||
|
@ -374,7 +426,7 @@ public abstract class BaseHapiTerminologySvc implements IHapiTerminologySvc {
|
|||
retVal += validateConceptForStorage(next.getChild(), theCodeSystem, theConceptsStack, theAllConcepts);
|
||||
}
|
||||
|
||||
theConceptsStack.remove(theConcept);
|
||||
theConceptsStack.remove(theConceptsStack.size() - 1);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
|
|
@ -48,4 +48,12 @@ public interface IHapiTerminologySvc {
|
|||
|
||||
List<TermConcept> findCodes(String theSystem);
|
||||
|
||||
void saveDeferred();
|
||||
|
||||
/**
|
||||
* This is mostly for unit tests - we can disable processing of deferred concepts
|
||||
* by changing this flag
|
||||
*/
|
||||
void setProcessDeferred(boolean theProcessDeferred);
|
||||
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ import java.io.OutputStream;
|
|||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
|
@ -262,11 +263,18 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
|
|||
|
||||
ourLog.info("Have {} total concepts, {} root concepts", code2concept.size(), codeSystemVersion.getConcepts().size());
|
||||
|
||||
myTermSvc.storeNewCodeSystemVersion(LOINC_URL, codeSystemVersion, theRequestDetails);
|
||||
String url = LOINC_URL;
|
||||
storeCodeSystem(theRequestDetails, codeSystemVersion, url);
|
||||
|
||||
return new UploadStatistics(code2concept.size());
|
||||
}
|
||||
|
||||
private void storeCodeSystem(RequestDetails theRequestDetails, final TermCodeSystemVersion codeSystemVersion, String url) {
|
||||
myTermSvc.setProcessDeferred(false);
|
||||
myTermSvc.storeNewCodeSystemVersion(url, codeSystemVersion, theRequestDetails);
|
||||
myTermSvc.setProcessDeferred(true);
|
||||
}
|
||||
|
||||
UploadStatistics processSnomedCtFiles(List<byte[]> theZipBytes, RequestDetails theRequestDetails) {
|
||||
final TermCodeSystemVersion codeSystemVersion = new TermCodeSystemVersion();
|
||||
final Map<String, TermConcept> id2concept = new HashMap<String, TermConcept>();
|
||||
|
@ -289,6 +297,13 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
|
|||
|
||||
theZipBytes.clear();
|
||||
|
||||
ourLog.info("Looking for root codes");
|
||||
for (Iterator<Entry<String, TermConcept>> iter = rootConcepts.entrySet().iterator(); iter.hasNext(); ) {
|
||||
if (iter.next().getValue().getParents().isEmpty() == false) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
ourLog.info("Done loading SNOMED CT files - {} root codes, {} total codes", rootConcepts.size(), code2concept.size());
|
||||
|
||||
Counter circularCounter = new Counter();
|
||||
|
@ -300,7 +315,8 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
|
|||
}
|
||||
|
||||
codeSystemVersion.getConcepts().addAll(rootConcepts.values());
|
||||
myTermSvc.storeNewCodeSystemVersion(SCT_URL, codeSystemVersion, theRequestDetails);
|
||||
String url = SCT_URL;
|
||||
storeCodeSystem(theRequestDetails, codeSystemVersion, url);
|
||||
|
||||
return new UploadStatistics(code2concept.size());
|
||||
}
|
||||
|
@ -470,23 +486,33 @@ public class TerminologyLoaderSvc implements IHapiTerminologyLoaderSvc {
|
|||
String destinationId = theRecord.get("destinationId");
|
||||
String typeId = theRecord.get("typeId");
|
||||
boolean active = "1".equals(theRecord.get("active"));
|
||||
if (!active) {
|
||||
return;
|
||||
}
|
||||
|
||||
TermConcept typeConcept = myCode2concept.get(typeId);
|
||||
TermConcept sourceConcept = myCode2concept.get(sourceId);
|
||||
TermConcept targetConcept = myCode2concept.get(destinationId);
|
||||
if (sourceConcept != null && targetConcept != null && typeConcept != null) {
|
||||
if (typeConcept.getDisplay().equals("Is a (attribute)")) {
|
||||
RelationshipTypeEnum relationshipType = RelationshipTypeEnum.ISA;
|
||||
if (!sourceId.equals(destinationId)) {
|
||||
if (active) {
|
||||
TermConceptParentChildLink link = new TermConceptParentChildLink();
|
||||
link.setChild(sourceConcept);
|
||||
link.setParent(targetConcept);
|
||||
link.setRelationshipType(TermConceptParentChildLink.RelationshipTypeEnum.ISA);
|
||||
link.setRelationshipType(relationshipType);
|
||||
link.setCodeSystem(myCodeSystemVersion);
|
||||
myRootConcepts.remove(link.getChild().getCode());
|
||||
|
||||
targetConcept.addChild(sourceConcept, RelationshipTypeEnum.ISA);
|
||||
targetConcept.addChild(sourceConcept, relationshipType);
|
||||
} else {
|
||||
// not active, so we're removing any existing links
|
||||
for (TermConceptParentChildLink next : new ArrayList<TermConceptParentChildLink>(targetConcept.getChildren())) {
|
||||
if (next.getRelationshipType() == relationshipType) {
|
||||
if (next.getChild().getCode().equals(sourceConcept.getCode())) {
|
||||
next.getParent().getChildren().remove(next);
|
||||
next.getChild().getParents().remove(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (ignoredTypes.contains(typeConcept.getDisplay())) {
|
||||
// ignore
|
||||
|
|
|
@ -344,6 +344,8 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
|
|||
public void testIndexingIsDeferredForLargeCodeSystems() {
|
||||
myDaoConfig.setDeferIndexingForCodesystemsOfSize(1);
|
||||
|
||||
myTermSvc.setProcessDeferred(false);
|
||||
|
||||
createExternalCsAndLocalVs();
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
|
@ -353,10 +355,25 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
|
|||
include.addFilter().setProperty("display").setOp(FilterOperator.ISA).setValue("ParentA");
|
||||
|
||||
ValueSet result = myValueSetDao.expand(vs, null);
|
||||
|
||||
String encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
|
||||
ourLog.info(encoded);
|
||||
|
||||
assertEquals(0, result.getExpansion().getContains().size());
|
||||
|
||||
myTermSvc.setProcessDeferred(true);
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
|
||||
result = myValueSetDao.expand(vs, null);
|
||||
encoded = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(result);
|
||||
ourLog.info(encoded);
|
||||
assertEquals(4, result.getExpansion().getContains().size());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.term;
|
|||
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInRelativeOrder;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
@ -12,8 +13,10 @@ import static org.mockito.Mockito.verify;
|
|||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
@ -62,18 +65,18 @@ public class TerminologyLoaderSvcTest {
|
|||
public void testLoadLoinc() throws Exception {
|
||||
ByteArrayOutputStream bos1 = new ByteArrayOutputStream();
|
||||
ZipOutputStream zos1 = new ZipOutputStream(bos1);
|
||||
addEntry(zos1,"/loinc/", "loinc.csv");
|
||||
addEntry(zos1, "/loinc/", "loinc.csv");
|
||||
zos1.close();
|
||||
ourLog.info("ZIP file has {} bytes", bos1.toByteArray().length);
|
||||
|
||||
ByteArrayOutputStream bos2 = new ByteArrayOutputStream();
|
||||
ZipOutputStream zos2 = new ZipOutputStream(bos2);
|
||||
addEntry(zos2,"/loinc/", "LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV");
|
||||
addEntry(zos2, "/loinc/", "LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV");
|
||||
zos2.close();
|
||||
ourLog.info("ZIP file has {} bytes", bos2.toByteArray().length);
|
||||
|
||||
RequestDetails details = mock(RequestDetails.class);
|
||||
mySvc.loadLoinc(Arrays.asList(bos1.toByteArray(), bos2.toByteArray()), details);
|
||||
mySvc.loadLoinc(list(bos1.toByteArray(), bos2.toByteArray()), details);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -84,38 +87,48 @@ public class TerminologyLoaderSvcTest {
|
|||
addEntry(zos, "/sct/", "sct2_Concept_Full-en_INT_20160131.txt");
|
||||
addEntry(zos, "/sct/", "sct2_Description_Full-en_INT_20160131.txt");
|
||||
addEntry(zos, "/sct/", "sct2_Identifier_Full_INT_20160131.txt");
|
||||
addEntry(zos,"/sct/", "sct2_Relationship_Full_INT_20160131.txt");
|
||||
addEntry(zos,"/sct/", "sct2_StatedRelationship_Full_INT_20160131.txt");
|
||||
addEntry(zos, "/sct/", "sct2_Relationship_Full_INT_20160131.txt");
|
||||
addEntry(zos, "/sct/", "sct2_StatedRelationship_Full_INT_20160131.txt");
|
||||
addEntry(zos, "/sct/", "sct2_TextDefinition_Full-en_INT_20160131.txt");
|
||||
zos.close();
|
||||
|
||||
ourLog.info("ZIP file has {} bytes", bos.toByteArray().length);
|
||||
|
||||
RequestDetails details = mock(RequestDetails.class);
|
||||
mySvc.loadSnomedCt(Collections.singletonList(bos.toByteArray()), details);
|
||||
mySvc.loadSnomedCt(list(bos.toByteArray()), details);
|
||||
|
||||
verify(myTermSvc).storeNewCodeSystemVersion(any(String.class), myCsvCaptor.capture(), any(RequestDetails.class));
|
||||
|
||||
TermCodeSystemVersion csv = myCsvCaptor.getValue();
|
||||
TreeSet<String> allCodes = toCodes(csv);
|
||||
TreeSet<String> allCodes = toCodes(csv, true);
|
||||
ourLog.info(allCodes.toString());
|
||||
|
||||
assertThat(allCodes, containsInRelativeOrder("116680003"));
|
||||
assertThat(allCodes, not(containsInRelativeOrder("207527008")));
|
||||
|
||||
allCodes = toCodes(csv, false);
|
||||
ourLog.info(allCodes.toString());
|
||||
assertThat(allCodes, hasItem("126816002"));
|
||||
}
|
||||
|
||||
private TreeSet<String> toCodes(TermCodeSystemVersion theCsv) {
|
||||
private List<byte[]> list(byte[]... theByteArray) {
|
||||
return new ArrayList<byte[]>(Arrays.asList(theByteArray));
|
||||
}
|
||||
|
||||
private TreeSet<String> toCodes(TermCodeSystemVersion theCsv, boolean theAddChildren) {
|
||||
TreeSet<String> retVal = new TreeSet<String>();
|
||||
for (TermConcept next : theCsv.getConcepts()) {
|
||||
toCodes(retVal, next);
|
||||
toCodes(retVal, next, theAddChildren);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
private void toCodes(TreeSet<String> theCodes, TermConcept theConcept) {
|
||||
private void toCodes(TreeSet<String> theCodes, TermConcept theConcept, boolean theAddChildren) {
|
||||
theCodes.add(theConcept.getCode());
|
||||
if (theAddChildren) {
|
||||
for (TermConceptParentChildLink next : theConcept.getChildren()) {
|
||||
toCodes(theCodes, next.getChild());
|
||||
toCodes(theCodes, next.getChild(), theAddChildren);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,5 +159,4 @@ public class TerminologyLoaderSvcTest {
|
|||
zos.closeEntry();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ id effectiveTime active moduleId definitionStatusId
|
|||
126813005 20020131 1 900000000000207008 900000000000074008
|
||||
126813006 20020131 1 900000000000207008 900000000000074008
|
||||
126817006 20020131 1 900000000000207008 900000000000074008
|
||||
126816002 20020131 1 900000000000207008 900000000000074008
|
||||
207527008 20020131 1 900000000000207008 900000000000074008
|
||||
207527008 20040731 1 900000000000207008 900000000000073002
|
||||
207527008 20090731 0 900000000000207008 900000000000074008
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
id effectiveTime active moduleId sourceId destinationId relationshipGroup typeId characteristicTypeId modifierId
|
||||
100022 20020131 1 900000000000207008 126815003 126813005 0 116680003 900000000000011006 900000000000451002
|
||||
100022 20090731 0 900000000000207008 126816002 126813005 0 116680003 900000000000011006 900000000000451002
|
||||
100025 20020131 1 900000000000207008 126816002 126813005 0 116680003 900000000000011006 900000000000451002
|
||||
100025 20090731 0 900000000000207008 126816002 126813005 0 116680003 900000000000011006 900000000000451002
|
||||
101021 20020131 1 900000000000207008 126817006 126815003 0 116680003 900000000000011006 900000000000451002
|
||||
101021 20020131 1 900000000000207008 126815003 126817006 0 116680003 900000000000011006 900000000000451002
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.mockito.internal.stubbing.defaultanswers.ReturnsDeepStubs;
|
|||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import ca.uhn.fhir.context.ConfigurationException;
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.model.api.Bundle;
|
||||
import ca.uhn.fhir.model.api.ExtensionDt;
|
||||
|
@ -77,6 +78,8 @@ import ca.uhn.fhir.rest.api.MethodOutcome;
|
|||
import ca.uhn.fhir.rest.api.PreferReturnEnum;
|
||||
import ca.uhn.fhir.rest.api.SummaryEnum;
|
||||
import ca.uhn.fhir.rest.client.apache.ApacheRestfulClientFactory;
|
||||
import ca.uhn.fhir.rest.client.api.IHttpClient;
|
||||
import ca.uhn.fhir.rest.client.api.IRestfulClient;
|
||||
import ca.uhn.fhir.rest.client.exceptions.InvalidResponseException;
|
||||
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
|
||||
import ca.uhn.fhir.rest.method.SearchStyleEnum;
|
||||
|
@ -88,42 +91,24 @@ import ca.uhn.fhir.util.TestUtil;
|
|||
|
||||
public class GenericClientDstu2Test {
|
||||
private static FhirContext ourCtx;
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientDstu2Test.class);
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(GenericClientDstu2Test.class);
|
||||
private HttpClient myHttpClient;
|
||||
|
||||
private HttpResponse myHttpResponse;
|
||||
|
||||
private int myResponseCount = 0;
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadForUnknownType() throws Exception {
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
try {
|
||||
client.read(new UriDt("1"));
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("The given URI is not an absolute URL and is not usable for this operation: 1", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
client.read(new UriDt("http://example.com/InvalidResource/1"));
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("Unknown resource name \"InvalidResource\" (this name is not known in FHIR version \"DSTU2\")", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
myHttpClient = mock(HttpClient.class, new ReturnsDeepStubs());
|
||||
ourCtx.setRestfulClientFactory(new ApacheRestfulClientFactory(ourCtx));
|
||||
ourCtx.getRestfulClientFactory().setHttpClient(myHttpClient);
|
||||
ourCtx.getRestfulClientFactory().setConnectionRequestTimeout(10000);
|
||||
ourCtx.getRestfulClientFactory().setConnectTimeout(10000);
|
||||
ourCtx.getRestfulClientFactory().setPoolMaxPerRoute(100);
|
||||
ourCtx.getRestfulClientFactory().setPoolMaxTotal(100);
|
||||
|
||||
ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
|
||||
myHttpResponse = mock(HttpResponse.class, new ReturnsDeepStubs());
|
||||
myResponseCount = 0;
|
||||
|
@ -199,68 +184,6 @@ public class GenericClientDstu2Test {
|
|||
idx++;
|
||||
}
|
||||
|
||||
/**
|
||||
* See #322
|
||||
*/
|
||||
@Test
|
||||
public void testFetchConformanceWithSmartExtensions() throws Exception {
|
||||
final String respString = IOUtils.toString(GenericClientDstu2Test.class.getResourceAsStream("/conformance_322.json"));
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:8080/fhir");
|
||||
Conformance conf = client.fetchConformance().ofType(Conformance.class).execute();
|
||||
|
||||
Rest rest = conf.getRest().get(0);
|
||||
RestSecurity security = rest.getSecurity();
|
||||
|
||||
List<ExtensionDt> ext = security.getUndeclaredExtensionsByUrl("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris");
|
||||
List<ExtensionDt> tokenExts = ext.get(0).getUndeclaredExtensionsByUrl("token");
|
||||
ExtensionDt tokenExt = tokenExts.get(0);
|
||||
UriDt value = (UriDt) tokenExt.getValue();
|
||||
assertEquals("https://my-server.org/token", value.getValueAsString());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* See #322
|
||||
*/
|
||||
@Test
|
||||
public void testFetchConformanceWithSmartExtensionsAltCase() throws Exception {
|
||||
final String respString = IOUtils.toString(GenericClientDstu2Test.class.getResourceAsStream("/conformance_322.json")).replace("valueuri", "valueUri");
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:8080/fhir");
|
||||
Conformance conf = client.fetchConformance().ofType(Conformance.class).execute();
|
||||
|
||||
Rest rest = conf.getRest().get(0);
|
||||
RestSecurity security = rest.getSecurity();
|
||||
|
||||
List<ExtensionDt> ext = security.getUndeclaredExtensionsByUrl("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris");
|
||||
List<ExtensionDt> tokenExts = ext.get(0).getUndeclaredExtensionsByUrl("token");
|
||||
ExtensionDt tokenExt = tokenExts.get(0);
|
||||
UriDt value = (UriDt) tokenExt.getValue();
|
||||
assertEquals("https://my-server.org/token", value.getValueAsString());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAcceptHeaderPreflightConformance() throws Exception {
|
||||
String methodName = "testAcceptHeaderPreflightConformance";
|
||||
|
@ -560,6 +483,30 @@ public class GenericClientDstu2Test {
|
|||
assertEquals("Patient/123", output.getResource().getIdElement().toUnqualifiedVersionless().getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteByResource() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_204_NO_CONTENT, ""));
|
||||
when(myHttpResponse.getEntity().getContent()).then(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
int idx = 0;
|
||||
|
||||
Patient pat = new Patient();
|
||||
pat.setId("Patient/123");
|
||||
|
||||
client.delete().resource(pat).execute();
|
||||
assertEquals("DELETE", capt.getAllValues().get(idx).getMethod());
|
||||
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteConditional() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
|
@ -595,59 +542,6 @@ public class GenericClientDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testDeleteNonFluent() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_204_NO_CONTENT, ""));
|
||||
when(myHttpResponse.getEntity().getContent()).then(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
int idx = 0;
|
||||
|
||||
client.delete(Patient.class, new IdDt("Patient/123"));
|
||||
assertEquals("DELETE", capt.getAllValues().get(idx).getMethod());
|
||||
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
client.delete(Patient.class, "123");
|
||||
assertEquals("DELETE", capt.getAllValues().get(idx).getMethod());
|
||||
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteByResource() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_204_NO_CONTENT, ""));
|
||||
when(myHttpResponse.getEntity().getContent()).then(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
int idx = 0;
|
||||
|
||||
Patient pat = new Patient();
|
||||
pat.setId("Patient/123");
|
||||
|
||||
client.delete().resource(pat).execute();
|
||||
assertEquals("DELETE", capt.getAllValues().get(idx).getMethod());
|
||||
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteInvalidRequest() throws Exception {
|
||||
Patient pat = new Patient();
|
||||
|
@ -691,6 +585,97 @@ public class GenericClientDstu2Test {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Test
|
||||
public void testDeleteNonFluent() throws Exception {
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), Constants.STATUS_HTTP_204_NO_CONTENT, ""));
|
||||
when(myHttpResponse.getEntity().getContent()).then(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(""), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
|
||||
int idx = 0;
|
||||
|
||||
client.delete(Patient.class, new IdDt("Patient/123"));
|
||||
assertEquals("DELETE", capt.getAllValues().get(idx).getMethod());
|
||||
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
client.delete(Patient.class, "123");
|
||||
assertEquals("DELETE", capt.getAllValues().get(idx).getMethod());
|
||||
assertEquals("http://example.com/fhir/Patient/123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* See #322
|
||||
*/
|
||||
@Test
|
||||
public void testFetchConformanceWithSmartExtensions() throws Exception {
|
||||
final String respString = IOUtils.toString(GenericClientDstu2Test.class.getResourceAsStream("/conformance_322.json"));
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:8080/fhir");
|
||||
Conformance conf = client.fetchConformance().ofType(Conformance.class).execute();
|
||||
|
||||
Rest rest = conf.getRest().get(0);
|
||||
RestSecurity security = rest.getSecurity();
|
||||
|
||||
List<ExtensionDt> ext = security.getUndeclaredExtensionsByUrl("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris");
|
||||
List<ExtensionDt> tokenExts = ext.get(0).getUndeclaredExtensionsByUrl("token");
|
||||
ExtensionDt tokenExt = tokenExts.get(0);
|
||||
UriDt value = (UriDt) tokenExt.getValue();
|
||||
assertEquals("https://my-server.org/token", value.getValueAsString());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* See #322
|
||||
*/
|
||||
@Test
|
||||
public void testFetchConformanceWithSmartExtensionsAltCase() throws Exception {
|
||||
final String respString = IOUtils.toString(GenericClientDstu2Test.class.getResourceAsStream("/conformance_322.json")).replace("valueuri", "valueUri");
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).thenAnswer(new Answer<ReaderInputStream>() {
|
||||
@Override
|
||||
public ReaderInputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(respString), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://localhost:8080/fhir");
|
||||
Conformance conf = client.fetchConformance().ofType(Conformance.class).execute();
|
||||
|
||||
Rest rest = conf.getRest().get(0);
|
||||
RestSecurity security = rest.getSecurity();
|
||||
|
||||
List<ExtensionDt> ext = security.getUndeclaredExtensionsByUrl("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris");
|
||||
List<ExtensionDt> tokenExts = ext.get(0).getUndeclaredExtensionsByUrl("token");
|
||||
ExtensionDt tokenExt = tokenExts.get(0);
|
||||
UriDt value = (UriDt) tokenExt.getValue();
|
||||
assertEquals("https://my-server.org/token", value.getValueAsString());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHistory() throws Exception {
|
||||
|
||||
|
@ -796,6 +781,16 @@ public class GenericClientDstu2Test {
|
|||
idx++;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidClient() {
|
||||
try {
|
||||
ourCtx.getRestfulClientFactory().newClient(RestfulClientInstance.class, "http://foo");
|
||||
fail();
|
||||
} catch (ConfigurationException e) {
|
||||
assertEquals("ca.uhn.fhir.context.ConfigurationException: ca.uhn.fhir.rest.client.GenericClientDstu2Test.RestfulClientInstance is not an interface", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMetaAdd() throws Exception {
|
||||
IParser p = ourCtx.newXmlParser();
|
||||
|
@ -1663,6 +1658,24 @@ public class GenericClientDstu2Test {
|
|||
assertEquals("FAM", response.getName().get(0).getFamily().get(0).getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadForUnknownType() throws Exception {
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
try {
|
||||
client.read(new UriDt("1"));
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
assertEquals("The given URI is not an absolute URL and is not usable for this operation: 1", e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
client.read(new UriDt("http://example.com/InvalidResource/1"));
|
||||
fail();
|
||||
} catch (DataFormatException e) {
|
||||
assertEquals("Unknown resource name \"InvalidResource\" (this name is not known in FHIR version \"DSTU2\")", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadUpdatedHeaderDoesntOverwriteResourceValue() throws Exception {
|
||||
|
||||
|
@ -1786,6 +1799,76 @@ public class GenericClientDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByNumber() throws Exception {
|
||||
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).then(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Encounter")
|
||||
.where(Encounter.LENGTH.greaterThan().number(123))
|
||||
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Encounter?length=gt123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Encounter")
|
||||
.where(Encounter.LENGTH.lessThan().number(123))
|
||||
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Encounter?length=lt123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Encounter")
|
||||
.where(Encounter.LENGTH.greaterThanOrEqual().number("123"))
|
||||
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Encounter?length=ge123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Encounter")
|
||||
.where(Encounter.LENGTH.lessThanOrEqual().number("123"))
|
||||
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Encounter?length=le123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Encounter")
|
||||
.where(Encounter.LENGTH.exactly().number(123))
|
||||
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Encounter?length=123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByPost() throws Exception {
|
||||
String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||
|
@ -1892,76 +1975,6 @@ public class GenericClientDstu2Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByNumber() throws Exception {
|
||||
final String msg = "{\"resourceType\":\"Bundle\",\"id\":null,\"base\":\"http://localhost:57931/fhir/contextDev\",\"total\":1,\"link\":[{\"relation\":\"self\",\"url\":\"http://localhost:57931/fhir/contextDev/Patient?identifier=urn%3AMultiFhirVersionTest%7CtestSubmitPatient01&_format=json\"}],\"entry\":[{\"resource\":{\"resourceType\":\"Patient\",\"id\":\"1\",\"meta\":{\"versionId\":\"1\",\"lastUpdated\":\"2014-12-20T18:41:29.706-05:00\"},\"identifier\":[{\"system\":\"urn:MultiFhirVersionTest\",\"value\":\"testSubmitPatient01\"}]}}]}";
|
||||
|
||||
ArgumentCaptor<HttpUriRequest> capt = ArgumentCaptor.forClass(HttpUriRequest.class);
|
||||
when(myHttpClient.execute(capt.capture())).thenReturn(myHttpResponse);
|
||||
when(myHttpResponse.getStatusLine()).thenReturn(new BasicStatusLine(new ProtocolVersion("HTTP", 1, 1), 200, "OK"));
|
||||
when(myHttpResponse.getEntity().getContentType()).thenReturn(new BasicHeader("content-type", Constants.CT_FHIR_JSON + "; charset=UTF-8"));
|
||||
when(myHttpResponse.getEntity().getContent()).then(new Answer<InputStream>() {
|
||||
@Override
|
||||
public InputStream answer(InvocationOnMock theInvocation) throws Throwable {
|
||||
return new ReaderInputStream(new StringReader(msg), Charset.forName("UTF-8"));
|
||||
}
|
||||
});
|
||||
|
||||
IGenericClient client = ourCtx.newRestfulGenericClient("http://example.com/fhir");
|
||||
int idx = 0;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Encounter")
|
||||
.where(Encounter.LENGTH.greaterThan().number(123))
|
||||
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Encounter?length=gt123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Encounter")
|
||||
.where(Encounter.LENGTH.lessThan().number(123))
|
||||
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Encounter?length=lt123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Encounter")
|
||||
.where(Encounter.LENGTH.greaterThanOrEqual().number("123"))
|
||||
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Encounter?length=ge123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Encounter")
|
||||
.where(Encounter.LENGTH.lessThanOrEqual().number("123"))
|
||||
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Encounter?length=le123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
//@formatter:off
|
||||
client.search()
|
||||
.forResource("Encounter")
|
||||
.where(Encounter.LENGTH.exactly().number(123))
|
||||
.returnBundle(ca.uhn.fhir.model.dstu2.resource.Bundle.class)
|
||||
.execute();
|
||||
//@formatter:on
|
||||
assertEquals("http://example.com/fhir/Encounter?length=123", capt.getAllValues().get(idx).getURI().toString());
|
||||
idx++;
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchByUrl() throws Exception {
|
||||
|
||||
|
@ -2630,9 +2643,61 @@ public class GenericClientDstu2Test {
|
|||
return (OperationOutcome) theOperationOutcome;
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterClassClearContext() {
|
||||
TestUtil.clearAllStaticFieldsForUnitTest();
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
ourCtx = FhirContext.forDstu2();
|
||||
}
|
||||
|
||||
public final static class RestfulClientInstance implements IRestfulClient {
|
||||
@Override
|
||||
public <T extends IBaseResource> T fetchResourceFromUrl(Class<T> theResourceType, String theUrl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FhirContext getFhirContext() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IHttpClient getHttpClient() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerBase() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerInterceptor(IClientInterceptor theInterceptor) {
|
||||
//nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEncoding(EncodingEnum theEncoding) {
|
||||
//nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPrettyPrint(Boolean thePrettyPrint) {
|
||||
//nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSummary(SummaryEnum theSummary) {
|
||||
//nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterInterceptor(IClientInterceptor theInterceptor) {
|
||||
//nothing
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package ca.uhn.fhir.rest.client.apache;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.rest.client.BaseClient;
|
||||
import ca.uhn.fhir.rest.client.exceptions.FhirClientConnectionException;
|
||||
|
||||
public class ApacheRestfulClientFactoryTest {
|
||||
|
||||
@Test
|
||||
public void testSetContext() {
|
||||
ApacheRestfulClientFactory factory = new ApacheRestfulClientFactory();
|
||||
factory.getServerValidationModeEnum();
|
||||
factory.setFhirContext(FhirContext.forDstu2());
|
||||
try {
|
||||
factory.setFhirContext(FhirContext.forDstu2());
|
||||
fail();
|
||||
} catch (IllegalStateException e) {
|
||||
assertEquals("java.lang.IllegalStateException: RestfulClientFactory instance is already associated with one FhirContext. RestfulClientFactory instances can not be shared.", e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidatateBase() {
|
||||
FhirContext ctx = FhirContext.forDstu2();
|
||||
ApacheRestfulClientFactory factory = new ApacheRestfulClientFactory();
|
||||
factory.setFhirContext(ctx);
|
||||
factory.setConnectTimeout(1);
|
||||
try {
|
||||
factory.validateServerBase("http://127.0.0.1:22225", factory.getHttpClient("http://foo"), (BaseClient) ctx.newRestfulGenericClient("http://foo"));
|
||||
fail();
|
||||
} catch (FhirClientConnectionException e) {
|
||||
assertEquals("Failed to retrieve the server metadata statement during client initialization. URL used was http://127.0.0.1:22225metadata", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,6 +40,7 @@ import ca.uhn.fhir.model.api.IResource;
|
|||
import ca.uhn.fhir.model.dstu2.composite.ResourceReferenceDt;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Bundle;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Observation;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Parameters;
|
||||
import ca.uhn.fhir.model.dstu2.resource.Patient;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum;
|
||||
import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum;
|
||||
|
@ -47,6 +48,7 @@ import ca.uhn.fhir.model.primitive.IdDt;
|
|||
import ca.uhn.fhir.rest.annotation.Create;
|
||||
import ca.uhn.fhir.rest.annotation.Delete;
|
||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||
import ca.uhn.fhir.rest.annotation.Operation;
|
||||
import ca.uhn.fhir.rest.annotation.Read;
|
||||
import ca.uhn.fhir.rest.annotation.ResourceParam;
|
||||
import ca.uhn.fhir.rest.annotation.Search;
|
||||
|
@ -158,6 +160,215 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
assertTrue(ourHitMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationServerLevel() throws Exception {
|
||||
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||
@Override
|
||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||
//@formatter:off
|
||||
return new RuleBuilder()
|
||||
.allow("RULE 1").operation().named("opName").onServer().andThen()
|
||||
.build();
|
||||
//@formatter:on
|
||||
}
|
||||
});
|
||||
|
||||
HttpGet httpGet;
|
||||
HttpResponse status;
|
||||
String response;
|
||||
|
||||
// Server
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createObservation(10, "Patient/2"));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/$opName");
|
||||
status = ourClient.execute(httpGet);
|
||||
extractResponseAndClose(status);
|
||||
assertEquals(200, status.getStatusLine().getStatusCode());
|
||||
assertTrue(ourHitMethod);
|
||||
|
||||
// Type
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$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 = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/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 testOperationInstanceLevel() throws Exception {
|
||||
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||
@Override
|
||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||
//@formatter:off
|
||||
return new RuleBuilder()
|
||||
.allow("RULE 1").operation().named("opName").onInstance(new IdDt("http://example.com/Patient/1/_history/2")).andThen()
|
||||
.build();
|
||||
//@formatter:on
|
||||
}
|
||||
});
|
||||
|
||||
HttpGet httpGet;
|
||||
HttpResponse status;
|
||||
String response;
|
||||
|
||||
// Server
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createObservation(10, "Patient/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 = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/$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 = Arrays.asList(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);
|
||||
|
||||
// Wrong instance
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/2/$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 testOperationAnyName() throws Exception {
|
||||
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||
@Override
|
||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||
//@formatter:off
|
||||
return new RuleBuilder()
|
||||
.allow("RULE 1").operation().withAnyName().onServer().andThen()
|
||||
.build();
|
||||
//@formatter:on
|
||||
}
|
||||
});
|
||||
|
||||
HttpGet httpGet;
|
||||
HttpResponse status;
|
||||
String response;
|
||||
|
||||
// Server
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(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);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationTypeLevel() throws Exception {
|
||||
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||
@Override
|
||||
public List<IAuthRule> buildRuleList(RequestDetails theRequestDetails) {
|
||||
//@formatter:off
|
||||
return new RuleBuilder()
|
||||
.allow("RULE 1").operation().named("opName").onType(Patient.class).andThen()
|
||||
.build();
|
||||
//@formatter:on
|
||||
}
|
||||
});
|
||||
|
||||
HttpGet httpGet;
|
||||
HttpResponse status;
|
||||
String response;
|
||||
|
||||
// Server
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createObservation(10, "Patient/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 = Arrays.asList(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);
|
||||
|
||||
// Wrong type
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createPatient(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);
|
||||
|
||||
// Wrong name
|
||||
ourHitMethod = false;
|
||||
ourReturn = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/1/$opName2");
|
||||
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 = Arrays.asList(createPatient(2));
|
||||
httpGet = new HttpGet("http://localhost:" + ourPort + "/Patient/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 testDenyAll() throws Exception {
|
||||
ourServlet.registerInterceptor(new AuthorizationInterceptor(PolicyEnum.DENY) {
|
||||
|
@ -762,6 +973,17 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
retVal.setResource(theResource);
|
||||
return retVal;
|
||||
}
|
||||
@Operation(name="opName", idempotent=true)
|
||||
public Parameters operation() {
|
||||
ourHitMethod = true;
|
||||
return (Parameters) new Parameters().setId("1");
|
||||
}
|
||||
@Operation(name="opName", idempotent=true)
|
||||
public Parameters operation(@IdParam IdDt theId) {
|
||||
ourHitMethod = true;
|
||||
return (Parameters) new Parameters().setId("1");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -814,10 +1036,36 @@ public class AuthorizationInterceptorDstu2Test {
|
|||
return retVal;
|
||||
}
|
||||
|
||||
@Operation(name="opName", idempotent=true)
|
||||
public Parameters operation() {
|
||||
ourHitMethod = true;
|
||||
return (Parameters) new Parameters().setId("1");
|
||||
}
|
||||
|
||||
@Operation(name="opName", idempotent=true)
|
||||
public Parameters operation(@IdParam IdDt theId) {
|
||||
ourHitMethod = true;
|
||||
return (Parameters) new Parameters().setId("1");
|
||||
}
|
||||
|
||||
@Operation(name="opName2", idempotent=true)
|
||||
public Parameters operation2(@IdParam IdDt theId) {
|
||||
ourHitMethod = true;
|
||||
return (Parameters) new Parameters().setId("1");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class PlainProvider
|
||||
{
|
||||
|
||||
@Operation(name="opName", idempotent=true)
|
||||
public Parameters operation() {
|
||||
ourHitMethod = true;
|
||||
return (Parameters) new Parameters().setId("1");
|
||||
}
|
||||
|
||||
|
||||
@Transaction()
|
||||
public Bundle search(@TransactionParam Bundle theInput) {
|
||||
ourHitMethod = true;
|
||||
|
|
|
@ -377,6 +377,10 @@
|
|||
response message
|
||||
</action>
|
||||
-->
|
||||
<action type="add">
|
||||
AuthorizationInterceptor can now allow or deny requests to extended
|
||||
operations (e.g. $everything)
|
||||
</action>
|
||||
</release>
|
||||
<release version="1.5" date="2016-04-20">
|
||||
<action type="fix" issue="339">
|
||||
|
|
Loading…
Reference in New Issue