Impl CodeSystem $validate-code for R4 with basic test cases
This commit is contained in:
parent
7f7e1f39bc
commit
64f2bae2e7
|
@ -47,6 +47,8 @@ public interface IFhirResourceDaoCodeSystem<T extends IBaseResource, CD, CC> ext
|
||||||
|
|
||||||
SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, CD theCodingA, CD theCodingB, IPrimitiveType<String> theVersion, RequestDetails theRequestDetails);
|
SubsumesResult subsumes(IPrimitiveType<String> theCodeA, IPrimitiveType<String> theCodeB, IPrimitiveType<String> theSystem, CD theCodingA, CD theCodingB, IPrimitiveType<String> theVersion, RequestDetails theRequestDetails);
|
||||||
|
|
||||||
|
IValidationSupport.CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType<String> theCodeSystemUrl, IPrimitiveType<String> theVersion, IPrimitiveType<String> theCode, IPrimitiveType<String> theDisplay, CD theCoding, CC theCodeableConcept, RequestDetails theRequestDetails);
|
||||||
|
|
||||||
class SubsumesResult {
|
class SubsumesResult {
|
||||||
|
|
||||||
private final ConceptSubsumptionOutcome myOutcome;
|
private final ConceptSubsumptionOutcome myOutcome;
|
||||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoValueSet;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
|
||||||
|
@ -56,6 +57,7 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
|
||||||
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions;
|
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions;
|
||||||
import static ca.uhn.fhir.jpa.util.LogicUtil.multiXor;
|
import static ca.uhn.fhir.jpa.util.LogicUtil.multiXor;
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
@ -312,4 +314,13 @@ public class FhirResourceDaoValueSetDstu2 extends BaseHapiFhirResourceDao<ValueS
|
||||||
return myTerminologySvc.validateCode(vsValidateCodeOptions(), theId, toStringOrNull(theValueSetIdentifier), toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
|
return myTerminologySvc.validateCode(vsValidateCodeOptions(), theId, toStringOrNull(theValueSetIdentifier), toStringOrNull(theSystem), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-- For CodeSystem, not sure we need it here or throw invalid operation exception, since there is no CodeSystem in dstu2
|
||||||
|
@Override
|
||||||
|
public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType<String> theCodeSystemUrl,
|
||||||
|
IPrimitiveType<String> theVersion, IPrimitiveType<String> theCode, IPrimitiveType<String> theDisplay,
|
||||||
|
CodingDt theCoding, CodeableConceptDt theCodeableConcept, RequestDetails theRequestDetails) {
|
||||||
|
|
||||||
|
return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.dao.dstu3;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
|
||||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||||
|
@ -51,6 +52,8 @@ import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
|
||||||
|
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
import static org.hl7.fhir.convertors.conv30_40.CodeSystem30_40.convertCodeSystem;
|
import static org.hl7.fhir.convertors.conv30_40.CodeSystem30_40.convertCodeSystem;
|
||||||
|
|
||||||
|
@ -169,4 +172,12 @@ public class FhirResourceDaoCodeSystemDstu3 extends BaseHapiFhirResourceDao<Code
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType<String> theCodeSystemUrl,
|
||||||
|
IPrimitiveType<String> theVersion, IPrimitiveType<String> theCode, IPrimitiveType<String> theDisplay,
|
||||||
|
Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
|
||||||
|
|
||||||
|
return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ package ca.uhn.fhir.jpa.dao.r4;
|
||||||
|
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
|
||||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||||
|
@ -50,6 +51,8 @@ import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
|
||||||
|
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
|
public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
|
||||||
|
@ -164,5 +167,13 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSys
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType<String> theCodeSystemUrl,
|
||||||
|
IPrimitiveType<String> theVersion, IPrimitiveType<String> theCode, IPrimitiveType<String> theDisplay,
|
||||||
|
Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
|
||||||
|
|
||||||
|
return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.dao.r5;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport.CodeValidationResult;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
||||||
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
import ca.uhn.fhir.jpa.dao.BaseHapiFhirResourceDao;
|
||||||
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
import ca.uhn.fhir.jpa.dao.index.IdHelperService;
|
||||||
|
@ -51,6 +52,8 @@ import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static ca.uhn.fhir.jpa.dao.FhirResourceDaoValueSetDstu2.toStringOrNull;
|
||||||
|
import static ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoValueSetDstu3.vsValidateCodeOptions;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
|
public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
|
||||||
|
@ -166,5 +169,11 @@ public class FhirResourceDaoCodeSystemR5 extends BaseHapiFhirResourceDao<CodeSys
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CodeValidationResult validateCode(IIdType theCodeSystemId, IPrimitiveType<String> theCodeSystemUrl,
|
||||||
|
IPrimitiveType<String> theVersion, IPrimitiveType<String> theCode, IPrimitiveType<String> theDisplay,
|
||||||
|
Coding theCoding, CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
|
||||||
|
|
||||||
|
return myTerminologySvc.codeSystemValidateCode(theCodeSystemId, toStringOrNull(theCodeSystemUrl), toStringOrNull(theVersion), toStringOrNull(theCode), toStringOrNull(theDisplay), theCoding, theCodeableConcept);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,19 @@
|
||||||
package ca.uhn.fhir.jpa.provider.r4;
|
package ca.uhn.fhir.jpa.provider.r4;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
|
import org.hl7.fhir.r4.model.CodeType;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.hl7.fhir.r4.model.UriType;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR JPA Server
|
* HAPI FHIR JPA Server
|
||||||
|
@ -23,20 +37,11 @@ package ca.uhn.fhir.jpa.provider.r4;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoCodeSystem;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
|
import ca.uhn.fhir.jpa.provider.BaseJpaResourceProviderValueSetDstu2;
|
||||||
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
import ca.uhn.fhir.rest.annotation.Operation;
|
import ca.uhn.fhir.rest.annotation.Operation;
|
||||||
import ca.uhn.fhir.rest.annotation.OperationParam;
|
import ca.uhn.fhir.rest.annotation.OperationParam;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import org.hl7.fhir.r4.model.BooleanType;
|
|
||||||
import org.hl7.fhir.r4.model.CodeSystem;
|
|
||||||
import org.hl7.fhir.r4.model.CodeType;
|
|
||||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
|
||||||
import org.hl7.fhir.r4.model.UriType;
|
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4<CodeSystem> {
|
public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4<CodeSystem> {
|
||||||
|
|
||||||
|
@ -98,4 +103,36 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4<C
|
||||||
endRequest(theServletRequest);
|
endRequest(theServletRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* $validate-code operation
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Operation(name = JpaConstants.OPERATION_VALIDATE_CODE, idempotent = true, returnParameters = {
|
||||||
|
@OperationParam(name = "result", type = BooleanType.class, min = 1),
|
||||||
|
@OperationParam(name = "message", type = StringType.class),
|
||||||
|
@OperationParam(name = "display", type = StringType.class)
|
||||||
|
})
|
||||||
|
public Parameters validateCode(
|
||||||
|
HttpServletRequest theServletRequest,
|
||||||
|
@IdParam(optional = true) IdType theId,
|
||||||
|
@OperationParam(name = "url", min = 0, max = 1) UriType theCodeSystemUrl,
|
||||||
|
@OperationParam(name = "code", min = 0, max = 1) CodeType theCode,
|
||||||
|
@OperationParam(name = "version", min = 0, max = 1) UriType theVersion,
|
||||||
|
@OperationParam(name = "display", min = 0, max = 1) StringType theDisplay,
|
||||||
|
@OperationParam(name = "coding", min = 0, max = 1) Coding theCoding,
|
||||||
|
@OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theCodeableConcept,
|
||||||
|
RequestDetails theRequestDetails
|
||||||
|
) {
|
||||||
|
|
||||||
|
startRequest(theServletRequest);
|
||||||
|
try {
|
||||||
|
IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> dao = (IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept>) getDao();
|
||||||
|
|
||||||
|
IValidationSupport.CodeValidationResult result = dao.validateCode(theId, theCodeSystemUrl, theVersion, theCode, theDisplay, theCoding, theCodeableConcept, theRequestDetails);
|
||||||
|
return (Parameters) BaseJpaResourceProviderValueSetDstu2.toValidateCodeResult(getContext(), result);
|
||||||
|
} finally {
|
||||||
|
endRequest(theServletRequest);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,99 @@
|
||||||
package ca.uhn.fhir.jpa.term;
|
package ca.uhn.fhir.jpa.term;
|
||||||
|
|
||||||
|
import static org.apache.commons.lang3.StringUtils.defaultString;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNoneBlank;
|
||||||
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.PersistenceContext;
|
||||||
|
import javax.persistence.PersistenceContextType;
|
||||||
|
import javax.persistence.TypedQuery;
|
||||||
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
import javax.persistence.criteria.Join;
|
||||||
|
import javax.persistence.criteria.JoinType;
|
||||||
|
import javax.persistence.criteria.Predicate;
|
||||||
|
import javax.persistence.criteria.Root;
|
||||||
|
import javax.persistence.criteria.Fetch;
|
||||||
|
import javax.validation.constraints.NotBlank;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.apache.commons.lang3.time.DateUtils;
|
||||||
|
import org.apache.lucene.index.Term;
|
||||||
|
import org.apache.lucene.queries.TermsQuery;
|
||||||
|
import org.apache.lucene.search.BooleanClause;
|
||||||
|
import org.apache.lucene.search.BooleanQuery;
|
||||||
|
import org.apache.lucene.search.Query;
|
||||||
|
import org.apache.lucene.search.RegexpQuery;
|
||||||
|
import org.apache.lucene.search.TermQuery;
|
||||||
|
import org.hibernate.ScrollMode;
|
||||||
|
import org.hibernate.ScrollableResults;
|
||||||
|
import org.hibernate.search.jpa.FullTextEntityManager;
|
||||||
|
import org.hibernate.search.jpa.FullTextQuery;
|
||||||
|
import org.hibernate.search.query.dsl.BooleanJunction;
|
||||||
|
import org.hibernate.search.query.dsl.QueryBuilder;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService;
|
||||||
|
import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport;
|
||||||
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IAnyResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
import org.hl7.fhir.r4.model.CanonicalType;
|
||||||
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap;
|
||||||
|
import org.hl7.fhir.r4.model.Enumerations;
|
||||||
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
|
import org.hl7.fhir.r4.model.IntegerType;
|
||||||
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.hl7.fhir.r4.model.ValueSet;
|
||||||
|
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
|
||||||
|
import org.quartz.JobExecutionContext;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Slice;
|
||||||
|
import org.springframework.transaction.PlatformTransactionManager;
|
||||||
|
import org.springframework.transaction.TransactionDefinition;
|
||||||
|
import org.springframework.transaction.annotation.Propagation;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
|
||||||
|
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
|
||||||
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
|
import com.github.benmanes.caffeine.cache.Cache;
|
||||||
|
import com.github.benmanes.caffeine.cache.Caffeine;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import com.google.common.base.Stopwatch;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* #%L
|
* #%L
|
||||||
* HAPI FHIR JPA Server
|
* HAPI FHIR JPA Server
|
||||||
|
@ -85,94 +179,6 @@ import ca.uhn.fhir.util.StopWatch;
|
||||||
import ca.uhn.fhir.util.UrlUtil;
|
import ca.uhn.fhir.util.UrlUtil;
|
||||||
import ca.uhn.fhir.util.ValidateUtil;
|
import ca.uhn.fhir.util.ValidateUtil;
|
||||||
import ca.uhn.fhir.util.VersionIndependentConcept;
|
import ca.uhn.fhir.util.VersionIndependentConcept;
|
||||||
import com.github.benmanes.caffeine.cache.Cache;
|
|
||||||
import com.github.benmanes.caffeine.cache.Caffeine;
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
|
||||||
import com.google.common.base.Stopwatch;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
import org.apache.commons.lang3.time.DateUtils;
|
|
||||||
import org.apache.lucene.index.Term;
|
|
||||||
import org.apache.lucene.queries.TermsQuery;
|
|
||||||
import org.apache.lucene.search.BooleanClause;
|
|
||||||
import org.apache.lucene.search.BooleanQuery;
|
|
||||||
import org.apache.lucene.search.Query;
|
|
||||||
import org.apache.lucene.search.RegexpQuery;
|
|
||||||
import org.apache.lucene.search.TermQuery;
|
|
||||||
import org.hibernate.ScrollMode;
|
|
||||||
import org.hibernate.ScrollableResults;
|
|
||||||
import org.hibernate.search.jpa.FullTextEntityManager;
|
|
||||||
import org.hibernate.search.jpa.FullTextQuery;
|
|
||||||
import org.hibernate.search.query.dsl.BooleanJunction;
|
|
||||||
import org.hibernate.search.query.dsl.QueryBuilder;
|
|
||||||
import org.hl7.fhir.common.hapi.validation.support.CommonCodeSystemsTerminologyService;
|
|
||||||
import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport;
|
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
|
||||||
import org.hl7.fhir.instance.model.api.IAnyResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseDatatype;
|
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
|
||||||
import org.hl7.fhir.r4.model.CanonicalType;
|
|
||||||
import org.hl7.fhir.r4.model.CodeSystem;
|
|
||||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
|
||||||
import org.hl7.fhir.r4.model.ConceptMap;
|
|
||||||
import org.hl7.fhir.r4.model.Enumerations;
|
|
||||||
import org.hl7.fhir.r4.model.IdType;
|
|
||||||
import org.hl7.fhir.r4.model.IntegerType;
|
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
|
||||||
import org.hl7.fhir.r4.model.codesystems.ConceptSubsumptionOutcome;
|
|
||||||
import org.quartz.JobExecutionContext;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
import org.springframework.data.domain.PageRequest;
|
|
||||||
import org.springframework.data.domain.Pageable;
|
|
||||||
import org.springframework.data.domain.Slice;
|
|
||||||
import org.springframework.transaction.PlatformTransactionManager;
|
|
||||||
import org.springframework.transaction.TransactionDefinition;
|
|
||||||
import org.springframework.transaction.annotation.Propagation;
|
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
|
||||||
import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
|
|
||||||
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
|
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.persistence.EntityManager;
|
|
||||||
import javax.persistence.PersistenceContext;
|
|
||||||
import javax.persistence.PersistenceContextType;
|
|
||||||
import javax.persistence.TypedQuery;
|
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
|
||||||
import javax.persistence.criteria.CriteriaQuery;
|
|
||||||
import javax.persistence.criteria.Join;
|
|
||||||
import javax.persistence.criteria.Predicate;
|
|
||||||
import javax.persistence.criteria.Root;
|
|
||||||
import javax.validation.constraints.NotNull;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.defaultString;
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNoneBlank;
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|
||||||
|
|
||||||
public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
||||||
public static final int DEFAULT_FETCH_SIZE = 250;
|
public static final int DEFAULT_FETCH_SIZE = 250;
|
||||||
|
@ -2555,5 +2561,117 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc {
|
||||||
return ourLastResultsFromTranslationWithReverseCache;
|
return ourLastResultsFromTranslationWithReverseCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theCodeSystemUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept) {
|
||||||
|
|
||||||
|
CodeableConcept codeableConcept = toCanonicalCodeableConcept(theCodeableConcept);
|
||||||
|
boolean haveCodeableConcept = codeableConcept != null && codeableConcept.getCoding().size() > 0;
|
||||||
|
|
||||||
|
Coding coding = toCanonicalCoding(theCoding);
|
||||||
|
boolean haveCoding = coding != null && coding.isEmpty() == false;
|
||||||
|
|
||||||
|
boolean haveCode = theCode != null && theCode.isEmpty() == false;
|
||||||
|
|
||||||
|
if (!haveCodeableConcept && !haveCoding && !haveCode) {
|
||||||
|
throw new InvalidRequestException("No code, coding, or codeableConcept provided to validate.");
|
||||||
|
}
|
||||||
|
if (!LogicUtil.multiXor(haveCodeableConcept, haveCoding, haveCode)) {
|
||||||
|
throw new InvalidRequestException("$validate-code can only validate (code) OR (coding) OR (codeableConcept)");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean haveIdentifierParam = isNotBlank(theCodeSystemUrl);
|
||||||
|
String codeSystemUrl;
|
||||||
|
if (theCodeSystemId != null) {
|
||||||
|
IBaseResource codeSystem = myDaoRegistry.getResourceDao("CodeSystem").read(theCodeSystemId);
|
||||||
|
codeSystemUrl = CommonCodeSystemsTerminologyService.getCodeSystemUrl(codeSystem);
|
||||||
|
} else if (haveIdentifierParam) {
|
||||||
|
codeSystemUrl = theCodeSystemUrl;
|
||||||
|
} else {
|
||||||
|
throw new InvalidRequestException("Either CodeSystem ID or CodeSystem identifier must be provided. Unable to validate.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
String code = theCode;
|
||||||
|
String version = theVersion;
|
||||||
|
String display = theDisplay;
|
||||||
|
|
||||||
|
if (haveCodeableConcept) {
|
||||||
|
for (int i = 0; i < codeableConcept.getCoding().size(); i++) {
|
||||||
|
Coding nextCoding = codeableConcept.getCoding().get(i);
|
||||||
|
if (nextCoding.hasSystem()) {
|
||||||
|
if (!codeSystemUrl.equalsIgnoreCase(nextCoding.getSystem())) {
|
||||||
|
throw new InvalidRequestException("Coding.system '" + nextCoding.getSystem() + "' does not equal with CodeSystem.url '" + theCodeSystemUrl + "'. Unable to validate.");
|
||||||
|
}
|
||||||
|
codeSystemUrl = nextCoding.getSystem();
|
||||||
|
}
|
||||||
|
code = nextCoding.getCode();
|
||||||
|
display = nextCoding.getDisplay();
|
||||||
|
CodeValidationResult nextValidation = codeSystemValidateCode(codeSystemUrl, version, code, display);
|
||||||
|
if (nextValidation.isOk() || i == codeableConcept.getCoding().size() - 1) {
|
||||||
|
return nextValidation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (haveCoding) {
|
||||||
|
if (coding.hasSystem()) {
|
||||||
|
if (!codeSystemUrl.equalsIgnoreCase(coding.getSystem())) {
|
||||||
|
throw new InvalidRequestException("Coding.system '" + coding.getSystem() + "' does not equal with CodeSystem.url '" + theCodeSystemUrl + "'. Unable to validate.");
|
||||||
|
}
|
||||||
|
codeSystemUrl = coding.getSystem();
|
||||||
|
}
|
||||||
|
code = coding.getCode();
|
||||||
|
display = coding.getDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
return codeSystemValidateCode(codeSystemUrl, version, code, display);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private CodeValidationResult codeSystemValidateCode(String theCodeSystemUrl, String theCodeSystemVersion, String theCode, String theDisplay) {
|
||||||
|
|
||||||
|
CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder();
|
||||||
|
CriteriaQuery<TermConcept> query = criteriaBuilder.createQuery(TermConcept.class);
|
||||||
|
Root<TermConcept> root = query.from(TermConcept.class);
|
||||||
|
|
||||||
|
Fetch<TermCodeSystemVersion, TermConcept> systemVersionFetch = root.fetch("myCodeSystem", JoinType.INNER);
|
||||||
|
Join<TermCodeSystemVersion, TermConcept> systemVersionJoin = (Join<TermCodeSystemVersion, TermConcept>)systemVersionFetch;
|
||||||
|
Fetch<TermCodeSystem, TermCodeSystemVersion> systemFetch = systemVersionFetch.fetch("myCodeSystem", JoinType.INNER);
|
||||||
|
Join<TermCodeSystem, TermCodeSystemVersion> systemJoin = (Join<TermCodeSystem, TermCodeSystemVersion>)systemFetch;
|
||||||
|
|
||||||
|
ArrayList<Predicate> predicates = new ArrayList<>();
|
||||||
|
|
||||||
|
if (isNotBlank(theCode)) {
|
||||||
|
predicates.add(criteriaBuilder.equal(root.get("myCode"), theCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNotBlank(theDisplay)) {
|
||||||
|
predicates.add(criteriaBuilder.equal(root.get("myDisplay"), theDisplay));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNoneBlank(theCodeSystemUrl)) {
|
||||||
|
predicates.add(criteriaBuilder.equal(systemJoin.get("myCodeSystemUri"), theCodeSystemUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isNoneBlank(theCodeSystemVersion)) {
|
||||||
|
predicates.add(criteriaBuilder.equal(systemVersionJoin.get("myCodeSystemVersionId"), theCodeSystemVersion));
|
||||||
|
}
|
||||||
|
|
||||||
|
Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0]));
|
||||||
|
query.where(outerPredicate);
|
||||||
|
|
||||||
|
final TypedQuery<TermConcept> typedQuery = myEntityManager.createQuery(query.select(root));
|
||||||
|
org.hibernate.query.Query<TermConcept> hibernateQuery = (org.hibernate.query.Query<TermConcept>) typedQuery;
|
||||||
|
hibernateQuery.setFetchSize(myFetchSize);
|
||||||
|
List<TermConcept> resultsList = hibernateQuery.getResultList();
|
||||||
|
|
||||||
|
if (!resultsList.isEmpty()) {
|
||||||
|
TermConcept concept = resultsList.get(0);
|
||||||
|
return new CodeValidationResult().setCode(concept.getCode()).setDisplay(concept.getDisplay());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isBlank(theDisplay))
|
||||||
|
return createFailureCodeValidationResult(theCodeSystemUrl, theCode);
|
||||||
|
else
|
||||||
|
return createFailureCodeValidationResult(theCodeSystemUrl, theCode, " - Concept Display : " + theDisplay);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,4 +126,9 @@ public interface ITermReadSvc extends IValidationSupport {
|
||||||
*/
|
*/
|
||||||
boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet);
|
boolean isValueSetPreExpandedForCodeValidation(IBaseResource theValueSet);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Version independent
|
||||||
|
*/
|
||||||
|
CodeValidationResult codeSystemValidateCode(IIdType theCodeSystemId, String theValueSetUrl, String theVersion, String theCode, String theDisplay, IBaseDatatype theCoding, IBaseDatatype theCodeableConcept);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,433 @@
|
||||||
|
package ca.uhn.fhir.jpa.provider.r4;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
|
import org.hl7.fhir.r4.model.CodeType;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
import org.hl7.fhir.r4.model.UriType;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.transaction.TransactionStatus;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||||
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
|
||||||
|
|
||||||
|
public class ResourceProviderR4CodeSystemValidationTest extends BaseResourceProviderR4Test {
|
||||||
|
|
||||||
|
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderR4CodeSystemValidationTest.class);
|
||||||
|
|
||||||
|
private IIdType myCsId;
|
||||||
|
private static final String CS_ACMS_URL = "http://acme.org";
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
@Transactional
|
||||||
|
public void before02() throws IOException {
|
||||||
|
loadAndPersistCodeSystem();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadAndPersistCodeSystem() throws IOException {
|
||||||
|
CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml");
|
||||||
|
codeSystem.setId("CodeSystem/cs");
|
||||||
|
persistCodeSystem(codeSystem);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void persistCodeSystem(CodeSystem theCodeSystem) {
|
||||||
|
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
|
||||||
|
@Override
|
||||||
|
protected void doInTransactionWithoutResult(@Nonnull TransactionStatus theStatus) {
|
||||||
|
myCsId = myCodeSystemDao.create(theCodeSystem, mySrd).getId().toUnqualifiedVersionless();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
myCodeSystemDao.readEntity(myCsId, null).getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCode() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("code").setValue(new CodeType("8452-5"));
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeNotFoundByCode() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("code").setValue(new CodeType("8452-5-a"));
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(false, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Unknown code {http://acme.org}8452-5-a", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodeMatchDisplay() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("code").setValue(new CodeType("8452-5"));
|
||||||
|
inParams.addParameter().setName("display").setValue(new CodeType("Systolic blood pressure.inspiration - expiration"));
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodeNotMatchDisplay() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("code").setValue(new CodeType("8452-5"));
|
||||||
|
inParams.addParameter().setName("display").setValue(new CodeType("Old Systolic blood pressure.inspiration - expiration"));
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(false, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Unknown code {http://acme.org}8452-5 - Concept Display : Old Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodeWithoutUrl() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("code").setValue(new CodeType("8452-5"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("HTTP 400 Bad Request: Either CodeSystem ID or CodeSystem identifier must be provided. Unable to validate.",e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodeWithId() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("code").setValue(new CodeType("8452-5"));
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onInstance(myCsId).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeWithoutCodeOrCodingOrCodeableConcept() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("display").setValue(new CodeType("Systolic blood pressure.inspiration - expiration"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("HTTP 400 Bad Request: No code, coding, or codeableConcept provided to validate.",e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeWithCodeAndCoding() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("code").setValue(new CodeType("8452-5"));
|
||||||
|
inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-1")));
|
||||||
|
|
||||||
|
try {
|
||||||
|
myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("HTTP 400 Bad Request: $validate-code can only validate (code) OR (coding) OR (codeableConcept)",e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodingWithUrlNotMatch() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5").setSystem("http://url2")));
|
||||||
|
|
||||||
|
try {
|
||||||
|
myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCoding() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5")));
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodingWithSystem() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5").setSystem(CS_ACMS_URL)));
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodingUrlNotMatch() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5").setSystem("http://url2")));
|
||||||
|
|
||||||
|
try {
|
||||||
|
myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodingWithDisplay() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5").setDisplay("Systolic blood pressure.inspiration - expiration")));
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeNotFoundByCoding() throws Exception {
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("coding").setValue((new Coding().setCode("8452-5-a")));
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(false, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Unknown code {http://acme.org}8452-5-a", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodeableConcept() throws Exception {
|
||||||
|
|
||||||
|
CodeableConcept cc = new CodeableConcept();
|
||||||
|
cc.addCoding().setCode("8452-5");
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("codeableConcept").setValue(cc);
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodeableConceptWithSystem() throws Exception {
|
||||||
|
|
||||||
|
CodeableConcept cc = new CodeableConcept();
|
||||||
|
cc.addCoding().setCode("8452-5").setSystem(CS_ACMS_URL);
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("codeableConcept").setValue(cc);
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodeableConceptWithDisplay() throws Exception {
|
||||||
|
|
||||||
|
CodeableConcept cc = new CodeableConcept();
|
||||||
|
cc.addCoding().setCode("8452-5").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure.inspiration - expiration");
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("codeableConcept").setValue(cc);
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeNotFoundByCodeableConcept() throws Exception {
|
||||||
|
|
||||||
|
CodeableConcept cc = new CodeableConcept();
|
||||||
|
cc.addCoding().setCode("8452-5-a");
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("codeableConcept").setValue(cc);
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(false, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Unknown code {http://acme.org}8452-5-a", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodeableConceptUrlNotMatch() throws Exception {
|
||||||
|
|
||||||
|
CodeableConcept cc = new CodeableConcept();
|
||||||
|
cc.addCoding().setCode("8452-5").setSystem("http://url2").setDisplay("Systolic blood pressure.inspiration - expiration");
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("codeableConcept").setValue(cc);
|
||||||
|
|
||||||
|
try {
|
||||||
|
myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
} catch (InvalidRequestException e) {
|
||||||
|
assertEquals("HTTP 400 Bad Request: Coding.system 'http://url2' does not equal with CodeSystem.url 'http://acme.org'. Unable to validate.",e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodeableConceptWithMultipleMatchedEntries() throws Exception {
|
||||||
|
|
||||||
|
CodeableConcept cc = new CodeableConcept();
|
||||||
|
cc.addCoding().setCode("8452-5").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure.inspiration - expiration");
|
||||||
|
cc.addCoding().setCode("8451-7").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure--inspiration");
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("codeableConcept").setValue(cc);
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodeableConceptWithMultipleMatchedFirstEntry() throws Exception {
|
||||||
|
|
||||||
|
CodeableConcept cc = new CodeableConcept();
|
||||||
|
cc.addCoding().setCode("8452-5").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure.inspiration - expiration");
|
||||||
|
cc.addCoding().setCode("8451-7-a").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure--inspiration");
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("codeableConcept").setValue(cc);
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Systolic blood pressure.inspiration - expiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testValidateCodeFoundByCodeableConceptWithMultipleMatchedSecondEntry() throws Exception {
|
||||||
|
|
||||||
|
CodeableConcept cc = new CodeableConcept();
|
||||||
|
cc.addCoding().setCode("8452-5-a").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure.inspiration - expiration");
|
||||||
|
cc.addCoding().setCode("8451-7").setSystem(CS_ACMS_URL).setDisplay("Systolic blood pressure--inspiration");
|
||||||
|
|
||||||
|
Parameters inParams = new Parameters();
|
||||||
|
inParams.addParameter().setName("url").setValue(new UriType(CS_ACMS_URL));
|
||||||
|
inParams.addParameter().setName("codeableConcept").setValue(cc);
|
||||||
|
|
||||||
|
Parameters respParam = myClient.operation().onType(CodeSystem.class).named("validate-code").withParameters(inParams).execute();
|
||||||
|
|
||||||
|
String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam);
|
||||||
|
ourLog.info(resp);
|
||||||
|
|
||||||
|
assertEquals(true, ((BooleanType) respParam.getParameter().get(0).getValue()).booleanValue());
|
||||||
|
assertEquals("Systolic blood pressure--inspiration", ((StringType) respParam.getParameter().get(1).getValue()).getValueAsString());
|
||||||
|
}
|
||||||
|
}
|
|
@ -332,6 +332,32 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport {
|
||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getCodeSystemUrl(@Nonnull IBaseResource theCodeSystem) {
|
||||||
|
String url;
|
||||||
|
switch (theCodeSystem.getStructureFhirVersionEnum()) {
|
||||||
|
case DSTU2_HL7ORG: {
|
||||||
|
url = ((CodeSystem) theCodeSystem).getUrl();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DSTU3: {
|
||||||
|
url = ((org.hl7.fhir.dstu3.model.CodeSystem) theCodeSystem).getUrl();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case R4: {
|
||||||
|
url = ((org.hl7.fhir.r4.model.CodeSystem) theCodeSystem).getUrl();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case R5: {
|
||||||
|
url = ((org.hl7.fhir.r5.model.CodeSystem) theCodeSystem).getUrl();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DSTU2:
|
||||||
|
case DSTU2_1:
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Can not handle version: " + theCodeSystem.getStructureFhirVersionEnum());
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
private static HashMap<String, String> buildUspsCodes() {
|
private static HashMap<String, String> buildUspsCodes() {
|
||||||
HashMap<String, String> uspsCodes = new HashMap<>();
|
HashMap<String, String> uspsCodes = new HashMap<>();
|
||||||
uspsCodes.put("AK", "Alaska");
|
uspsCodes.put("AK", "Alaska");
|
||||||
|
|
Loading…
Reference in New Issue