From fe25d93fe6737c85a62c2ac53ce031f99155d57a Mon Sep 17 00:00:00 2001 From: James Agnew Date: Mon, 2 Apr 2018 09:08:11 -0400 Subject: [PATCH 01/19] Sync with master --- .../dao/data/ITermConceptDesignationDao.java | 28 ++ .../BaseResourceIndexedSearchParam.java | 2 +- .../ca/uhn/fhir/jpa/entity/TermConcept.java | 65 ++-- .../jpa/entity/TermConceptDesignation.java | 103 ++++++ .../jpa/term/BaseHapiTerminologySvcImpl.java | 295 ++++++++++++------ .../uhn/fhir/jpa/term/loinc/BaseHandler.java | 13 + .../fhir/jpa/term/loinc/LoincPartHandler.java | 37 ++- .../jpa/term/TerminologySvcImplDstu3Test.java | 5 + src/changes/changes.xml | 125 +++++++- 9 files changed, 537 insertions(+), 136 deletions(-) create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDesignationDao.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDesignationDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDesignationDao.java new file mode 100644 index 00000000000..ad7ca7265d4 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDesignationDao.java @@ -0,0 +1,28 @@ +package ca.uhn.fhir.jpa.dao.data; + +import ca.uhn.fhir.jpa.entity.TermConceptDesignation; +import org.springframework.data.jpa.repository.JpaRepository; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +public interface ITermConceptDesignationDao extends JpaRepository { + // nothing +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java index 5133859d649..4f977e65502 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java @@ -53,7 +53,7 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable { private Long myResourcePid; @Field() - @Column(name = "RES_TYPE", nullable = false) + @Column(name = "RES_TYPE", nullable = false, length = 30) private String myResourceType; @Field() diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java index ca2ef4ace87..ff799c97dc1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java @@ -81,6 +81,9 @@ public class TermConcept implements Serializable { @FieldBridge(impl = TermConceptPropertyFieldBridge.class) private Collection myProperties; + @OneToMany(mappedBy = "myConcept", orphanRemoval = true) + private Collection myDesignations; + @Id() @SequenceGenerator(name = "SEQ_CONCEPT_PID", sequenceName = "SEQ_CONCEPT_PID") @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PID") @@ -123,6 +126,13 @@ public class TermConcept implements Serializable { } } + public TermConceptDesignation addDesignation() { + TermConceptDesignation designation = new TermConceptDesignation(); + designation.setConcept(this); + getDesignations().add(designation); + return designation; + } + private TermConceptProperty addProperty(@Nonnull TermConceptPropertyTypeEnum thePropertyType, @Nonnull String thePropertyName, @Nonnull String thePropertyValue) { Validate.notBlank(thePropertyName); @@ -189,6 +199,29 @@ public class TermConcept implements Serializable { } } + public List getCodingProperties(String thePropertyName) { + List retVal = new ArrayList<>(); + for (TermConceptProperty next : getProperties()) { + if (thePropertyName.equals(next.getKey())) { + if (next.getType() == TermConceptPropertyTypeEnum.CODING) { + Coding coding = new Coding(); + coding.setSystem(next.getCodeSystem()); + coding.setCode(next.getValue()); + coding.setDisplay(next.getDisplay()); + retVal.add(coding); + } + } + } + return retVal; + } + + public Collection getDesignations() { + if (myDesignations == null) { + myDesignations = new ArrayList<>(); + } + return myDesignations; + } + public String getDisplay() { return myDisplay; } @@ -231,6 +264,14 @@ public class TermConcept implements Serializable { return myProperties; } + public Integer getSequence() { + return mySequence; + } + + public void setSequence(Integer theSequence) { + mySequence = theSequence; + } + public List getStringProperties(String thePropertyName) { List retVal = new ArrayList<>(); for (TermConceptProperty next : getProperties()) { @@ -243,30 +284,6 @@ public class TermConcept implements Serializable { return retVal; } - public List getCodingProperties(String thePropertyName) { - List retVal = new ArrayList<>(); - for (TermConceptProperty next : getProperties()) { - if (thePropertyName.equals(next.getKey())) { - if (next.getType() == TermConceptPropertyTypeEnum.CODING) { - Coding coding = new Coding(); - coding.setSystem(next.getCodeSystem()); - coding.setCode(next.getValue()); - coding.setDisplay(next.getDisplay()); - retVal.add(coding); - } - } - } - return retVal; - } - - public Integer getSequence() { - return mySequence; - } - - public void setSequence(Integer theSequence) { - mySequence = theSequence; - } - public String getStringProperty(String thePropertyName) { List properties = getStringProperties(thePropertyName); if (properties.size() > 0) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java new file mode 100644 index 00000000000..f0c6c7e6d14 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java @@ -0,0 +1,103 @@ +package ca.uhn.fhir.jpa.entity; + +/* + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import javax.persistence.*; +import java.io.Serializable; + +@Entity +@Table(name = "TRM_CONCEPT_DESIG", uniqueConstraints = { +}, indexes = { +}) +public class TermConceptDesignation implements Serializable { + + private static final long serialVersionUID = 1L; + @ManyToOne + @JoinColumn(name = "CONCEPT_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTPROP_CONCEPT")) + private TermConcept myConcept; + @Id() + @SequenceGenerator(name = "SEQ_CONCEPT_DESIG_PID", sequenceName = "SEQ_CONCEPT_DESIG_PID") + @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_DESIG_PID") + @Column(name = "PID") + private Long myId; + @Column(name = "LANG", length = 50, nullable = true) + private String myLanguage; + @Column(name = "USE_SYSTEM", length = 50, nullable = true) + private String myUseSystem; + @Column(name = "USE_CODE", length = 50, nullable = true) + private String myUseCode; + @Column(name = "USE_DISPLAY", length = 50, nullable = true) + private String myUseDisplay; + @Column(name = "VAL", length = 50, nullable = false) + private String myValue; + + public String getLanguage() { + return myLanguage; + } + + public TermConceptDesignation setLanguage(String theLanguage) { + myLanguage = theLanguage; + return this; + } + + public String getUseCode() { + return myUseCode; + } + + public TermConceptDesignation setUseCode(String theUseCode) { + myUseCode = theUseCode; + return this; + } + + public String getUseDisplay() { + return myUseDisplay; + } + + public TermConceptDesignation setUseDisplay(String theUseDisplay) { + myUseDisplay = theUseDisplay; + return this; + } + + public String getUseSystem() { + return myUseSystem; + } + + public TermConceptDesignation setUseSystem(String theUseSystem) { + myUseSystem = theUseSystem; + return this; + } + + public String getValue() { + return myValue; + } + + public TermConceptDesignation setValue(String theValue) { + myValue = theValue; + return this; + } + + public TermConceptDesignation setConcept(TermConcept theConcept) { + myConcept = theConcept; + return this; + } + + +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index 9ad462731e2..86fe953d6b0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -78,6 +78,8 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc @Autowired protected ITermConceptPropertyDao myConceptPropertyDao; @Autowired + protected ITermConceptDesignationDao myConceptDesignationDao; + @Autowired protected FhirContext myContext; @PersistenceContext(type = PersistenceContextType.TRANSACTION) protected EntityManager myEntityManager; @@ -94,7 +96,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc private boolean myProcessDeferred = true; @Autowired private PlatformTransactionManager myTransactionMgr; - @Autowired + @Autowired(required = false) private IFhirResourceDaoCodeSystem myCodeSystemResourceDao; private void addCodeIfNotAlreadyAdded(String theCodeSystem, ValueSet.ValueSetExpansionComponent theExpansionComponent, Set theAddedCodes, TermConcept theConcept) { @@ -106,6 +108,19 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } } + private void addConceptsToList(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set theAddedCodes, String theSystem, List theConcept) { + for (CodeSystem.ConceptDefinitionComponent next : theConcept) { + if (!theAddedCodes.contains(next.getCode())) { + theAddedCodes.add(next.getCode()); + ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains(); + contains.setCode(next.getCode()); + contains.setSystem(theSystem); + contains.setDisplay(next.getDisplay()); + } + addConceptsToList(theExpansionComponent, theAddedCodes, theSystem, next.getConcept()); + } + } + private void addDisplayFilterExact(QueryBuilder qb, BooleanJunction bool, ValueSet.ConceptSetFilterComponent nextFilter) { bool.must(qb.phrase().onField("myDisplay").sentence(nextFilter.getValue()).createQuery()); } @@ -142,11 +157,35 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc @Override public void deleteCodeSystem(TermCodeSystem theCodeSystem) { ourLog.info(" * Deleting code system {}", theCodeSystem.getPid()); + + myEntityManager.flush(); + TermCodeSystem cs = myCodeSystemDao.findOne(theCodeSystem.getPid()); + cs.setCurrentVersion(null); + myCodeSystemDao.save(cs); + myCodeSystemDao.flush(); + + int i = 0; for (TermCodeSystemVersion next : myCodeSystemVersionDao.findByCodeSystemResource(theCodeSystem.getPid())) { myConceptParentChildLinkDao.deleteByCodeSystemVersion(next.getPid()); - myConceptDao.deleteByCodeSystemVersion(next.getPid()); + for (TermConcept nextConcept : myConceptDao.findByCodeSystemVersion(next.getPid())) { + myConceptPropertyDao.delete(nextConcept.getProperties()); + myConceptDesignationDao.delete(nextConcept.getDesignations()); + myConceptDao.delete(nextConcept); + } + if (next.getCodeSystem().getCurrentVersion() == next) { + next.getCodeSystem().setCurrentVersion(null); + myCodeSystemDao.save(next.getCodeSystem()); + } + myCodeSystemVersionDao.delete(next); + + if (i % 1000 == 0) { + myEntityManager.flush(); + } } - myCodeSystemDao.delete(theCodeSystem.getPid()); + myCodeSystemVersionDao.deleteForCodeSystem(theCodeSystem); + myCodeSystemDao.delete(theCodeSystem); + + myEntityManager.flush(); } private int ensureParentsSaved(Collection theParents) { @@ -171,112 +210,140 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc @Override @Transactional(propagation = Propagation.REQUIRED) public ValueSet expandValueSet(ValueSet theValueSetToExpand) { - - ValueSet.ConceptSetComponent include = theValueSetToExpand.getCompose().getIncludeFirstRep(); - String system = include.getSystem(); - ourLog.info("Starting expansion around code system: {}", system); - - TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system); - if (cs == null) { - throw new InvalidRequestException("Unknown code system: " + system); - } - - TermCodeSystemVersion csv = cs.getCurrentVersion(); - ValueSet.ValueSetExpansionComponent expansionComponent = new ValueSet.ValueSetExpansionComponent(); Set addedCodes = new HashSet<>(); boolean haveIncludeCriteria = false; - /* - * Include Concepts - */ - for (ValueSet.ConceptReferenceComponent next : include.getConcept()) { - String nextCode = next.getCode(); - if (isNotBlank(nextCode) && !addedCodes.contains(nextCode)) { - haveIncludeCriteria = true; - TermConcept code = findCode(system, nextCode); - if (code != null) { - addedCodes.add(nextCode); - ValueSet.ValueSetExpansionContainsComponent contains = expansionComponent.addContains(); - contains.setCode(nextCode); - contains.setSystem(system); - contains.setDisplay(code.getDisplay()); - } - } - } + for (ValueSet.ConceptSetComponent include : theValueSetToExpand.getCompose().getInclude()) { + String system = include.getSystem(); + if (isNotBlank(system)) { + ourLog.info("Starting expansion around code system: {}", system); - /* - * Filters - */ + TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system); + if (cs != null) { + TermCodeSystemVersion csv = cs.getCurrentVersion(); - if (include.getFilter().size() > 0) { - haveIncludeCriteria = true; - - FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager); - QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get(); - BooleanJunction bool = qb.bool(); - - bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery()); - - for (ValueSet.ConceptSetFilterComponent nextFilter : include.getFilter()) { - if (isBlank(nextFilter.getValue()) && nextFilter.getOp() == null && isBlank(nextFilter.getProperty())) { - continue; - } - - if (isBlank(nextFilter.getValue()) || nextFilter.getOp() == null || isBlank(nextFilter.getProperty())) { - throw new InvalidRequestException("Invalid filter, must have fields populated: property op value"); - } - - - if (nextFilter.getProperty().equals("display:exact") && nextFilter.getOp() == ValueSet.FilterOperator.EQUAL) { - addDisplayFilterExact(qb, bool, nextFilter); - } else if ("display".equals(nextFilter.getProperty()) && nextFilter.getOp() == ValueSet.FilterOperator.EQUAL) { - if (nextFilter.getValue().trim().contains(" ")) { - addDisplayFilterExact(qb, bool, nextFilter); - } else { - addDisplayFilterInexact(qb, bool, nextFilter); - } - } else if ((nextFilter.getProperty().equals("concept") || nextFilter.getProperty().equals("code")) && nextFilter.getOp() == ValueSet.FilterOperator.ISA) { - - TermConcept code = findCode(system, nextFilter.getValue()); - if (code == null) { - throw new InvalidRequestException("Invalid filter criteria - code does not exist: {" + system + "}" + nextFilter.getValue()); + /* + * Include Concepts + */ + for (ValueSet.ConceptReferenceComponent next : include.getConcept()) { + String nextCode = next.getCode(); + if (isNotBlank(nextCode) && !addedCodes.contains(nextCode)) { + haveIncludeCriteria = true; + TermConcept code = findCode(system, nextCode); + if (code != null) { + addedCodes.add(nextCode); + ValueSet.ValueSetExpansionContainsComponent contains = expansionComponent.addContains(); + contains.setCode(nextCode); + contains.setSystem(system); + contains.setDisplay(code.getDisplay()); + } + } } - ourLog.info(" * Filtering on codes with a parent of {}/{}/{}", code.getId(), code.getCode(), code.getDisplay()); - bool.must(qb.keyword().onField("myParentPids").matching("" + code.getId()).createQuery()); + /* + * Filters + */ - } else { + if (include.getFilter().size() > 0) { + haveIncludeCriteria = true; + + FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager); + QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get(); + BooleanJunction bool = qb.bool(); + + bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery()); + + for (ValueSet.ConceptSetFilterComponent nextFilter : include.getFilter()) { + if (isBlank(nextFilter.getValue()) && nextFilter.getOp() == null && isBlank(nextFilter.getProperty())) { + continue; + } + + if (isBlank(nextFilter.getValue()) || nextFilter.getOp() == null || isBlank(nextFilter.getProperty())) { + throw new InvalidRequestException("Invalid filter, must have fields populated: property op value"); + } + + + if (nextFilter.getProperty().equals("display:exact") && nextFilter.getOp() == ValueSet.FilterOperator.EQUAL) { + addDisplayFilterExact(qb, bool, nextFilter); + } else if ("display".equals(nextFilter.getProperty()) && nextFilter.getOp() == ValueSet.FilterOperator.EQUAL) { + if (nextFilter.getValue().trim().contains(" ")) { + addDisplayFilterExact(qb, bool, nextFilter); + } else { + addDisplayFilterInexact(qb, bool, nextFilter); + } + } else if ((nextFilter.getProperty().equals("concept") || nextFilter.getProperty().equals("code")) && nextFilter.getOp() == ValueSet.FilterOperator.ISA) { + + TermConcept code = findCode(system, nextFilter.getValue()); + if (code == null) { + throw new InvalidRequestException("Invalid filter criteria - code does not exist: {" + system + "}" + nextFilter.getValue()); + } + + ourLog.info(" * Filtering on codes with a parent of {}/{}/{}", code.getId(), code.getCode(), code.getDisplay()); + bool.must(qb.keyword().onField("myParentPids").matching("" + code.getId()).createQuery()); + + } else { // bool.must(qb.keyword().onField("myProperties").matching(nextFilter.getStringProperty()+"="+nextFilter.getValue()).createQuery()); - bool.must(qb.phrase().onField("myProperties").sentence(nextFilter.getProperty() + "=" + nextFilter.getValue()).createQuery()); + bool.must(qb.phrase().onField("myProperties").sentence(nextFilter.getProperty() + "=" + nextFilter.getValue()).createQuery()); + + } + } + + Query luceneQuery = bool.createQuery(); + FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class); + jpaQuery.setMaxResults(1000); + + StopWatch sw = new StopWatch(); + + @SuppressWarnings("unchecked") + List result = jpaQuery.getResultList(); + + ourLog.info("Expansion completed in {}ms", sw.getMillis()); + + for (TermConcept nextConcept : result) { + addCodeIfNotAlreadyAdded(system, expansionComponent, addedCodes, nextConcept); + } + + expansionComponent.setTotal(jpaQuery.getResultSize()); + } + + if (!haveIncludeCriteria) { + List allCodes = findCodes(system); + for (TermConcept nextConcept : allCodes) { + addCodeIfNotAlreadyAdded(system, expansionComponent, addedCodes, nextConcept); + } + } + + } else { + // No codesystem matching the URL found in the database + + CodeSystem codeSystemFromContext = getCodeSystemFromContext(system); + if (codeSystemFromContext == null) { + throw new InvalidRequestException("Unknown code system: " + system); + } + + if (include.getConcept().isEmpty() == false) { + for (ValueSet.ConceptReferenceComponent next : include.getConcept()) { + String nextCode = next.getCode(); + if (isNotBlank(nextCode) && !addedCodes.contains(nextCode)) { + CodeSystem.ConceptDefinitionComponent code = findCode(codeSystemFromContext.getConcept(), nextCode); + if (code != null) { + addedCodes.add(nextCode); + ValueSet.ValueSetExpansionContainsComponent contains = expansionComponent.addContains(); + contains.setCode(nextCode); + contains.setSystem(system); + contains.setDisplay(code.getDisplay()); + } + } + } + } else { + List concept = codeSystemFromContext.getConcept(); + addConceptsToList(expansionComponent, addedCodes, system, concept); + } } } - - Query luceneQuery = bool.createQuery(); - FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class); - jpaQuery.setMaxResults(1000); - - StopWatch sw = new StopWatch(); - - @SuppressWarnings("unchecked") - List result = jpaQuery.getResultList(); - - ourLog.info("Expansion completed in {}ms", sw.getMillis()); - - for (TermConcept nextConcept : result) { - addCodeIfNotAlreadyAdded(system, expansionComponent, addedCodes, nextConcept); - } - - expansionComponent.setTotal(jpaQuery.getResultSize()); - } - - if (!haveIncludeCriteria) { - List allCodes = findCodes(system); - for (TermConcept nextConcept : allCodes) { - addCodeIfNotAlreadyAdded(system, expansionComponent, addedCodes, nextConcept); - } } ValueSet valueSet = new ValueSet(); @@ -284,9 +351,17 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc return valueSet; } - @Override - public List expandValueSet(String theValueSet) { - throw new UnsupportedOperationException(); // FIXME implement + protected List expandValueSetAndReturnVersionIndependentConcepts(org.hl7.fhir.r4.model.ValueSet theValueSetToExpandR4) { + org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent expandedR4 = expandValueSet(theValueSetToExpandR4).getExpansion(); + + ArrayList retVal = new ArrayList<>(); + for (org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent nextContains : expandedR4.getContains()) { + retVal.add( + new VersionIndependentConcept() + .setSystem(nextContains.getSystem()) + .setCode(nextContains.getCode())); + } + return retVal; } private void fetchChildren(TermConcept theConcept, Set theSetToPopulate) { @@ -312,6 +387,16 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } } + private CodeSystem.ConceptDefinitionComponent findCode(List theConcepts, String theCode) { + for (CodeSystem.ConceptDefinitionComponent next : theConcepts) { + if (theCode.equals(next.getCode())) { + return next; + } + findCode(next.getConcept(), theCode); + } + return null; + } + @Override public TermConcept findCode(String theCodeSystem, String theCode) { TermCodeSystemVersion csv = findCurrentCodeSystemVersionForSystem(theCodeSystem); @@ -398,6 +483,8 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc return myCodeSystemDao.findByCodeSystemUri(theSystem); } + protected abstract CodeSystem getCodeSystemFromContext(String theSystem); + private void persistChildren(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, IdentityHashMap theConceptsStack, int theTotalConcepts) { if (theConceptsStack.put(theConcept, PLACEHOLDER_OBJECT) != null) { return; @@ -429,10 +516,14 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } } - for (TermConceptProperty next : theConcept.getProperties()){ + for (TermConceptProperty next : theConcept.getProperties()) { myConceptPropertyDao.save(next); } + for (TermConceptDesignation next : theConcept.getDesignations()) { + myConceptDesignationDao.save(next); + } + } private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) { @@ -636,12 +727,16 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc for (TermCodeSystemVersion next : existing) { ourLog.info(" * Deleting code system version {}", next.getPid()); myConceptParentChildLinkDao.deleteByCodeSystemVersion(next.getPid()); - myConceptDao.deleteByCodeSystemVersion(next.getPid()); + for (TermConcept nextConcept : myConceptDao.findByCodeSystemVersion(next.getPid())) { + myConceptPropertyDao.delete(nextConcept.getProperties()); + myConceptDao.delete(nextConcept); + } } ourLog.info("Flushing..."); myConceptParentChildLinkDao.flush(); + myConceptPropertyDao.flush(); myConceptDao.flush(); ourLog.info("Done flushing"); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java index f35c5e6c5da..db3ba9ef002 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java @@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.term.loinc; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IRecordHandler; import org.hl7.fhir.r4.model.ConceptMap; +import org.hl7.fhir.r4.model.ContactPoint; import org.hl7.fhir.r4.model.Enumerations; import org.hl7.fhir.r4.model.ValueSet; @@ -79,6 +80,12 @@ abstract class BaseHandler implements IRecordHandler { conceptMap.setId(theMapping.getConceptMapId()); conceptMap.setUrl(theMapping.getConceptMapUri()); conceptMap.setName(theMapping.getConceptMapName()); + conceptMap.setPublisher("Regentrief Institute, Inc."); + conceptMap.addContact() + .setName("Regentrief Institute, Inc.") + .addTelecom() + .setSystem(ContactPoint.ContactPointSystem.URL) + .setValue("https://loinc.org"); myIdToConceptMaps.put(theMapping.getConceptMapId(), conceptMap); myConceptMaps.add(conceptMap); } else { @@ -144,6 +151,12 @@ abstract class BaseHandler implements IRecordHandler { vs.setId(theValueSetId); vs.setName(theValueSetName); vs.setStatus(Enumerations.PublicationStatus.ACTIVE); + vs.setPublisher("Regentrief Institute, Inc."); + vs.addContact() + .setName("Regentrief Institute, Inc.") + .addTelecom() + .setSystem(ContactPoint.ContactPointSystem.URL) + .setValue("https://loinc.org"); myIdToValueSet.put(theValueSetId, vs); myValueSets.add(vs); } else { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartHandler.java index 3dadb4e0e04..bfcd1aea80d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IRecordHandler; @@ -9,6 +29,7 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.HashMap; import java.util.Map; +import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.trim; public class LoincPartHandler implements IRecordHandler { @@ -30,15 +51,23 @@ public class LoincPartHandler implements IRecordHandler { String partTypeName = trim(theRecord.get("PartTypeName")); String partName = trim(theRecord.get("PartName")); String partDisplayName = trim(theRecord.get("PartDisplayName")); - String status = trim(theRecord.get("Status")); - if (!"ACTIVE".equals(status)) { - return; - } + // Per Dan's note, we include deprecated parts +// String status = trim(theRecord.get("Status")); +// if (!"ACTIVE".equals(status)) { +// return; +// } TermConcept concept = new TermConcept(myCodeSystemVersion, partNumber); concept.setDisplay(partName); + if (isNotBlank(partDisplayName)) { + concept.addDesignation() + .setConcept(concept) + .setUseDisplay("PartDisplayName") + .setValue(partDisplayName); + } + myCode2Concept.put(partDisplayName, concept); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java index ca5dc8639b0..ff796a5ccb0 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java @@ -181,6 +181,11 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { TermConcept childAAA = new TermConcept(cs, "childAAA"); childAAA.addPropertyString("propA", "valueAAA"); childAAA.addPropertyString("propB", "foo"); + childAAA.addDesignation() + .setUseSystem("http://designationsystem") + .setUseCode("somedesig") + .setUseDisplay("Designation Use") + .setValue("Bananas"); childAA.addChild(childAAA, RelationshipTypeEnum.ISA); TermConcept childAAB = new TermConcept(cs, "childAAB"); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 8869cddb77e..1e41560b15d 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -6,7 +6,37 @@ HAPI FHIR Changelog - + + + When performing a FHIR resource update in the JPA server + where the update happens within a transaction, and the + resource being updated contains placeholder IDs, and + the resource has not actually changed, a new version was + created even though there was not actually any change. + This particular combination of circumstances seems very + specific and improbable, but it is quite common for some + types of solutions (e.g. mapping HL7v2 data) so this + fix can prevent significant wasted space in some cases. + + + JPA server index tables did not have a column length specified + on the resource type column. This caused the default of 255 to + be used, which wasted a lot of space since resource names are all + less than 30 chars long and a single resource can have 10-100+ + index rows depending on configuration. This has now been set + to a much more sensible 30. + + + The LOINC uploader for the JPA Terminology Server has been + significantly beefed up so that it now takes in the full + set of LOINC distribution artifacts, and creates not only + the LOINC CodeSystem but a complete set of concept properties, + a number of LOINC ValueSets, and a number of LOINC ConceptMaps. + This work was sponsored by the Regenstrief Institute. Thanks + to Regenstrief for their support! + + + This release corrects an inefficiency in the JPA Server, but requires a schema change in order to update. Prior to this version of HAPI FHIR, a CLOB column @@ -14,12 +44,29 @@ tables: HFJ_RESOURCE and HFJ_RES_VER. Because the same content was stored in two places, the database consumed more space than is needed to.
]]> - In order to reduce this duplication, two columns have been removed from the - HFJ_RESOURCE table. This means that on any database that is being upgraded - to HAPI FHIR 3.3.0+, you will need to remove the columns - RES_TEXT and RES_ENCODING]]> (or - set them to nullable if you want an easy means of rolling back). Naturally - you should back your database up prior to making this change. + In order to reduce this duplication, the + RES_TEXT and RES_ENCODING]]> + columns have been + dropped]]> + from the + HFJ_RESOURCE]]> + table, and the + RES_TEXT and RES_ENCODING]]> + columns have been + made NULLABLE]]> + on the + HFJ_RES_VER]]> + table. +
]]> + The following migration script may be used to apply these changes to + your database. Naturally you should back your database up prior to + making this change. + ALTER TABLE hfj_resource DROP COLUMN res_text; +ALTER TABLE hfj_resource DROP COLUMN res_encoding; +ALTER TABLE hfj_res_ver ALTER COLUMN res_encoding DROP NOT NULL; +ALTER TABLE hfj_res_ver ALTER COLUMN res_text DROP NOT NULL; + ]]>
The validation module has been refactored to use the R4 (currently maintained) @@ -41,6 +88,28 @@ ]]> + + A number of HAPI FHIR modules have been converted so that they now work + as OSGi modules. Unlike the previous OSGi module, which was a mega-JAR + with all of HAPI FHIR in it, this is simply the appropriate + OSGi manifest inside the existing JARs. Thanks to John Poth + for the Pull Request! +
+ Note that this does not cover all modules in the project. Current support includes: +
    +
  • HAPI-FHIR structures DSTU2, HL7ORGDSTU2, DSTU2.1, DSTU3, R4
  • +
  • HAPI-FHIR Resource validation DSTU2, HL7ORGDSTU2, DSTU2.1, DSTU3, R4
  • +
  • Apache Karaf features for all the above
  • +
  • Integration Tests
  • +
+ Remaining work includes: +
    +
  • HAPI-FHIR Server support
  • +
  • HAPI-FHIR narrative support. This might be tricky as Thymeleaf doesn't support OSGi.
  • +
+ ]]> +
Fix a crash in the JSON parser when parsing extensions on repeatable elements (e.g. Patient.address.line) where there is an extension on the @@ -240,6 +309,48 @@ is supported according to the FHIR specification. Thanks to Jeff Chung for the pull request! + + JPA Server Operation Interceptor create/update methods will now no + longer be fired if the create/update operation being performed + is a no-op (e.g. a conditional create that did not need to perform + any action, or an update where the contents didn't actually change) + + + JPA server sometimes updated resources even though the client + supplied an update with no actual changes in it, due to + changes in the metadata section being considered content + changes. Thanks to Kyle Meadows for the pull request! + + + A new example project has been added called hapi-fhir-jpaserver-dynamic, + which uses application/environment properties to configure which version + of FHIR the server supports and other configuration. Thanks to + Anoush Mouradian for the pull request! + + + A new example project showing the use of JAX-RS Server Side Events has + been added. Thanks to Jens Kristian Villadsen for the pull request! + + + An unneccesary reference to the Javassist library has been + removed from the build. Thanks to Łukasz Dywicki for the + pull request! + + + Support has been added to the JPA server for the :not modifier. Thanks + to Łukasz Dywicki for the pull request! + + + Suport for the :contains string search parameter modifier has been added to + the JPA server. Thanks to Anthony Sute for the pull request! + + + All instances of DefaultProfileValidationSupport (i.e. one for + each version of FHIR) have been fixed so that they explicitly + close any InputStreams they open in order to read the built-in + profile resources. Leaving these open caused resource starvation + in some cases under heavy load. +
From b6232b5683b3ff63f614ea9a4baa0350264b5077 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Mon, 2 Apr 2018 21:28:31 -0400 Subject: [PATCH 02/19] loinc updates --- .../jpa/cds/example/CdsServerExample.java | 4 +- .../ca/uhn/fhir/jpa/demo/JpaServerDemo.java | 7 +- .../jpa/config/dstu3/BaseDstu3Config.java | 13 +- .../uhn/fhir/jpa/config/r4/BaseR4Config.java | 6 +- .../ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 74 ++- .../fhir/jpa/dao/BaseHapiFhirResourceDao.java | 75 +-- .../ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java | 29 +- .../uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java | 480 +++++++++--------- .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 5 +- .../dao/data/ITermCodeSystemVersionDao.java | 19 +- .../fhir/jpa/dao/data/ITermConceptDao.java | 4 +- .../jpa/dao/dstu3/FhirSystemDaoDstu3.java | 242 +++++---- .../dao/dstu3/JpaValidationSupportDstu3.java | 5 + .../uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java | 16 +- .../BaseResourceIndexedSearchParam.java | 2 +- .../uhn/fhir/jpa/entity/TermCodeSystem.java | 14 + .../jpa/entity/TermCodeSystemVersion.java | 2 +- .../jpa/entity/TermConceptDesignation.java | 2 +- .../TermConceptPropertyFieldBridge.java | 20 + .../entity/TermConceptPropertyTypeEnum.java | 20 + ...a => BaseTerminologyUploaderProvider.java} | 72 ++- .../TerminologyUploaderProviderDstu3.java | 73 +++ .../r4/TerminologyUploaderProviderR4.java | 50 ++ .../jpa/term/BaseHapiTerminologySvcImpl.java | 21 +- .../jpa/term/HapiTerminologySvcDstu2.java | 5 + .../jpa/term/HapiTerminologySvcDstu3.java | 28 + .../fhir/jpa/term/HapiTerminologySvcR4.java | 15 + .../ca/uhn/fhir/jpa/term/IRecordHandler.java | 20 + .../jpa/term/TerminologyLoaderSvcImpl.java | 14 +- .../jpa/term/VersionIndependentConcept.java | 16 +- .../uhn/fhir/jpa/term/loinc/BaseHandler.java | 28 +- .../BaseLoincTop2000LabResultsHandler.java | 20 + .../term/loinc/LoincAnswerListHandler.java | 20 + .../loinc/LoincAnswerListLinkHandler.java | 20 + .../loinc/LoincDocumentOntologyHandler.java | 24 +- .../uhn/fhir/jpa/term/loinc/LoincHandler.java | 27 + .../jpa/term/loinc/LoincHierarchyHandler.java | 20 + .../LoincIeeeMedicalDeviceCodeHandler.java | 24 +- .../LoincImagingDocumentCodeHandler.java | 20 + .../jpa/term/loinc/LoincPartLinkHandler.java | 28 +- .../LoincPartRelatedCodeMappingHandler.java | 27 +- .../term/loinc/LoincRsnaPlaybookHandler.java | 46 +- .../LoincTop2000LabResultsSiHandler.java | 20 + .../LoincTop2000LabResultsUsHandler.java | 20 + .../loinc/LoincUniversalOrderSetHandler.java | 20 + .../jpa/term/snomedct/SctHandlerConcept.java | 20 + .../term/snomedct/SctHandlerDescription.java | 20 + .../term/snomedct/SctHandlerRelationship.java | 20 + .../java/ca/uhn/fhir/jpa/util/TestUtil.java | 2 +- .../java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java | 3 + .../jpa/dao/dstu2/FhirSystemDaoDstu2Test.java | 322 ++++++++++-- .../jpa/dao/dstu3/FhirSystemDaoDstu3Test.java | 208 ++++++++ .../fhir/jpa/dao/r4/FhirSystemDaoR4Test.java | 163 +++++- .../dstu3/BaseResourceProviderDstu3Test.java | 7 +- .../ResourceProviderDstu3CodeSystemTest.java | 33 +- .../TerminologyUploaderProviderDstu3Test.java | 195 +++---- .../r4/BaseResourceProviderR4Test.java | 5 +- .../r4/TerminologyUploaderProviderR4Test.java | 275 +++++----- ...minologyLoaderSvcIntegrationDstu3Test.java | 2 +- .../TerminologyLoaderSvcSnomedCtTest.java | 5 +- .../jpa/term/TerminologySvcImplDstu3Test.java | 379 +++++++------- .../ca/uhn/fhirtest/TestRestfulServer.java | 179 +++---- .../PublicSecurityInterceptor.java | 6 +- .../uhn/fhir/rest/server/RestfulServer.java | 7 +- .../ctx/DefaultProfileValidationSupport.java | 31 +- .../DefaultProfileValidationSupport.java | 29 +- .../validation/ValidationSupportChain.java | 226 +++++---- 67 files changed, 2606 insertions(+), 1248 deletions(-) rename hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/{TerminologyUploaderProvider.java => BaseTerminologyUploaderProvider.java} (66%) create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4.java diff --git a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsServerExample.java b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsServerExample.java index 5670c8213f3..ed5e7185cd4 100644 --- a/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsServerExample.java +++ b/example-projects/hapi-fhir-jpaserver-cds-example/src/main/java/ca/uhn/fhir/jpa/cds/example/CdsServerExample.java @@ -5,9 +5,9 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.IFhirSystemDao; -import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; +import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3; import ca.uhn.fhir.jpa.rp.dstu3.ActivityDefinitionResourceProvider; import ca.uhn.fhir.jpa.rp.dstu3.MeasureResourceProvider; import ca.uhn.fhir.jpa.rp.dstu3.PlanDefinitionResourceProvider; @@ -149,7 +149,7 @@ public class CdsServerExample extends RestfulServer { * so it is a potential security vulnerability. Consider using an AuthorizationInterceptor * with this feature. */ - registerProvider(myAppCtx.getBean(TerminologyUploaderProvider.class)); + registerProvider(myAppCtx.getBean(TerminologyUploaderProviderDstu3.class)); } public IResourceProvider getProvider(String name) { diff --git a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java index 7511f23fd20..b4972e3e99d 100644 --- a/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java +++ b/hapi-fhir-cli/hapi-fhir-cli-jpaserver/src/main/java/ca/uhn/fhir/jpa/demo/JpaServerDemo.java @@ -8,9 +8,10 @@ import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; +import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3; import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4; import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4; -import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; +import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4; import ca.uhn.fhir.model.dstu2.composite.MetaDt; import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; @@ -78,10 +79,10 @@ public class JpaServerDemo extends RestfulServer { systemProvider.add(myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class)); } else if (fhirVersion == FhirVersionEnum.DSTU3) { systemProvider.add(myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class)); - systemProvider.add(myAppCtx.getBean(TerminologyUploaderProvider.class)); + systemProvider.add(myAppCtx.getBean(TerminologyUploaderProviderDstu3.class)); } else if (fhirVersion == FhirVersionEnum.R4) { systemProvider.add(myAppCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class)); - systemProvider.add(myAppCtx.getBean(TerminologyUploaderProvider.class)); + systemProvider.add(myAppCtx.getBean(TerminologyUploaderProviderR4.class)); } else { throw new IllegalStateException(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java index a01dd043e01..807512adef5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java @@ -9,8 +9,11 @@ import ca.uhn.fhir.jpa.dao.IFulltextSearchSvc; import ca.uhn.fhir.jpa.dao.ISearchParamRegistry; import ca.uhn.fhir.jpa.dao.dstu3.SearchParamExtractorDstu3; import ca.uhn.fhir.jpa.dao.dstu3.SearchParamRegistryDstu3; -import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; -import ca.uhn.fhir.jpa.term.*; +import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3; +import ca.uhn.fhir.jpa.term.HapiTerminologySvcDstu3; +import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; +import ca.uhn.fhir.jpa.term.IHapiTerminologySvcDstu3; +import ca.uhn.fhir.jpa.term.TerminologyLoaderSvcImpl; import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3; import ca.uhn.fhir.validation.IValidatorModule; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; @@ -115,8 +118,8 @@ public class BaseDstu3Config extends BaseConfig { } @Bean(autowire = Autowire.BY_TYPE) - public TerminologyUploaderProvider terminologyUploaderProvider() { - TerminologyUploaderProvider retVal = new TerminologyUploaderProvider(); + public TerminologyUploaderProviderDstu3 terminologyUploaderProvider() { + TerminologyUploaderProviderDstu3 retVal = new TerminologyUploaderProviderDstu3(); retVal.setContext(fhirContextDstu3()); return retVal; } @@ -126,5 +129,5 @@ public class BaseDstu3Config extends BaseConfig { public IValidationSupport validationSupportChainDstu3() { return new JpaValidationSupportChainDstu3(); } - + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java index 6a0d5cc286b..c9ff4f94920 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java @@ -10,7 +10,7 @@ import ca.uhn.fhir.jpa.dao.ISearchParamRegistry; import ca.uhn.fhir.jpa.dao.r4.SearchParamExtractorR4; import ca.uhn.fhir.jpa.dao.r4.SearchParamRegistryR4; import ca.uhn.fhir.jpa.graphql.JpaStorageServices; -import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; +import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4; import ca.uhn.fhir.jpa.term.HapiTerminologySvcR4; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; import ca.uhn.fhir.jpa.term.IHapiTerminologySvcR4; @@ -133,8 +133,8 @@ public class BaseR4Config extends BaseConfig { } @Bean(autowire = Autowire.BY_TYPE) - public TerminologyUploaderProvider terminologyUploaderProvider() { - TerminologyUploaderProvider retVal = new TerminologyUploaderProvider(); + public TerminologyUploaderProviderR4 terminologyUploaderProvider() { + TerminologyUploaderProviderR4 retVal = new TerminologyUploaderProviderR4(); retVal.setContext(fhirContextR4()); return retVal; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 8c06a482c5f..930329fffb5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -44,10 +44,12 @@ import ca.uhn.fhir.rest.api.QualifiedParamList; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.RestSearchParameterTypeEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.*; import ca.uhn.fhir.rest.server.exceptions.*; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; +import ca.uhn.fhir.rest.server.interceptor.IServerOperationInterceptor; import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.*; import com.google.common.annotations.VisibleForTesting; @@ -67,7 +69,6 @@ import org.hl7.fhir.instance.model.api.*; import org.hl7.fhir.r4.model.BaseResource; import org.hl7.fhir.r4.model.Bundle.HTTPVerb; import org.hl7.fhir.r4.model.CanonicalType; -import org.hl7.fhir.r4.model.IdType; import org.hl7.fhir.r4.model.Reference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.PlatformTransactionManager; @@ -110,6 +111,7 @@ public abstract class BaseHapiFhirDao implements IDao { private static final Map ourRetrievalContexts = new HashMap(); private static final String PROCESSING_SUB_REQUEST = "BaseHapiFhirDao.processingSubRequest"; private static boolean ourValidationDisabledForUnitTest; + private static boolean ourDisableIncrementOnUpdateForUnitTest = false; static { Map> resourceMetaParams = new HashMap>(); @@ -834,6 +836,22 @@ public abstract class BaseHapiFhirDao implements IDao { return new PersistedJpaBundleProvider(search.getUuid(), this); } + void incrementId(T theResource, ResourceTable theSavedEntity, IIdType theResourceId) { + String newVersion; + long newVersionLong; + if (theResourceId == null || theResourceId.getVersionIdPart() == null) { + newVersion = "1"; + newVersionLong = 1; + } else { + newVersionLong = theResourceId.getVersionIdPartAsLong() + 1; + newVersion = Long.toString(newVersionLong); + } + + IIdType newId = theResourceId.withVersion(newVersion); + theResource.getIdElement().setValue(newId.getValue()); + theSavedEntity.setVersion(newVersionLong); + } + @Override public void injectDependenciesIntoBundleProvider(PersistedJpaBundleProvider theProvider) { theProvider.setContext(getContext()); @@ -1816,6 +1834,55 @@ public abstract class BaseHapiFhirDao implements IDao { return updateEntity(theResource, entity, theDeletedTimestampOrNull, true, true, theUpdateTime, false, true); } + public ResourceTable updateInternal(T theResource, boolean thePerformIndexing, boolean theForceUpdateVersion, RequestDetails theRequestDetails, ResourceTable theEntity, IIdType theResourceId, IBaseResource theOldResource) { + // Notify interceptors + ActionRequestDetails requestDetails = null; + if (theRequestDetails != null) { + requestDetails = new ActionRequestDetails(theRequestDetails, theResource, theResourceId.getResourceType(), theResourceId); + notifyInterceptors(RestOperationTypeEnum.UPDATE, requestDetails); + } + + // Notify IServerOperationInterceptors about pre-action call + if (theRequestDetails != null) { + theRequestDetails.getRequestOperationCallback().resourcePreUpdate(theOldResource, theResource); + } + for (IServerInterceptor next : getConfig().getInterceptors()) { + if (next instanceof IServerOperationInterceptor) { + ((IServerOperationInterceptor) next).resourcePreUpdate(theRequestDetails, theOldResource, theResource); + } + } + + // Perform update + ResourceTable savedEntity = updateEntity(theResource, theEntity, null, thePerformIndexing, thePerformIndexing, new Date(), theForceUpdateVersion, thePerformIndexing); + + /* + * If we aren't indexing (meaning we're probably executing a sub-operation within a transaction), + * we'll manually increase the version. This is important because we want the updated version number + * to be reflected in the resource shared with interceptors + */ + if (!thePerformIndexing && !savedEntity.isUnchangedInCurrentOperation() && !ourDisableIncrementOnUpdateForUnitTest) { + if (theResourceId.hasVersionIdPart() == false) { + theResourceId = theResourceId.withVersion(Long.toString(savedEntity.getVersion())); + } + incrementId(theResource, savedEntity, theResourceId); + } + + // Notify interceptors + if (!savedEntity.isUnchangedInCurrentOperation()) { + if (theRequestDetails != null) { + theRequestDetails.getRequestOperationCallback().resourceUpdated(theResource); + theRequestDetails.getRequestOperationCallback().resourceUpdated(theOldResource, theResource); + } + for (IServerInterceptor next : getConfig().getInterceptors()) { + if (next instanceof IServerOperationInterceptor) { + ((IServerOperationInterceptor) next).resourceUpdated(theRequestDetails, theResource); + ((IServerOperationInterceptor) next).resourceUpdated(theRequestDetails, theOldResource, theResource); + } + } + } + return savedEntity; + } + private void validateChildReferences(IBase theElement, String thePath) { if (theElement == null) { return; @@ -2133,6 +2200,11 @@ public abstract class BaseHapiFhirDao implements IDao { return b.toString(); } + @VisibleForTesting + public static void setDisableIncrementOnUpdateForUnitTest(boolean theDisableIncrementOnUpdateForUnitTest) { + ourDisableIncrementOnUpdateForUnitTest = theDisableIncrementOnUpdateForUnitTest; + } + /** * Do not call this method outside of unit tests */ diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java index 30dd60c30c2..c08cf315219 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirResourceDao.java @@ -77,7 +77,6 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; public abstract class BaseHapiFhirResourceDao extends BaseHapiFhirDao implements IFhirResourceDao { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiFhirResourceDao.class); - private static boolean ourDisableIncrementOnUpdateForUnitTest = false; @Autowired protected PlatformTransactionManager myPlatformTransactionManager; @Autowired @@ -592,22 +591,6 @@ public abstract class BaseHapiFhirResourceDao extends B return retVal; } - private void incrementId(T theResource, ResourceTable theSavedEntity, IIdType theResourceId) { - String newVersion; - long newVersionLong; - if (theResourceId == null || theResourceId.getVersionIdPart() == null) { - newVersion = "1"; - newVersionLong = 1; - } else { - newVersionLong = theResourceId.getVersionIdPartAsLong() + 1; - newVersion = Long.toString(newVersionLong); - } - - IIdType newId = theResourceId.withVersion(newVersion); - theResource.getIdElement().setValue(newId.getValue()); - theSavedEntity.setVersion(newVersionLong); - } - protected boolean isPagingProviderDatabaseBacked(RequestDetails theRequestDetails) { if (theRequestDetails == null || theRequestDetails.getServer() == null) { return false; @@ -1229,54 +1212,24 @@ public abstract class BaseHapiFhirResourceDao extends B "Invalid resource ID[" + entity.getIdDt().toUnqualifiedVersionless() + "] of type[" + entity.getResourceType() + "] - Does not match expected [" + getResourceName() + "]"); } - // Notify interceptors - ActionRequestDetails requestDetails = null; - if (theRequestDetails != null) { - requestDetails = new ActionRequestDetails(theRequestDetails, theResource, getResourceName(), resourceId); - notifyInterceptors(RestOperationTypeEnum.UPDATE, requestDetails); - } - IBaseResource oldResource = toResource(entity, false); - // Notify IServerOperationInterceptors about pre-action call - if (theRequestDetails != null) { - theRequestDetails.getRequestOperationCallback().resourcePreUpdate(oldResource, theResource); + /* + * If we aren't indexing, that means we're doing this inside a transaction. + * The transaction will do the actual storate to the database a bit later on, + * after placeholder IDs have been replaced, by calling {@link #updateInternal} + * directly. So we just bail now. + */ + if (!thePerformIndexing) { + DaoMethodOutcome outcome = toMethodOutcome(entity, theResource).setCreated(false); + outcome.setPreviousResource(oldResource); + return outcome; } - for (IServerInterceptor next : getConfig().getInterceptors()) { - if (next instanceof IServerOperationInterceptor) { - ((IServerOperationInterceptor) next).resourcePreUpdate(theRequestDetails, oldResource, theResource); - } - } - - // Perform update - ResourceTable savedEntity = updateEntity(theResource, entity, null, thePerformIndexing, thePerformIndexing, new Date(), theForceUpdateVersion, thePerformIndexing); /* - * If we aren't indexing (meaning we're probably executing a sub-operation within a transaction), - * we'll manually increase the version. This is important because we want the updated version number - * to be reflected in the resource shared with interceptors + * Otherwise, we're not in a transaction */ - if (!thePerformIndexing && !savedEntity.isUnchangedInCurrentOperation() && !ourDisableIncrementOnUpdateForUnitTest) { - if (resourceId.hasVersionIdPart() == false) { - resourceId = resourceId.withVersion(Long.toString(savedEntity.getVersion())); - } - incrementId(theResource, savedEntity, resourceId); - } - - // Notify interceptors - if (!savedEntity.isUnchangedInCurrentOperation()) { - if (theRequestDetails != null) { - theRequestDetails.getRequestOperationCallback().resourceUpdated(theResource); - theRequestDetails.getRequestOperationCallback().resourceUpdated(oldResource, theResource); - } - for (IServerInterceptor next : getConfig().getInterceptors()) { - if (next instanceof IServerOperationInterceptor) { - ((IServerOperationInterceptor) next).resourceUpdated(theRequestDetails, theResource); - ((IServerOperationInterceptor) next).resourceUpdated(theRequestDetails, oldResource, theResource); - } - } - } - + ResourceTable savedEntity = updateInternal(theResource, thePerformIndexing, theForceUpdateVersion, theRequestDetails, entity, resourceId, oldResource); DaoMethodOutcome outcome = toMethodOutcome(savedEntity, theResource).setCreated(false); if (!thePerformIndexing) { @@ -1366,9 +1319,5 @@ public abstract class BaseHapiFhirResourceDao extends B } } - @VisibleForTesting - public static void setDisableIncrementOnUpdateForUnitTest(boolean theDisableIncrementOnUpdateForUnitTest) { - ourDisableIncrementOnUpdateForUnitTest = theDisableIncrementOnUpdateForUnitTest; - } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java index 46ffdca1403..e9809f98873 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java @@ -22,24 +22,41 @@ package ca.uhn.fhir.jpa.dao; import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.rest.api.MethodOutcome; +import org.hl7.fhir.instance.model.api.IBaseResource; public class DaoMethodOutcome extends MethodOutcome { private ResourceTable myEntity; + private IBaseResource myPreviousResource; public ResourceTable getEntity() { return myEntity; } - @Override - public DaoMethodOutcome setCreated(Boolean theCreated) { - super.setCreated(theCreated); - return this; - } - public DaoMethodOutcome setEntity(ResourceTable theEntity) { myEntity = theEntity; return this; } + /** + * For update operations, this is the body of the resource as it was before the + * update + */ + public IBaseResource getPreviousResource() { + return myPreviousResource; + } + + /** + * For update operations, this is the body of the resource as it was before the + * update + */ + public void setPreviousResource(IBaseResource thePreviousResource) { + myPreviousResource = thePreviousResource; + } + + @Override + public DaoMethodOutcome setCreated(Boolean theCreated) { + super.setCreated(theCreated); + return this; + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java index ae465b9c483..b6a5e7e8de9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java @@ -19,24 +19,6 @@ package ca.uhn.fhir.jpa.dao; * limitations under the License. * #L% */ -import static org.apache.commons.lang3.StringUtils.*; - -import java.util.*; - -import javax.persistence.TypedQuery; - -import ca.uhn.fhir.model.primitive.UriDt; -import org.apache.http.NameValuePair; -import org.hl7.fhir.instance.model.api.IBaseResource; -import org.hl7.fhir.instance.model.api.IIdType; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.transaction.*; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionCallback; -import org.springframework.transaction.support.TransactionTemplate; - -import com.google.common.collect.ArrayListMultimap; import ca.uhn.fhir.context.RuntimeResourceDefinition; import ca.uhn.fhir.jpa.entity.ResourceTable; @@ -51,15 +33,23 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle; import ca.uhn.fhir.model.dstu2.resource.Bundle.Entry; import ca.uhn.fhir.model.dstu2.resource.Bundle.EntryResponse; import ca.uhn.fhir.model.dstu2.resource.OperationOutcome; -import ca.uhn.fhir.model.dstu2.valueset.*; +import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum; +import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum; +import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.InstantDt; +import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.IParser; -import ca.uhn.fhir.rest.api.*; +import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.api.RequestTypeEnum; +import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.RestfulServerUtils; -import ca.uhn.fhir.rest.server.exceptions.*; +import ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.NotModifiedException; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.rest.server.method.BaseMethodBinding; import ca.uhn.fhir.rest.server.method.BaseResourceReturningMethodBinding; @@ -67,6 +57,23 @@ import ca.uhn.fhir.rest.server.servlet.ServletRequestDetails; import ca.uhn.fhir.util.FhirTerser; import ca.uhn.fhir.util.UrlUtil; import ca.uhn.fhir.util.UrlUtil.UrlParts; +import com.google.common.collect.ArrayListMultimap; +import org.apache.http.NameValuePair; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.persistence.TypedQuery; +import java.util.*; + +import static org.apache.commons.lang3.StringUtils.*; public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirSystemDaoDstu2.class); @@ -137,121 +144,11 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { } long delay = System.currentTimeMillis() - start; - ourLog.info("Batch completed in {}ms", new Object[] { delay }); + ourLog.info("Batch completed in {}ms", new Object[] {delay}); return resp; } - private String extractTransactionUrlOrThrowException(Entry nextEntry, HTTPVerbEnum verb) { - String url = nextEntry.getRequest().getUrl(); - if (isBlank(url)) { - throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionMissingUrl", verb.name())); - } - return url; - } - - /** - * This method is called for nested bundles (e.g. if we received a transaction with an entry that - * was a GET search, this method is called on the bundle for the search result, that will be placed in the - * outer bundle). This method applies the _summary and _content parameters to the output of - * that bundle. - * - * TODO: This isn't the most efficient way of doing this.. hopefully we can come up with something better in the future. - */ - private IBaseResource filterNestedBundle(RequestDetails theRequestDetails, IBaseResource theResource) { - IParser p = getContext().newJsonParser(); - RestfulServerUtils.configureResponseParser(theRequestDetails, p); - return p.parseResource(theResource.getClass(), p.encodeResourceToString(theResource)); - } - - private IFhirResourceDao getDaoOrThrowException(Class theClass) { - IFhirResourceDao retVal = getDao(theClass); - if (retVal == null) { - throw new InvalidRequestException("Unable to process request, this server does not know how to handle resources of type " + getContext().getResourceDefinition(theClass).getName()); - } - return retVal; - } - - @Override - public MetaDt metaGetOperation(RequestDetails theRequestDetails) { - // Notify interceptors - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); - notifyInterceptors(RestOperationTypeEnum.META, requestDetails); - - String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)"; - TypedQuery q = myEntityManager.createQuery(sql, TagDefinition.class); - List tagDefinitions = q.getResultList(); - - MetaDt retVal = toMetaDt(tagDefinitions); - - return retVal; - } - - protected MetaDt toMetaDt(Collection tagDefinitions) { - MetaDt retVal = new MetaDt(); - for (TagDefinition next : tagDefinitions) { - switch (next.getTagType()) { - case PROFILE: - retVal.addProfile(next.getCode()); - break; - case SECURITY_LABEL: - retVal.addSecurity().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay()); - break; - case TAG: - retVal.addTag().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay()); - break; - } - } - return retVal; - } - - - private ca.uhn.fhir.jpa.dao.IFhirResourceDao toDao(UrlParts theParts, String theVerb, String theUrl) { - RuntimeResourceDefinition resType; - try { - resType = getContext().getResourceDefinition(theParts.getResourceType()); - } catch (DataFormatException e) { - String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl); - throw new InvalidRequestException(msg); - } - IFhirResourceDao dao = null; - if (resType != null) { - dao = getDao(resType.getImplementingClass()); - } - if (dao == null) { - String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl); - throw new InvalidRequestException(msg); - } - - // if (theParts.getResourceId() == null && theParts.getParams() == null) { - // String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl); - // throw new InvalidRequestException(msg); - // } - - return dao; - } - - @Transactional(propagation = Propagation.REQUIRED) - @Override - public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) { - if (theRequestDetails != null) { - ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, theRequest, "Bundle", null); - notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails); - } - - String actionName = "Transaction"; - return transaction((ServletRequestDetails) theRequestDetails, theRequest, actionName); - } - - private Bundle transaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) { - super.markRequestAsProcessingSubRequest(theRequestDetails); - try { - return doTransaction(theRequestDetails, theRequest, theActionName); - } finally { - super.clearRequestAsProcessingSubRequest(theRequestDetails); - } - } - @SuppressWarnings("unchecked") private Bundle doTransaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) { BundleTypeEnum transactionType = theRequest.getTypeElement().getValueAsEnum(); @@ -279,10 +176,10 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { /* * We want to execute the transaction request bundle elements in the order - * specified by the FHIR specification (see TransactionSorter) so we save the + * specified by the FHIR specification (see TransactionSorter) so we save the * original order in the request, then sort it. - * - * Entries with a type of GET are removed from the bundle so that they + * + * Entries with a type of GET are removed from the bundle so that they * can be processed at the very end. We do this because the incoming resources * are saved in a two-phase way in order to deal with interdependencies, and * we want the GET processing to use the final indexing state @@ -298,11 +195,12 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { } } Collections.sort(theRequest.getEntry(), new TransactionSorter()); - - List deletedResources = new ArrayList(); - List deleteConflicts = new ArrayList(); - Map entriesToProcess = new IdentityHashMap(); + + List deletedResources = new ArrayList<>(); + List deleteConflicts = new ArrayList<>(); + Map entriesToProcess = new IdentityHashMap<>(); Set nonUpdatedEntities = new HashSet(); + Set updatedEntities = new HashSet<>(); /* * Loop through the request and process any entries of type @@ -321,7 +219,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { nextResourceId = res.getId(); - if (nextResourceId.hasIdPart() == false) { + if (!nextResourceId.hasIdPart()) { if (isNotBlank(nextReqEntry.getFullUrl())) { nextResourceId = new IdDt(nextReqEntry.getFullUrl()); } @@ -361,86 +259,85 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { Entry nextRespEntry = response.getEntry().get(originalRequestOrder.get(nextReqEntry)); switch (verb) { - case POST: { - // CREATE - @SuppressWarnings("rawtypes") - IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass()); - res.setId((String) null); - DaoMethodOutcome outcome; - outcome = resourceDao.create(res, nextReqEntry.getRequest().getIfNoneExist(), false, theRequestDetails); - handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res); - entriesToProcess.put(nextRespEntry, outcome.getEntity()); - if (outcome.getCreated() == false) { - nonUpdatedEntities.add(outcome.getEntity()); - } - break; - } - case DELETE: { - // DELETE - String url = extractTransactionUrlOrThrowException(nextReqEntry, verb); - UrlParts parts = UrlUtil.parseUrl(url); - ca.uhn.fhir.jpa.dao.IFhirResourceDao dao = toDao(parts, verb.getCode(), url); - int status = Constants.STATUS_HTTP_204_NO_CONTENT; - if (parts.getResourceId() != null) { - DaoMethodOutcome outcome = dao.delete(new IdDt(parts.getResourceType(), parts.getResourceId()), deleteConflicts, theRequestDetails); - if (outcome.getEntity() != null) { - deletedResources.add(outcome.getId().toUnqualifiedVersionless()); - entriesToProcess.put(nextRespEntry, outcome.getEntity()); - } - } else { - DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(parts.getResourceType() + '?' + parts.getParams(), deleteConflicts, theRequestDetails); - List allDeleted = deleteOutcome.getDeletedEntities(); - for (ResourceTable deleted : allDeleted) { - deletedResources.add(deleted.getIdDt().toUnqualifiedVersionless()); - } - if (allDeleted.isEmpty()) { - status = Constants.STATUS_HTTP_404_NOT_FOUND; - } - } - - nextRespEntry.getResponse().setStatus(toStatusString(status)); - break; - } - case PUT: { - // UPDATE - @SuppressWarnings("rawtypes") - IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass()); - - DaoMethodOutcome outcome; - - String url = extractTransactionUrlOrThrowException(nextReqEntry, verb); - - UrlParts parts = UrlUtil.parseUrl(url); - if (isNotBlank(parts.getResourceId())) { - res.setId(new IdDt(parts.getResourceType(), parts.getResourceId())); - outcome = resourceDao.update(res, null, false, theRequestDetails); - } else { + case POST: { + // CREATE + @SuppressWarnings("rawtypes") + IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass()); res.setId((String) null); - outcome = resourceDao.update(res, parts.getResourceType() + '?' + parts.getParams(), false, theRequestDetails); + DaoMethodOutcome outcome; + outcome = resourceDao.create(res, nextReqEntry.getRequest().getIfNoneExist(), false, theRequestDetails); + handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res); + entriesToProcess.put(nextRespEntry, outcome.getEntity()); + if (outcome.getCreated() == false) { + nonUpdatedEntities.add(outcome.getEntity()); + } + break; } + case DELETE: { + // DELETE + String url = extractTransactionUrlOrThrowException(nextReqEntry, verb); + UrlParts parts = UrlUtil.parseUrl(url); + ca.uhn.fhir.jpa.dao.IFhirResourceDao dao = toDao(parts, verb.getCode(), url); + int status = Constants.STATUS_HTTP_204_NO_CONTENT; + if (parts.getResourceId() != null) { + DaoMethodOutcome outcome = dao.delete(new IdDt(parts.getResourceType(), parts.getResourceId()), deleteConflicts, theRequestDetails); + if (outcome.getEntity() != null) { + deletedResources.add(outcome.getId().toUnqualifiedVersionless()); + entriesToProcess.put(nextRespEntry, outcome.getEntity()); + } + } else { + DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(parts.getResourceType() + '?' + parts.getParams(), deleteConflicts, theRequestDetails); + List allDeleted = deleteOutcome.getDeletedEntities(); + for (ResourceTable deleted : allDeleted) { + deletedResources.add(deleted.getIdDt().toUnqualifiedVersionless()); + } + if (allDeleted.isEmpty()) { + status = Constants.STATUS_HTTP_404_NOT_FOUND; + } + } - handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res); - entriesToProcess.put(nextRespEntry, outcome.getEntity()); - break; - } - case GET: - break; + nextRespEntry.getResponse().setStatus(toStatusString(status)); + break; + } + case PUT: { + // UPDATE + @SuppressWarnings("rawtypes") + IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass()); + + DaoMethodOutcome outcome; + + String url = extractTransactionUrlOrThrowException(nextReqEntry, verb); + + UrlParts parts = UrlUtil.parseUrl(url); + if (isNotBlank(parts.getResourceId())) { + res.setId(new IdDt(parts.getResourceType(), parts.getResourceId())); + outcome = resourceDao.update(res, null, false, theRequestDetails); + } else { + res.setId((String) null); + outcome = resourceDao.update(res, parts.getResourceType() + '?' + parts.getParams(), false, theRequestDetails); + } + + if (outcome.getCreated() == Boolean.FALSE) { + updatedEntities.add(outcome.getEntity()); + } + + handleTransactionCreateOrUpdateOutcome(idSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res); + entriesToProcess.put(nextRespEntry, outcome.getEntity()); + break; + } + case GET: + break; } } /* * Make sure that there are no conflicts from deletions. E.g. we can't delete something * if something else has a reference to it.. Unless the thing that has a reference to it - * was also deleted as a part of this transaction, which is why we check this now at the + * was also deleted as a part of this transaction, which is why we check this now at the * end. */ - - for (Iterator iter = deleteConflicts.iterator(); iter.hasNext(); ) { - DeleteConflict next = iter.next(); - if (deletedResources.contains(next.getTargetId().toVersionless())) { - iter.remove(); - } - } + + deleteConflicts.removeIf(next -> deletedResources.contains(next.getTargetId().toVersionless())); validateDeleteConflictsEmptyOrThrowException(deleteConflicts); /* @@ -489,8 +386,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { InstantDt deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get(nextResource); Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; - boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity()); - if (shouldUpdate) { + if (updatedEntities.contains(nextOutcome.getEntity())) { + updateInternal(nextResource, true, false, theRequestDetails, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource()); + } else if (!nonUpdatedEntities.contains(nextOutcome.getEntity())) { updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, updateTime, false, true); } } @@ -508,7 +406,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { Set val = resourceDao.processMatchUrl(matchUrl); if (val.size() > 1) { throw new InvalidRequestException( - "Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?"); + "Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?"); } } } @@ -537,9 +435,9 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { requestDetails.setServletRequest(theRequestDetails.getServletRequest()); requestDetails.setRequestType(RequestTypeEnum.GET); requestDetails.setServer(theRequestDetails.getServer()); - + String url = extractTransactionUrlOrThrowException(nextReqEntry, HTTPVerbEnum.GET); - + int qIndex = url.indexOf('?'); ArrayListMultimap paramValues = ArrayListMultimap.create(); requestDetails.setParameters(new HashMap()); @@ -564,7 +462,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { if (method == null) { throw new IllegalArgumentException("Unable to handle GET " + url); } - + if (isNotBlank(nextReqEntry.getRequest().getIfMatch())) { requestDetails.addHeader(Constants.HEADER_IF_MATCH, nextReqEntry.getRequest().getIfMatch()); } @@ -574,7 +472,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { if (isNotBlank(nextReqEntry.getRequest().getIfNoneMatch())) { requestDetails.addHeader(Constants.HEADER_IF_NONE_MATCH, nextReqEntry.getRequest().getIfNoneMatch()); } - + if (method instanceof BaseResourceReturningMethodBinding) { try { IBaseResource resource = ((BaseResourceReturningMethodBinding) method).doInvokeServer(theRequestDetails.getServer(), requestDetails); @@ -594,23 +492,132 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { ourLog.info("Flushing context after {}", theActionName); myEntityManager.flush(); - + for (java.util.Map.Entry nextEntry : entriesToProcess.entrySet()) { nextEntry.getKey().getResponse().setLocation(nextEntry.getValue().getIdDt().toUnqualified().getValue()); nextEntry.getKey().getResponse().setEtag(nextEntry.getValue().getIdDt().getVersionIdPart()); } - + long delay = System.currentTimeMillis() - start; int numEntries = theRequest.getEntry().size(); long delayPer = delay / numEntries; - ourLog.info("{} completed in {}ms ({} entries at {}ms per entry)", new Object[] { theActionName , delay, numEntries, delayPer }); + ourLog.info("{} completed in {}ms ({} entries at {}ms per entry)", new Object[] {theActionName, delay, numEntries, delayPer}); response.setType(BundleTypeEnum.TRANSACTION_RESPONSE); return response; } + private String extractTransactionUrlOrThrowException(Entry nextEntry, HTTPVerbEnum verb) { + String url = nextEntry.getRequest().getUrl(); + if (isBlank(url)) { + throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionMissingUrl", verb.name())); + } + return url; + } + + /** + * This method is called for nested bundles (e.g. if we received a transaction with an entry that + * was a GET search, this method is called on the bundle for the search result, that will be placed in the + * outer bundle). This method applies the _summary and _content parameters to the output of + * that bundle. + *

+ * TODO: This isn't the most efficient way of doing this.. hopefully we can come up with something better in the future. + */ + private IBaseResource filterNestedBundle(RequestDetails theRequestDetails, IBaseResource theResource) { + IParser p = getContext().newJsonParser(); + RestfulServerUtils.configureResponseParser(theRequestDetails, p); + return p.parseResource(theResource.getClass(), p.encodeResourceToString(theResource)); + } + + private IFhirResourceDao getDaoOrThrowException(Class theClass) { + IFhirResourceDao retVal = getDao(theClass); + if (retVal == null) { + throw new InvalidRequestException("Unable to process request, this server does not know how to handle resources of type " + getContext().getResourceDefinition(theClass).getName()); + } + return retVal; + } + + @Override + public MetaDt metaGetOperation(RequestDetails theRequestDetails) { + // Notify interceptors + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails); + notifyInterceptors(RestOperationTypeEnum.META, requestDetails); + + String sql = "SELECT d FROM TagDefinition d WHERE d.myId IN (SELECT DISTINCT t.myTagId FROM ResourceTag t)"; + TypedQuery q = myEntityManager.createQuery(sql, TagDefinition.class); + List tagDefinitions = q.getResultList(); + + MetaDt retVal = toMetaDt(tagDefinitions); + + return retVal; + } + + private ca.uhn.fhir.jpa.dao.IFhirResourceDao toDao(UrlParts theParts, String theVerb, String theUrl) { + RuntimeResourceDefinition resType; + try { + resType = getContext().getResourceDefinition(theParts.getResourceType()); + } catch (DataFormatException e) { + String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl); + throw new InvalidRequestException(msg); + } + IFhirResourceDao dao = null; + if (resType != null) { + dao = getDao(resType.getImplementingClass()); + } + if (dao == null) { + String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl); + throw new InvalidRequestException(msg); + } + + // if (theParts.getResourceId() == null && theParts.getParams() == null) { + // String msg = getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionInvalidUrl", theVerb, theUrl); + // throw new InvalidRequestException(msg); + // } + + return dao; + } + + protected MetaDt toMetaDt(Collection tagDefinitions) { + MetaDt retVal = new MetaDt(); + for (TagDefinition next : tagDefinitions) { + switch (next.getTagType()) { + case PROFILE: + retVal.addProfile(next.getCode()); + break; + case SECURITY_LABEL: + retVal.addSecurity().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay()); + break; + case TAG: + retVal.addTag().setSystem(next.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay()); + break; + } + } + return retVal; + } + + @Transactional(propagation = Propagation.REQUIRED) + @Override + public Bundle transaction(RequestDetails theRequestDetails, Bundle theRequest) { + if (theRequestDetails != null) { + ActionRequestDetails requestDetails = new ActionRequestDetails(theRequestDetails, theRequest, "Bundle", null); + notifyInterceptors(RestOperationTypeEnum.TRANSACTION, requestDetails); + } + + String actionName = "Transaction"; + return transaction((ServletRequestDetails) theRequestDetails, theRequest, actionName); + } + + private Bundle transaction(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName) { + super.markRequestAsProcessingSubRequest(theRequestDetails); + try { + return doTransaction(theRequestDetails, theRequest, theActionName); + } finally { + super.clearRequestAsProcessingSubRequest(theRequestDetails); + } + } + private static void handleTransactionCreateOrUpdateOutcome(Map idSubstitutions, Map idToPersistedOutcome, IdDt nextResourceId, DaoMethodOutcome outcome, - Entry newEntry, String theResourceType, IResource theRes) { + Entry newEntry, String theResourceType, IResource theRes) { IdDt newId = (IdDt) outcome.getId().toUnqualifiedVersionless(); IdDt resourceId = isPlaceholder(nextResourceId) ? nextResourceId : nextResourceId.toUnqualifiedVersionless(); if (newId.equals(resourceId) == false) { @@ -645,9 +652,10 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { } //@formatter:off + /** * Transaction Order, per the spec: - * + *

* Process any DELETE interactions * Process any POST interactions * Process any PUT interactions @@ -657,7 +665,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { public class TransactionSorter implements Comparator { @Override - public int compare(Entry theO1, Entry theO2) { + public int compare(Entry theO1, Entry theO2) { int o1 = toOrder(theO1); int o2 = toOrder(theO2); @@ -667,20 +675,20 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { private int toOrder(Entry theO1) { int o1 = 0; if (theO1.getRequest().getMethodElement().getValueAsEnum() != null) { - switch (theO1.getRequest().getMethodElement().getValueAsEnum()) { - case DELETE: - o1 = 1; - break; - case POST: - o1 = 2; - break; - case PUT: - o1 = 3; - break; - case GET: - o1 = 4; - break; - } + switch (theO1.getRequest().getMethodElement().getValueAsEnum()) { + case DELETE: + o1 = 1; + break; + case POST: + o1 = 2; + break; + case PUT: + o1 = 3; + break; + case GET: + o1 = 4; + break; + } } return o1; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index 2469110cda2..de3746e753a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -1193,7 +1193,7 @@ public class SearchBuilder implements ISearchBuilder { codes = myTerminologySvc.findCodesBelow(system, code); } - ArrayList singleCodePredicates = new ArrayList(); + ArrayList singleCodePredicates = new ArrayList<>(); if (codes != null) { if (codes.isEmpty()) { @@ -1581,7 +1581,8 @@ public class SearchBuilder implements ISearchBuilder { } } if (valueSetUris.size() == 1) { - List candidateCodes = myTerminologySvc.expandValueSet(valueSetUris.iterator().next()); + String valueSet = valueSetUris.iterator().next(); + List candidateCodes = myTerminologySvc.expandValueSet(valueSet); for (VersionIndependentConcept nextCandidate : candidateCodes) { if (nextCandidate.getCode().equals(code)) { retVal = nextCandidate.getSystem(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java index c53f3bc2db7..05d4e147704 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java @@ -1,5 +1,12 @@ package ca.uhn.fhir.jpa.dao.data; +import ca.uhn.fhir.jpa.entity.TermCodeSystem; +import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + import java.util.List; /* @@ -22,17 +29,15 @@ import java.util.List; * #L% */ -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; +public interface ITermCodeSystemVersionDao extends JpaRepository { -import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; - -public interface ITermCodeSystemVersionDao extends JpaRepository { + @Modifying + @Query("DELETE FROM TermCodeSystemVersion csv WHERE csv.myCodeSystem = :cs") + void deleteForCodeSystem(@Param("cs") TermCodeSystem theCodeSystem); @Query("SELECT cs FROM TermCodeSystemVersion cs WHERE cs.myResource.myId = :resource_id") List findByCodeSystemResource(@Param("resource_id") Long theCodeSystemResourcePid); - + @Query("SELECT cs FROM TermCodeSystemVersion cs WHERE cs.myCodeSystemHavingThisVersionAsCurrentVersionIfAny.myResource.myId = :resource_id") TermCodeSystemVersion findCurrentVersionForCodeSystemResourcePid(@Param("resource_id") Long theCodeSystemResourcePid); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDao.java index 646b1407166..5f9dc078b6f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDao.java @@ -41,9 +41,9 @@ public interface ITermConceptDao extends JpaRepository { @Query("SELECT c FROM TermConcept c WHERE c.myCodeSystem = :code_system") List findByCodeSystemVersion(@Param("code_system") TermCodeSystemVersion theCodeSystem); - @Query("DELETE FROM TermConcept t WHERE t.myCodeSystem.myId = :cs_pid") + @Query("SELECT t FROM TermConcept t WHERE t.myCodeSystem.myId = :cs_pid") @Modifying - void deleteByCodeSystemVersion(@Param("cs_pid") Long thePid); + List findByCodeSystemVersion(@Param("cs_pid") Long thePid); @Query("UPDATE TermConcept t SET t.myIndexStatus = null") @Modifying diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java index 43dc130ee64..19ac3bf479a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java @@ -158,13 +158,11 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { ourLog.debug("Beginning {} with {} resources", theActionName, theRequest.getEntry().size()); -// long start = System.currentTimeMillis(); - final StopWatch transactionSw = new StopWatch(); final Date updateTime = new Date(); - final Set allIds = new LinkedHashSet(); - final Map idSubstitutions = new HashMap(); - final Map idToPersistedOutcome = new HashMap(); + final Set allIds = new LinkedHashSet<>(); + final Map idSubstitutions = new HashMap<>(); + final Map idToPersistedOutcome = new HashMap<>(); // Do all entries have a verb? for (int i = 0; i < theRequest.getEntry().size(); i++) { @@ -223,7 +221,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { Map entriesToProcess = txManager.execute(new TransactionCallback>() { @Override public Map doInTransaction(TransactionStatus status) { - return doTransactionWriteOperations(theRequestDetails, theRequest, theActionName, updateTime, allIds, idSubstitutions, idToPersistedOutcome, response, originalRequestOrder, entries, transactionSw); + return doTransactionWriteOperations(theRequestDetails, theRequest, theActionName, updateTime, allIds, idSubstitutions, idToPersistedOutcome, response, originalRequestOrder, entries); } }); for (Entry nextEntry : entriesToProcess.entrySet()) { @@ -300,20 +298,17 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { } - ourLog.info(theActionName + " completed in {}", transactionSw.toString()); - ourLog.info(theActionName + " details:\n{}", transactionSw.formatTaskDurations()); - response.setType(BundleType.TRANSACTIONRESPONSE); return response; } - - @SuppressWarnings("unchecked") - private Map doTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date updateTime, Set allIds, - Map theIdSubstitutions, Map idToPersistedOutcome, Bundle response, IdentityHashMap originalRequestOrder, List theEntries, StopWatch theStopWatch) { + + private Map doTransactionWriteOperations(ServletRequestDetails theRequestDetails, Bundle theRequest, String theActionName, Date theUpdateTime, Set theAllIds, + Map theIdSubstitutions, Map theIdToPersistedOutcome, Bundle theResponse, IdentityHashMap theOriginalRequestOrder, List theEntries) { Set deletedResources = new HashSet<>(); List deleteConflicts = new ArrayList<>(); Map entriesToProcess = new IdentityHashMap<>(); Set nonUpdatedEntities = new HashSet<>(); + Set updatedEntities = new HashSet<>(); Map> conditionalRequestUrls = new HashMap<>(); /* @@ -333,7 +328,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { nextResourceId = res.getIdElement(); - if (nextResourceId.hasIdPart() == false) { + if (!nextResourceId.hasIdPart()) { if (isNotBlank(nextReqEntry.getFullUrl())) { nextResourceId = new IdType(nextReqEntry.getFullUrl()); } @@ -352,12 +347,12 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { * Ensure that the bundle doesn't have any duplicates, since this causes all kinds of weirdness */ if (isPlaceholder(nextResourceId)) { - if (!allIds.add(nextResourceId)) { + if (!theAllIds.add(nextResourceId)) { throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextResourceId)); } } else if (nextResourceId.hasResourceType() && nextResourceId.hasIdPart()) { IdType nextId = nextResourceId.toUnqualifiedVersionless(); - if (!allIds.add(nextId)) { + if (!theAllIds.add(nextId)) { throw new InvalidRequestException(getContext().getLocalizer().getMessage(BaseHapiFhirSystemDao.class, "transactionContainsMultipleWithDuplicateId", nextId)); } } @@ -367,111 +362,112 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { HTTPVerb verb = nextReqEntry.getRequest().getMethodElement().getValue(); String resourceType = res != null ? getContext().getResourceDefinition(res).getName() : null; - BundleEntryComponent nextRespEntry = response.getEntry().get(originalRequestOrder.get(nextReqEntry)); - - theStopWatch.startTask("Process entry " + i + ": " + verb + " " + defaultString(resourceType)); + BundleEntryComponent nextRespEntry = theResponse.getEntry().get(theOriginalRequestOrder.get(nextReqEntry)); switch (verb) { - case POST: { - // CREATE - @SuppressWarnings("rawtypes") - IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass()); - res.setId((String) null); - DaoMethodOutcome outcome; - String matchUrl = nextReqEntry.getRequest().getIfNoneExist(); - matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl); - outcome = resourceDao.create(res, matchUrl, false, theRequestDetails); - if (nextResourceId != null) { - handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); - } - entriesToProcess.put(nextRespEntry, outcome.getEntity()); - if (outcome.getCreated() == false) { - nonUpdatedEntities.add(outcome.getEntity()); - } else { - if (isNotBlank(matchUrl)) { - conditionalRequestUrls.put(matchUrl, res.getClass()); + case POST: { + // CREATE + @SuppressWarnings("rawtypes") + IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass()); + res.setId((String) null); + DaoMethodOutcome outcome; + String matchUrl = nextReqEntry.getRequest().getIfNoneExist(); + matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl); + outcome = resourceDao.create(res, matchUrl, false, theRequestDetails); + if (nextResourceId != null) { + handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); } - } - - break; - } - case DELETE: { - // DELETE - String url = extractTransactionUrlOrThrowException(nextReqEntry, verb); - UrlParts parts = UrlUtil.parseUrl(url); - ca.uhn.fhir.jpa.dao.IFhirResourceDao dao = toDao(parts, verb.toCode(), url); - int status = Constants.STATUS_HTTP_204_NO_CONTENT; - if (parts.getResourceId() != null) { - IdType deleteId = new IdType(parts.getResourceType(), parts.getResourceId()); - if (!deletedResources.contains(deleteId.getValueAsString())) { - DaoMethodOutcome outcome = dao.delete(deleteId, deleteConflicts, theRequestDetails); - if (outcome.getEntity() != null) { - deletedResources.add(deleteId.getValueAsString()); - entriesToProcess.put(nextRespEntry, outcome.getEntity()); + entriesToProcess.put(nextRespEntry, outcome.getEntity()); + if (outcome.getCreated() == false) { + nonUpdatedEntities.add(outcome.getEntity()); + } else { + if (isNotBlank(matchUrl)) { + conditionalRequestUrls.put(matchUrl, res.getClass()); } } - } else { - String matchUrl = parts.getResourceType() + '?' + parts.getParams(); - matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl); - DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(matchUrl, deleteConflicts, theRequestDetails); - List allDeleted = deleteOutcome.getDeletedEntities(); - for (ResourceTable deleted : allDeleted) { - deletedResources.add(deleted.getIdDt().toUnqualifiedVersionless().getValueAsString()); - } - if (allDeleted.isEmpty()) { - status = Constants.STATUS_HTTP_204_NO_CONTENT; - } - nextRespEntry.getResponse().setOutcome((Resource) deleteOutcome.getOperationOutcome()); + break; } - - nextRespEntry.getResponse().setStatus(toStatusString(status)); - - break; - } - case PUT: { - // UPDATE - @SuppressWarnings("rawtypes") - IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass()); - - String url = extractTransactionUrlOrThrowException(nextReqEntry, verb); - - DaoMethodOutcome outcome; - UrlParts parts = UrlUtil.parseUrl(url); - if (isNotBlank(parts.getResourceId())) { - String version = null; - if (isNotBlank(nextReqEntry.getRequest().getIfMatch())) { - version = ParameterUtil.parseETagValue(nextReqEntry.getRequest().getIfMatch()); - } - res.setId(new IdType(parts.getResourceType(), parts.getResourceId(), version)); - outcome = resourceDao.update(res, null, false, theRequestDetails); - } else { - res.setId((String) null); - String matchUrl; - if (isNotBlank(parts.getParams())) { - matchUrl = parts.getResourceType() + '?' + parts.getParams(); + case DELETE: { + // DELETE + String url = extractTransactionUrlOrThrowException(nextReqEntry, verb); + UrlParts parts = UrlUtil.parseUrl(url); + ca.uhn.fhir.jpa.dao.IFhirResourceDao dao = toDao(parts, verb.toCode(), url); + int status = Constants.STATUS_HTTP_204_NO_CONTENT; + if (parts.getResourceId() != null) { + IdType deleteId = new IdType(parts.getResourceType(), parts.getResourceId()); + if (!deletedResources.contains(deleteId.getValueAsString())) { + DaoMethodOutcome outcome = dao.delete(deleteId, deleteConflicts, theRequestDetails); + if (outcome.getEntity() != null) { + deletedResources.add(deleteId.getValueAsString()); + entriesToProcess.put(nextRespEntry, outcome.getEntity()); + } + } } else { - matchUrl = parts.getResourceType(); - } - matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl); - outcome = resourceDao.update(res, matchUrl, false, theRequestDetails); - if (Boolean.TRUE.equals(outcome.getCreated())) { - conditionalRequestUrls.put(matchUrl, res.getClass()); - } - } + String matchUrl = parts.getResourceType() + '?' + parts.getParams(); + matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl); + DeleteMethodOutcome deleteOutcome = dao.deleteByUrl(matchUrl, deleteConflicts, theRequestDetails); + List allDeleted = deleteOutcome.getDeletedEntities(); + for (ResourceTable deleted : allDeleted) { + deletedResources.add(deleted.getIdDt().toUnqualifiedVersionless().getValueAsString()); + } + if (allDeleted.isEmpty()) { + status = Constants.STATUS_HTTP_204_NO_CONTENT; + } + + nextRespEntry.getResponse().setOutcome((Resource) deleteOutcome.getOperationOutcome()); + } + + nextRespEntry.getResponse().setStatus(toStatusString(status)); + + break; + } + case PUT: { + // UPDATE + @SuppressWarnings("rawtypes") + IFhirResourceDao resourceDao = getDaoOrThrowException(res.getClass()); + + String url = extractTransactionUrlOrThrowException(nextReqEntry, verb); + + DaoMethodOutcome outcome; + UrlParts parts = UrlUtil.parseUrl(url); + if (isNotBlank(parts.getResourceId())) { + String version = null; + if (isNotBlank(nextReqEntry.getRequest().getIfMatch())) { + version = ParameterUtil.parseETagValue(nextReqEntry.getRequest().getIfMatch()); + } + res.setId(new IdType(parts.getResourceType(), parts.getResourceId(), version)); + outcome = resourceDao.update(res, null, false, theRequestDetails); + } else { + res.setId((String) null); + String matchUrl; + if (isNotBlank(parts.getParams())) { + matchUrl = parts.getResourceType() + '?' + parts.getParams(); + } else { + matchUrl = parts.getResourceType(); + } + matchUrl = performIdSubstitutionsInMatchUrl(theIdSubstitutions, matchUrl); + outcome = resourceDao.update(res, matchUrl, false, theRequestDetails); + if (Boolean.TRUE.equals(outcome.getCreated())) { + conditionalRequestUrls.put(matchUrl, res.getClass()); + } + } + + if (outcome.getCreated() == Boolean.FALSE) { + updatedEntities.add(outcome.getEntity()); + } + + handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); + entriesToProcess.put(nextRespEntry, outcome.getEntity()); + break; + } + case GET: + case NULL: + break; - handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, idToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); - entriesToProcess.put(nextRespEntry, outcome.getEntity()); - break; - } - case GET: - case NULL: - break; } } - theStopWatch.endCurrentTask(); - /* * Make sure that there are no conflicts from deletions. E.g. we can't delete something * if something else has a reference to it.. Unless the thing that has a reference to it @@ -479,12 +475,8 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { * end. */ - for (Iterator iter = deleteConflicts.iterator(); iter.hasNext();) { - DeleteConflict next = iter.next(); - if (deletedResources.contains(next.getTargetId().toUnqualifiedVersionless().getValue())) { - iter.remove(); - } - } + deleteConflicts.removeIf(next -> + deletedResources.contains(next.getTargetId().toUnqualifiedVersionless().getValue())); validateDeleteConflictsEmptyOrThrowException(deleteConflicts); /* @@ -492,13 +484,13 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { */ FhirTerser terser = getContext().newTerser(); - for (DaoMethodOutcome nextOutcome : idToPersistedOutcome.values()) { + for (DaoMethodOutcome nextOutcome : theIdToPersistedOutcome.values()) { IBaseResource nextResource = nextOutcome.getResource(); if (nextResource == null) { continue; } - // Refererences + // References List allRefs = terser.getAllPopulatedChildElementsOfType(nextResource, IBaseReference.class); for (IBaseReference nextRef : allRefs) { IIdType nextId = nextRef.getReferenceElement(); @@ -534,18 +526,16 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { IPrimitiveType deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource); Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; - boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity()); - if (shouldUpdate) { - updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, shouldUpdate, false, updateTime, false, true); + + if (updatedEntities.contains(nextOutcome.getEntity())) { + updateInternal(nextResource, true, false, theRequestDetails, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource()); + } else if (!nonUpdatedEntities.contains(nextOutcome.getEntity())) { + updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, theUpdateTime, false, true); } } - theStopWatch.startTask("Flush Session"); - flushJpaSession(); - theStopWatch.endCurrentTask(); - /* * Double check we didn't allow any duplicates we shouldn't have */ @@ -557,12 +547,12 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { Set val = resourceDao.processMatchUrl(matchUrl); if (val.size() > 1) { throw new InvalidRequestException( - "Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?"); + "Unable to process " + theActionName + " - Request would cause multiple resources to match URL: \"" + matchUrl + "\". Does transaction request contain duplicates?"); } } } - for (IdType next : allIds) { + for (IdType next : theAllIds) { IdType replacement = theIdSubstitutions.get(next); if (replacement == null) { continue; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/JpaValidationSupportDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/JpaValidationSupportDstu3.java index 93b4e36f51f..b84b5b3e2ca 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/JpaValidationSupportDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/JpaValidationSupportDstu3.java @@ -19,6 +19,8 @@ import javax.transaction.Transactional.TxType; import java.util.Collections; import java.util.List; +import static org.apache.commons.lang3.StringUtils.isBlank; + /* * #%L * HAPI FHIR JPA Server @@ -87,6 +89,9 @@ public class JpaValidationSupportDstu3 implements IJpaValidationSupportDstu3 { @Override public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) { + if (isBlank(theSystem)) { + return null; + } return fetchResource(theCtx, CodeSystem.class, theSystem); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java index 1b3f3230579..12b22e1cfb8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java @@ -29,7 +29,6 @@ import ca.uhn.fhir.jpa.entity.ResourceTable; import ca.uhn.fhir.jpa.entity.TagDefinition; import ca.uhn.fhir.jpa.provider.ServletSubRequestDetails; import ca.uhn.fhir.jpa.util.DeleteConflict; -import ca.uhn.fhir.util.StopWatch; import ca.uhn.fhir.model.api.ResourceMetadataKeyEnum; import ca.uhn.fhir.parser.DataFormatException; import ca.uhn.fhir.parser.IParser; @@ -266,7 +265,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { paramValues.put(next.getName(), next.getValue()); } for (java.util.Map.Entry> nextParamEntry : paramValues.asMap().entrySet()) { - String[] nextValue = nextParamEntry.getValue().toArray(new String[ nextParamEntry.getValue().size() ]); + String[] nextValue = nextParamEntry.getValue().toArray(new String[nextParamEntry.getValue().size()]); requestDetails.addParameter(nextParamEntry.getKey(), nextValue); } url = url.substring(0, qIndex); @@ -323,6 +322,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { List deleteConflicts = new ArrayList<>(); Map entriesToProcess = new IdentityHashMap<>(); Set nonUpdatedEntities = new HashSet<>(); + Set updatedEntities = new HashSet<>(); Map> conditionalRequestUrls = new HashMap<>(); /* @@ -467,6 +467,10 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { } } + if (outcome.getCreated() == Boolean.FALSE) { + updatedEntities.add(outcome.getEntity()); + } + handleTransactionCreateOrUpdateOutcome(theIdSubstitutions, theIdToPersistedOutcome, nextResourceId, outcome, nextRespEntry, resourceType, res, theRequestDetails); entriesToProcess.put(nextRespEntry, outcome.getEntity()); break; @@ -538,9 +542,11 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { IPrimitiveType deletedInstantOrNull = ResourceMetadataKeyEnum.DELETED_AT.get((IAnyResource) nextResource); Date deletedTimestampOrNull = deletedInstantOrNull != null ? deletedInstantOrNull.getValue() : null; - boolean shouldUpdate = !nonUpdatedEntities.contains(nextOutcome.getEntity()); - if (shouldUpdate) { - updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, shouldUpdate, false, theUpdateTime, false, true); + + if (updatedEntities.contains(nextOutcome.getEntity())) { + updateInternal(nextResource, true, false, theRequestDetails, nextOutcome.getEntity(), nextResource.getIdElement(), nextOutcome.getPreviousResource()); + } else if (!nonUpdatedEntities.contains(nextOutcome.getEntity())) { + updateEntity(nextResource, nextOutcome.getEntity(), deletedTimestampOrNull, true, false, theUpdateTime, false, true); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java index 4f977e65502..5133859d649 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/BaseResourceIndexedSearchParam.java @@ -53,7 +53,7 @@ public abstract class BaseResourceIndexedSearchParam implements Serializable { private Long myResourcePid; @Field() - @Column(name = "RES_TYPE", nullable = false, length = 30) + @Column(name = "RES_TYPE", nullable = false) private String myResourceType; @Field() diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java index b51192d8770..d7e0e5e4c8e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystem.java @@ -20,6 +20,9 @@ package ca.uhn.fhir.jpa.entity; * #L% */ +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + import javax.persistence.*; import java.io.Serializable; @@ -89,4 +92,15 @@ public class TermCodeSystem implements Serializable { public void setResource(ResourceTable theResource) { myResource = theResource; } + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE) + .append("codeSystemUri", myCodeSystemUri) + .append("currentVersion", myCurrentVersion) + .append("pid", myPid) + .append("resourcePid", myResourcePid) + .append("name", myName) + .toString(); + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java index 4197c51121a..ea021ed1820 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java @@ -56,7 +56,7 @@ public class TermCodeSystemVersion implements Serializable { * issued. It should be made non-nullable at some point. */ @ManyToOne - @JoinColumn(name = "CODESYSTEM_PID", referencedColumnName = "PID", nullable = true) + @JoinColumn(name = "CODESYSTEM_PID", referencedColumnName = "PID", nullable = true, foreignKey = @ForeignKey(name = "FK_CODESYSVER_CS_ID")) private TermCodeSystem myCodeSystem; @SuppressWarnings("unused") @OneToOne(mappedBy = "myCurrentVersion", optional = true) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java index f0c6c7e6d14..51b64484155 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java @@ -31,7 +31,7 @@ public class TermConceptDesignation implements Serializable { private static final long serialVersionUID = 1L; @ManyToOne - @JoinColumn(name = "CONCEPT_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTPROP_CONCEPT")) + @JoinColumn(name = "CONCEPT_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTDESIG_CONCEPT")) private TermConcept myConcept; @Id() @SequenceGenerator(name = "SEQ_CONCEPT_DESIG_PID", sequenceName = "SEQ_CONCEPT_DESIG_PID") diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyFieldBridge.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyFieldBridge.java index 5a13b78000f..d10a1ac9734 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyFieldBridge.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyFieldBridge.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.entity; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import org.apache.lucene.document.Document; import org.hibernate.search.bridge.FieldBridge; import org.hibernate.search.bridge.LuceneOptions; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyTypeEnum.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyTypeEnum.java index 14762936e45..49062476c44 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyTypeEnum.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyTypeEnum.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.entity; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + /** * @see TermConceptProperty#getType() * @see TermConceptProperty#MAX_PROPTYPE_ENUM_LENGTH diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseTerminologyUploaderProvider.java similarity index 66% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseTerminologyUploaderProvider.java index ecf58bdd21b..0667cb44889 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/TerminologyUploaderProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseTerminologyUploaderProvider.java @@ -22,47 +22,47 @@ package ca.uhn.fhir.jpa.provider; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc.UploadStatistics; -import ca.uhn.fhir.rest.annotation.Operation; -import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.param.StringParam; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; +import org.hl7.fhir.r4.model.Attachment; import org.hl7.fhir.r4.model.IntegerType; import org.hl7.fhir.r4.model.Parameters; import org.hl7.fhir.r4.model.StringType; import org.springframework.beans.factory.annotation.Autowired; import javax.servlet.http.HttpServletRequest; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.InputStream; +import java.io.*; import java.util.ArrayList; import java.util.List; -import static org.apache.commons.lang3.StringUtils.defaultString; -import static org.apache.commons.lang3.StringUtils.isNotBlank; +import static org.apache.commons.lang3.StringUtils.*; -public class TerminologyUploaderProvider extends BaseJpaProvider { +public abstract class BaseTerminologyUploaderProvider extends BaseJpaProvider { public static final String UPLOAD_EXTERNAL_CODE_SYSTEM = "$upload-external-code-system"; - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyUploaderProvider.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseTerminologyUploaderProvider.class); @Autowired private IHapiTerminologyLoaderSvc myTerminologyLoaderSvc; - - @Operation(name = UPLOAD_EXTERNAL_CODE_SYSTEM, idempotent = false, returnParameters= { - @OperationParam(name="conceptCount", type=IntegerType.class, min=1) - }) - public Parameters uploadExternalCodeSystem( - HttpServletRequest theServletRequest, - @OperationParam(name="url", min=1) StringParam theCodeSystemUrl, - @OperationParam(name="localfile", min=1, max=OperationParam.MAX_UNLIMITED) List theLocalFile, - RequestDetails theRequestDetails - ) { + + protected Parameters handleUploadExternalCodeSystem( + HttpServletRequest theServletRequest, + StringParam theCodeSystemUrl, + List theLocalFile, + List thePackage, RequestDetails theRequestDetails + ) { startRequest(theServletRequest); + + if (theLocalFile == null || theLocalFile.size() == 0) { + if (thePackage == null || thePackage.size() == 0) { + throw new InvalidRequestException("No 'localfile' or 'package' parameter, or package had no data"); + } + } + try { List localFiles = new ArrayList<>(); if (theLocalFile != null && theLocalFile.size() > 0) { @@ -70,8 +70,8 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { if (isNotBlank(nextLocalFile.getValue())) { ourLog.info("Reading in local file: {}", nextLocalFile.getValue()); File nextFile = new File(nextLocalFile.getValue()); - if (!nextFile.exists() || nextFile.isFile()) { - throw new InvalidRequestException("Unknown file: " +nextFile.getName()); + if (!nextFile.exists() || !nextFile.isFile()) { + throw new InvalidRequestException("Unknown file: " + nextFile.getName()); } localFiles.add(new IHapiTerminologyLoaderSvc.FileDescriptor() { @Override @@ -91,7 +91,27 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { } } } - + + if (thePackage != null) { + for (Attachment nextPackage : thePackage) { + if (isBlank(nextPackage.getUrl())) { + throw new UnprocessableEntityException("Package is missing mandatory url element"); + } + + localFiles.add(new IHapiTerminologyLoaderSvc.FileDescriptor() { + @Override + public String getFilename() { + return nextPackage.getUrl(); + } + + @Override + public InputStream getInputStream() { + return new ByteArrayInputStream(nextPackage.getData()); + } + }); + } + } + String url = theCodeSystemUrl != null ? theCodeSystemUrl.getValue() : null; url = defaultString(url); @@ -99,11 +119,11 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { if (IHapiTerminologyLoaderSvc.SCT_URI.equals(url)) { stats = myTerminologyLoaderSvc.loadSnomedCt(localFiles, theRequestDetails); } else if (IHapiTerminologyLoaderSvc.LOINC_URI.equals(url)) { - stats = myTerminologyLoaderSvc.loadLoinc(localFiles, theRequestDetails); + stats = myTerminologyLoaderSvc.loadLoinc(localFiles, theRequestDetails); } else { throw new InvalidRequestException("Unknown URL: " + url); } - + Parameters retVal = new Parameters(); retVal.addParameter().setName("conceptCount").setValue(new IntegerType(stats.getConceptCount())); return retVal; @@ -112,5 +132,5 @@ public class TerminologyUploaderProvider extends BaseJpaProvider { } } - + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java new file mode 100644 index 00000000000..6438da856c7 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3.java @@ -0,0 +1,73 @@ +package ca.uhn.fhir.jpa.provider.dstu3; + +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.jpa.provider.BaseTerminologyUploaderProvider; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.param.StringParam; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import org.hl7.fhir.convertors.VersionConvertor_30_40; +import org.hl7.fhir.dstu3.model.Attachment; +import org.hl7.fhir.dstu3.model.IntegerType; +import org.hl7.fhir.dstu3.model.Parameters; +import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.exceptions.FHIRException; + +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.List; + +public class TerminologyUploaderProviderDstu3 extends BaseTerminologyUploaderProvider { + + @Operation(name = UPLOAD_EXTERNAL_CODE_SYSTEM, idempotent = false, returnParameters = { + @OperationParam(name = "conceptCount", type = IntegerType.class, min = 1) + }) + public Parameters uploadExternalCodeSystem( + HttpServletRequest theServletRequest, + @OperationParam(name = "url", min = 1) StringParam theCodeSystemUrl, + @OperationParam(name = "localfile", min = 1, max = OperationParam.MAX_UNLIMITED) List theLocalFile, + @OperationParam(name = "package", min = 0, max = OperationParam.MAX_UNLIMITED) List thePackage, + RequestDetails theRequestDetails + ) { + try { + List localFile = null; + if (theLocalFile != null) { + localFile = new ArrayList<>(); + for (StringType next : theLocalFile) { + localFile.add(VersionConvertor_30_40.convertString(next)); + } + } + List pkg = null; + if (thePackage!=null){ + pkg = new ArrayList<>(); + for (Attachment next : thePackage) { + pkg.add(VersionConvertor_30_40.convertAttachment(next)); + } + } + org.hl7.fhir.r4.model.Parameters retValR4 = handleUploadExternalCodeSystem(theServletRequest, theCodeSystemUrl, localFile, pkg, theRequestDetails); + return VersionConvertor_30_40.convertParameters(retValR4); + } catch (FHIRException e) { + throw new InternalErrorException(e); + } + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4.java new file mode 100644 index 00000000000..303a981a3d7 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4.java @@ -0,0 +1,50 @@ +package ca.uhn.fhir.jpa.provider.r4; + +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + +import ca.uhn.fhir.jpa.provider.BaseTerminologyUploaderProvider; +import ca.uhn.fhir.rest.annotation.Operation; +import ca.uhn.fhir.rest.annotation.OperationParam; +import ca.uhn.fhir.rest.api.server.RequestDetails; +import ca.uhn.fhir.rest.param.StringParam; +import org.hl7.fhir.r4.model.Attachment; +import org.hl7.fhir.r4.model.IntegerType; +import org.hl7.fhir.r4.model.Parameters; +import org.hl7.fhir.r4.model.StringType; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; + +public class TerminologyUploaderProviderR4 extends BaseTerminologyUploaderProvider { + + @Operation(name = UPLOAD_EXTERNAL_CODE_SYSTEM, idempotent = false, returnParameters = { + @OperationParam(name = "conceptCount", type = IntegerType.class, min = 1) + }) + public Parameters uploadExternalCodeSystem( + HttpServletRequest theServletRequest, + @OperationParam(name = "url", min = 1) StringParam theCodeSystemUrl, + @OperationParam(name = "localfile", min = 1, max = OperationParam.MAX_UNLIMITED) List theLocalFile, + @OperationParam(name = "package", min = 0, max = OperationParam.MAX_UNLIMITED) List thePackage, + RequestDetails theRequestDetails + ) { + return handleUploadExternalCodeSystem(theServletRequest, theCodeSystemUrl, theLocalFile, thePackage, theRequestDetails); + } +} diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index 86fe953d6b0..102b4fdb29f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -105,6 +105,15 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc contains.setCode(theConcept.getCode()); contains.setSystem(theCodeSystem); contains.setDisplay(theConcept.getDisplay()); + for (TermConceptDesignation nextDesignation : theConcept.getDesignations()) { + contains + .addDesignation() + .setValue(nextDesignation.getValue()) + .getUse() + .setSystem(nextDesignation.getUseSystem()) + .setCode(nextDesignation.getUseCode()) + .setDisplay(nextDesignation.getUseDisplay()); + } } } @@ -232,11 +241,13 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc haveIncludeCriteria = true; TermConcept code = findCode(system, nextCode); if (code != null) { - addedCodes.add(nextCode); - ValueSet.ValueSetExpansionContainsComponent contains = expansionComponent.addContains(); - contains.setCode(nextCode); - contains.setSystem(system); - contains.setDisplay(code.getDisplay()); + addCodeIfNotAlreadyAdded(system, expansionComponent, addedCodes, code); +// +// addedCodes.add(nextCode); +// ValueSet.ValueSetExpansionContainsComponent contains = expansionComponent.addContains(); +// contains.setCode(nextCode); +// contains.setSystem(system); +// contains.setDisplay(code.getDisplay()); } } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu2.java index d7259cba057..b26aa5e9a28 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu2.java @@ -76,6 +76,11 @@ public class HapiTerminologySvcDstu2 extends BaseHapiTerminologySvcImpl { throw new UnsupportedOperationException(); } + @Override + protected CodeSystem getCodeSystemFromContext(String theSystem) { + return null; + } + @Override public List expandValueSet(String theValueSet) { throw new UnsupportedOperationException(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java index d8f8aee0c5b..5087fc312b5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java @@ -153,6 +153,24 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen } } + @Override + public List expandValueSet(String theValueSet) { + ValueSet vs = myValidationSupport.fetchResource(myContext, ValueSet.class, theValueSet); + if (vs == null) { + return Collections.emptyList(); + } + + org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4; + try { + valueSetToExpandR4 = VersionConvertor_30_40.convertValueSet(vs); + } catch (FHIRException e) { + throw new InternalErrorException(e); + } + + + return expandValueSetAndReturnVersionIndependentConcepts(valueSetToExpandR4); + } + @Override public List fetchAllConformanceResources(FhirContext theContext) { return null; @@ -222,6 +240,16 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen return retVal; } + @Override + protected org.hl7.fhir.r4.model.CodeSystem getCodeSystemFromContext(String theSystem) { + CodeSystem codeSystem = myValidationSupport.fetchCodeSystem(myContext, theSystem); + try { + return VersionConvertor_30_40.convertCodeSystem(codeSystem); + } catch (FHIRException e) { + throw new InternalErrorException(e); + } + } + @Override public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) { return myTerminologySvc.supportsSystem(theSystem); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java index 3af96290f2f..41182ebcfa5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java @@ -112,6 +112,16 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements myValueSetResourceDao.update(theValueSet, matchUrl, theRequestDetails); } + @Override + public List expandValueSet(String theValueSet) { + ValueSet vs = myValidationSupport.fetchResource(myContext, ValueSet.class, theValueSet); + if (vs == null) { + return Collections.emptyList(); + } + + return expandValueSetAndReturnVersionIndependentConcepts(vs); + } + @Override public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { ValueSet valueSetToExpand = new ValueSet(); @@ -188,6 +198,11 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements return retVal; } + @Override + protected CodeSystem getCodeSystemFromContext(String theSystem) { + return myValidationSupport.fetchCodeSystem(myContext, theSystem); + } + @Override public boolean isCodeSystemSupported(FhirContext theContext, String theSystem) { return myTerminologySvc.supportsSystem(theSystem); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IRecordHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IRecordHandler.java index 314a6a65cf1..35e0b65f34b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IRecordHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IRecordHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import org.apache.commons.csv.CSVRecord; public interface IRecordHandler { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java index 4b3b8cfe841..c0112139d1b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java @@ -128,6 +128,13 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { CSVParser parsed; try { reader = new InputStreamReader(nextZipBytes.getInputStream(), Charsets.UTF_8); + + if (ourLog.isTraceEnabled()) { + String contents = IOUtils.toString(reader); + ourLog.info("File contents for: {}\n{}", nextFilename, contents); + reader = new StringReader(contents); + } + CSVFormat format = CSVFormat.newFormat(theDelimiter).withFirstRecordAsHeader(); if (theQuoteMode != null) { format = format.withQuote('"').withQuoteMode(theQuoteMode); @@ -402,16 +409,15 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { LoadedFileDescriptors(List theFileDescriptors) { try { for (FileDescriptor next : theFileDescriptors) { - File nextTemporaryFile = File.createTempFile("hapifhir", ".tmp"); - nextTemporaryFile.deleteOnExit(); - if (next.getFilename().toLowerCase().endsWith(".zip")) { ourLog.info("Uncompressing {} into temporary files", next.getFilename()); try (InputStream inputStream = next.getInputStream()) { ZipInputStream zis = new ZipInputStream(new BufferedInputStream(inputStream)); for (ZipEntry nextEntry; (nextEntry = zis.getNextEntry()) != null; ) { BOMInputStream fis = new BOMInputStream(zis); - FileOutputStream fos = new FileOutputStream(nextTemporaryFile); + File nextTemporaryFile = File.createTempFile("hapifhir", ".tmp"); + nextTemporaryFile.deleteOnExit(); + FileOutputStream fos = new FileOutputStream(nextTemporaryFile, false); IOUtils.copy(fis, fos); String nextEntryFileName = nextEntry.getName(); myUncompressedFileDescriptors.add(new FileDescriptor() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/VersionIndependentConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/VersionIndependentConcept.java index 59c5403d253..467bae71011 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/VersionIndependentConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/VersionIndependentConcept.java @@ -25,6 +25,16 @@ public class VersionIndependentConcept { private String mySystem; private String myCode; + /** + * Constructor + */ + public VersionIndependentConcept() { + super(); + } + + /** + * Constructor + */ public VersionIndependentConcept(String theSystem, String theCode) { setSystem(theSystem); setCode(theCode); @@ -34,16 +44,18 @@ public class VersionIndependentConcept { return mySystem; } - public void setSystem(String theSystem) { + public VersionIndependentConcept setSystem(String theSystem) { mySystem = theSystem; + return this; } public String getCode() { return myCode; } - public void setCode(String theCode) { + public VersionIndependentConcept setCode(String theCode) { myCode = theCode; + return this; } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java index db3ba9ef002..9dbc98dc6c7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IRecordHandler; import org.hl7.fhir.r4.model.ConceptMap; @@ -66,7 +86,7 @@ abstract class BaseHandler implements IRecordHandler { } - void addConceptMapEntry(ConceptMapping theMapping) { + void addConceptMapEntry(ConceptMapping theMapping, String theCopyright) { if (isBlank(theMapping.getSourceCode())) { return; } @@ -86,6 +106,7 @@ abstract class BaseHandler implements IRecordHandler { .addTelecom() .setSystem(ContactPoint.ContactPointSystem.URL) .setValue("https://loinc.org"); + conceptMap.setCopyright(theCopyright); myIdToConceptMaps.put(theMapping.getConceptMapId(), conceptMap); myConceptMaps.add(conceptMap); } else { @@ -151,12 +172,13 @@ abstract class BaseHandler implements IRecordHandler { vs.setId(theValueSetId); vs.setName(theValueSetName); vs.setStatus(Enumerations.PublicationStatus.ACTIVE); - vs.setPublisher("Regentrief Institute, Inc."); + vs.setPublisher("Regenstrief Institute, Inc."); vs.addContact() - .setName("Regentrief Institute, Inc.") + .setName("Regenstrief Institute, Inc.") .addTelecom() .setSystem(ContactPoint.ContactPointSystem.URL) .setValue("https://loinc.org"); + vs.setCopyright("This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at https://loinc.org/license/"); myIdToValueSet.put(theValueSetId, vs); myValueSets.add(vs); } else { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java index ac5d5cdffac..8e431107827 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; import ca.uhn.fhir.jpa.term.IRecordHandler; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java index bde2ad61f12..29a174827c2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListLinkHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListLinkHandler.java index cda7bdce357..f16ed0cbed8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListLinkHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListLinkHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java index 7916e06ba1e..09ff81684de 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; @@ -15,8 +35,8 @@ import static org.apache.commons.lang3.StringUtils.trim; public class LoincDocumentOntologyHandler extends BaseHandler implements IRecordHandler { - public static final String DOCUMENT_ONTOLOGY_CODES_VS_ID = "DOCUMENT-ONTOLOGY-CODES-VS"; - public static final String DOCUMENT_ONTOLOGY_CODES_VS_URI = "http://loinc.org/document-ontology-codes"; + public static final String DOCUMENT_ONTOLOGY_CODES_VS_ID = "loinc-document-ontology"; + public static final String DOCUMENT_ONTOLOGY_CODES_VS_URI = "http://loinc.org/vs/loinc-document-ontology"; public static final String DOCUMENT_ONTOLOGY_CODES_VS_NAME = "LOINC Document Ontology Codes"; private final Map myCode2Concept; private final TermCodeSystemVersion myCodeSystemVersion; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java index b2d95452c9b..be3597489f9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IRecordHandler; @@ -37,6 +57,13 @@ public class LoincHandler implements IRecordHandler { TermConcept concept = new TermConcept(myCodeSystemVersion, code); concept.setDisplay(display); + if (!display.equalsIgnoreCase(shortName)) { + concept + .addDesignation() + .setUseDisplay("ShortName") + .setValue(shortName); + } + for (String nextPropertyName : myPropertyNames) { if (!theRecord.toMap().containsKey(nextPropertyName)) { continue; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHierarchyHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHierarchyHandler.java index b550dadf466..02373716541 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHierarchyHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHierarchyHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java index 1375bcea03e..a15bc66332c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; import ca.uhn.fhir.jpa.term.IRecordHandler; @@ -18,6 +38,7 @@ public class LoincIeeeMedicalDeviceCodeHandler extends BaseHandler implements IR public static final String LOINC_IEEE_CM_ID = "LOINC-IEEE-MEDICAL-DEVICE-CM"; public static final String LOINC_IEEE_CM_URI = "http://loinc.org/fhir/loinc-ieee-device-code-mappings"; public static final String LOINC_IEEE_CM_NAME = "LOINC/IEEE Device Code Mappings"; + private static final String CM_COPYRIGHT = "This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at https://loinc.org/license/. The LOINC/IEEE Medical Device Code Mapping Table contains content from IEEE (http://ieee.org), copyright © 2017 IEEE."; /** * Constructor @@ -48,7 +69,8 @@ public class LoincIeeeMedicalDeviceCodeHandler extends BaseHandler implements IR .setTargetCodeSystem(targetCodeSystemUri) .setTargetCode(ieeeCode) .setTargetDisplay(ieeeDisplayName) - .setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL)); + .setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL), + CM_COPYRIGHT); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java index 4c58e6187e0..ffae5fa14c7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; import ca.uhn.fhir.jpa.term.IRecordHandler; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java index fe92e66b347..5ec6963bd84 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java @@ -1,20 +1,39 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IRecordHandler; import org.apache.commons.csv.CSVRecord; -import org.hl7.fhir.r4.model.ValueSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.HashMap; import java.util.Map; import static org.apache.commons.lang3.StringUtils.trim; public class LoincPartLinkHandler implements IRecordHandler { + private static final Logger ourLog = LoggerFactory.getLogger(LoincPartLinkHandler.class); private final Map myCode2Concept; private final TermCodeSystemVersion myCodeSystemVersion; @@ -33,11 +52,11 @@ public class LoincPartLinkHandler implements IRecordHandler { TermConcept loincConcept = myCode2Concept.get(loincNumber); TermConcept partConcept = myCode2Concept.get(partNumber); - if (loincConcept==null) { + if (loincConcept == null) { ourLog.warn("No loinc code: {}", loincNumber); return; } - if (partConcept==null) { + if (partConcept == null) { ourLog.warn("No part code: {}", partNumber); return; } @@ -45,5 +64,4 @@ public class LoincPartLinkHandler implements IRecordHandler { // For now we're ignoring these } -private static final Logger ourLog = LoggerFactory.getLogger(LoincPartLinkHandler.class); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java index e0198caacfb..627048108f7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; @@ -18,8 +38,9 @@ import static org.apache.commons.lang3.StringUtils.trim; public class LoincPartRelatedCodeMappingHandler extends BaseHandler implements IRecordHandler { public static final String LOINC_PART_MAP_ID = "LOINC-PART-MAP"; - public static final String LOINC_PART_MAP_URI = "http://loinc.org/fhir/loinc-part-map"; + public static final String LOINC_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-snomed-ct"; public static final String LOINC_PART_MAP_NAME = "LOINC Part Map"; + private static final String CM_COPYRIGHT = "This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at https://loinc.org/license/. The LOINC Part File, LOINC/SNOMED CT Expression Association and Map Sets File, RELMA database and associated search index files include SNOMED Clinical Terms (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights are reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org. Under the terms of the Affiliate License, use of SNOMED CT in countries that are not IHTSDO Members is subject to reporting and fee payment obligations. However, IHTSDO agrees to waive the requirements to report and pay fees for use of SNOMED CT content included in the LOINC Part Mapping and LOINC Term Associations for purposes that support or enable more effective use of LOINC. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov."; private final Map myCode2Concept; private final TermCodeSystemVersion myCodeSystemVersion; private final List myConceptMaps; @@ -76,7 +97,9 @@ public class LoincPartRelatedCodeMappingHandler extends BaseHandler implements I .setTargetDisplay(extCodeDisplayName) .setTargetCodeSystemVersion(extCodeSystemVersion) .setEquivalence(equivalence) - .setCopyright(extCodeSystemCopyrightNotice)); + .setCopyright(extCodeSystemCopyrightNotice), + CM_COPYRIGHT + ); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java index 7f1efd71e15..b2b7ba74631 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; @@ -17,17 +37,27 @@ import static org.apache.commons.lang3.StringUtils.trim; public class LoincRsnaPlaybookHandler extends BaseHandler implements IRecordHandler { - public static final String RSNA_CODES_VS_ID = "RSNA-LOINC-CODES-VS"; - public static final String RSNA_CODES_VS_URI = "http://loinc.org/rsna-codes"; - public static final String RSNA_CODES_VS_NAME = "RSNA Playbook"; + public static final String RSNA_CODES_VS_ID = "loinc-rsna-radiology-playbook"; + public static final String RSNA_CODES_VS_URI = "http://loinc.org/vs/loinc-rsna-radiology-playbook"; + public static final String RSNA_CODES_VS_NAME = "LOINC/RSNA Radiology Playbook"; public static final String RID_MAPPING_CM_ID = "LOINC-TO-RID-CODES-CM"; public static final String RID_MAPPING_CM_URI = "http://loinc.org/rid-codes"; public static final String RID_MAPPING_CM_NAME = "RSNA Playbook RID Codes Mapping"; - public static final String RID_CS_URI = "http://rid"; + public static final String RID_CS_URI = "http://www.radlex.org"; public static final String RPID_MAPPING_CM_ID = "LOINC-TO-RPID-CODES-CM"; public static final String RPID_MAPPING_CM_URI = "http://loinc.org/rpid-codes"; public static final String RPID_MAPPING_CM_NAME = "RSNA Playbook RPID Codes Mapping"; - public static final String RPID_CS_URI = "http://rpid"; + /* + * About these being the same - Per Dan: + * We had some discussion about this, and both + * RIDs (RadLex clinical terms) and RPIDs (Radlex Playbook Ids) + * belong to the same "code system" since they will never collide. + * The codesystem uri is "http://www.radlex.org". FYI, that's + * now listed on the FHIR page: + * https://www.hl7.org/fhir/terminologies-systems.html + */ + public static final String RPID_CS_URI = RID_CS_URI; + private static final String CM_COPYRIGHT = "This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at https://loinc.org/license/. The LOINC/RSNA Radiology Playbook and the LOINC Part File contain content from RadLex® (http://rsna.org/RadLex.aspx), copyright © 2005-2017, The Radiological Society of North America, Inc., available at no cost under the license at http://www.rsna.org/uploadedFiles/RSNA/Content/Informatics/RadLex_License_Agreement_and_Terms_of_Use_V2_Final.pdf."; private final Map myCode2Concept; private final TermCodeSystemVersion myCodeSystemVersion; private final Set myPropertyNames; @@ -118,7 +148,8 @@ public class LoincRsnaPlaybookHandler extends BaseHandler implements IRecordHand .setTargetCodeSystem(RID_CS_URI) .setTargetCode(rid) .setTargetDisplay(preferredName) - .setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL)); + .setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL), + CM_COPYRIGHT); } // LOINC Term -> Radlex RPID code mappings @@ -134,7 +165,8 @@ public class LoincRsnaPlaybookHandler extends BaseHandler implements IRecordHand .setTargetCodeSystem(RPID_CS_URI) .setTargetCode(rpid) .setTargetDisplay(longName) - .setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL)); + .setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL), + CM_COPYRIGHT); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java index 7b0310a76f0..8930694a490 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermConcept; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ValueSet; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java index 70f369b1f0f..1068349bf1b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermConcept; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ValueSet; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java index 34208471bd9..71439449f02 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.loinc; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; import ca.uhn.fhir.jpa.term.IRecordHandler; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerConcept.java index 42b080df729..c1fbdb30aa9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerConcept.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.snomedct; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.term.IRecordHandler; import org.apache.commons.csv.CSVRecord; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerDescription.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerDescription.java index e7ef13c41db..2aa2ebc8fd8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerDescription.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerDescription.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.snomedct; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IRecordHandler; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerRelationship.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerRelationship.java index 171c5e16a8c..cc58cecba43 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerRelationship.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerRelationship.java @@ -1,5 +1,25 @@ package ca.uhn.fhir.jpa.term.snomedct; +/*- + * #%L + * HAPI FHIR JPA Server + * %% + * Copyright (C) 2014 - 2018 University Health Network + * %% + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * #L% + */ + import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/TestUtil.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/TestUtil.java index 4d5dafb825f..5beb2f3a67f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/TestUtil.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/TestUtil.java @@ -96,7 +96,7 @@ public class TestUtil { assertThat(joinColumn.name(), null); ForeignKey fk = joinColumn.foreignKey(); Validate.notNull(fk); - Validate.isTrue(isNotBlank(fk.name())); + Validate.isTrue(isNotBlank(fk.name()), "Foreign key on " + ae.toString() + " has no name()"); Validate.isTrue(fk.name().startsWith("FK_")); assertThat(fk.name(), theNames); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java index dd0730869a6..6639e4bf878 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/BaseJpaTest.java @@ -252,6 +252,7 @@ public abstract class BaseJpaTest { public Void doInTransaction(TransactionStatus theStatus) { entityManager.createQuery("UPDATE " + ResourceHistoryTable.class.getSimpleName() + " d SET d.myForcedId = null").executeUpdate(); entityManager.createQuery("UPDATE " + ResourceTable.class.getSimpleName() + " d SET d.myForcedId = null").executeUpdate(); + entityManager.createQuery("UPDATE " + TermCodeSystem.class.getSimpleName() + " d SET d.myCurrentVersion = null").executeUpdate(); return null; } }); @@ -279,6 +280,8 @@ public abstract class BaseJpaTest { txTemplate.execute(new TransactionCallback() { @Override public Void doInTransaction(TransactionStatus theStatus) { + entityManager.createQuery("DELETE from " + TermConceptProperty.class.getSimpleName() + " d").executeUpdate(); + entityManager.createQuery("DELETE from " + TermConceptDesignation.class.getSimpleName() + " d").executeUpdate(); entityManager.createQuery("DELETE from " + TermConcept.class.getSimpleName() + " d").executeUpdate(); for (TermCodeSystem next : entityManager.createQuery("SELECT c FROM " + TermCodeSystem.class.getName() + " c", TermCodeSystem.class).getResultList()) { next.setCurrentVersion(null); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java index b894e83a823..70a5dac6d8d 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu2/FhirSystemDaoDstu2Test.java @@ -1,6 +1,7 @@ package ca.uhn.fhir.jpa.dao.dstu2; import ca.uhn.fhir.jpa.dao.BaseHapiFhirDao; +import ca.uhn.fhir.jpa.dao.SearchParameterMap; import ca.uhn.fhir.jpa.entity.TagTypeEnum; import ca.uhn.fhir.jpa.provider.SystemProviderDstu2Test; import ca.uhn.fhir.model.api.IResource; @@ -18,11 +19,13 @@ import ca.uhn.fhir.model.dstu2.resource.Bundle.EntryResponse; import ca.uhn.fhir.model.dstu2.valueset.BundleTypeEnum; import ca.uhn.fhir.model.dstu2.valueset.HTTPVerbEnum; import ca.uhn.fhir.model.dstu2.valueset.IssueSeverityEnum; +import ca.uhn.fhir.model.dstu2.valueset.ObservationStatusEnum; import ca.uhn.fhir.model.primitive.IdDt; import ca.uhn.fhir.model.primitive.UriDt; import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; +import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.*; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.util.TestUtil; @@ -831,6 +834,213 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { } } + @Test + public void testTransactionDoesntUpdateUnchangesResourceWithPlaceholderIds() { + Bundle output, input; + Bundle.EntryResponse respEntry; + IdType createdPatientId; + SearchParameterMap map; + IBundleProvider search; + + input = new Bundle(); + + /* + * Create a transaction with a patient and an observation using + * placeholder IDs in them + */ + Patient pat = new Patient(); + pat.setId(IdType.newRandomUuid()); + pat.addIdentifier().setSystem("foo").setValue("bar"); + input + .addEntry() + .setResource(pat) + .setFullUrl(pat.getId()) + .getRequest() + .setMethod(HTTPVerbEnum.POST) + .setUrl("/Patient") + .setIfNoneExist("Patient?identifier=foo|bar"); + Observation obs = new Observation(); + obs.addIdentifier().setSystem("foo").setValue("dog"); + obs.getSubject().setReference(pat.getId()); + input + .addEntry() + .setResource(obs) + .getRequest() + .setMethod(HTTPVerbEnum.PUT) + .setUrl("/Observation?identifier=foo|dog"); + output = mySystemDao.transaction(mySrd, input); + + /* + * Both resrouces should be created and have version 1 + */ + respEntry = output.getEntry().get(0).getResponse(); + assertEquals("201 Created", respEntry.getStatus()); + createdPatientId = new IdType(respEntry.getLocation()); + assertEquals("Patient", createdPatientId.getResourceType()); + assertEquals("1", createdPatientId.getVersionIdPart()); + + respEntry = output.getEntry().get(1).getResponse(); + assertEquals("201 Created", respEntry.getStatus()); + IdType createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Observation", createdObservationId.getResourceType()); + assertEquals("1", createdObservationId.getVersionIdPart()); + + /* + * Searches for both resources should work and the reference + * should be substituted correctly + */ + // Patient + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Patient.SP_IDENTIFIER, new TokenParam("foo", "bar")); + search = myPatientDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdPatientId.toUnqualifiedVersionless().getValue())); + pat = (Patient) search.getResources(0, 1).get(0); + assertEquals("foo", pat.getIdentifierFirstRep().getSystem()); + // Observation + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Observation.SP_IDENTIFIER, new TokenParam("foo", "dog")); + search = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdObservationId.toUnqualifiedVersionless().getValue())); + obs = (Observation) search.getResources(0, 1).get(0); + assertEquals("foo", obs.getIdentifierFirstRep().getSystem()); + assertEquals(createdPatientId.toUnqualifiedVersionless().getValue(), obs.getSubject().getReference().getValue()); + + /* + * Now run the same transaction, which should not make any changes this time + * around + */ + + input = new Bundle(); + pat = new Patient(); + pat.setId(IdType.newRandomUuid()); + pat.addIdentifier().setSystem("foo").setValue("bar"); + input + .addEntry() + .setResource(pat) + .setFullUrl(pat.getId()) + .getRequest() + .setMethod(HTTPVerbEnum.POST) + .setUrl("/Patient") + .setIfNoneExist("Patient?identifier=foo|bar"); + obs = new Observation(); + obs.addIdentifier().setSystem("foo").setValue("dog"); + obs.getSubject().setReference(pat.getId()); + input + .addEntry() + .setResource(obs) + .getRequest() + .setMethod(HTTPVerbEnum.PUT) + .setUrl("/Observation?identifier=foo|dog"); + output = mySystemDao.transaction(mySrd, input); + + /* + * Should still have version 1 of both resources + */ + respEntry = output.getEntry().get(0).getResponse(); + assertEquals("200 OK", respEntry.getStatus()); + createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Patient", createdObservationId.getResourceType()); + assertEquals("1", createdObservationId.getVersionIdPart()); + + respEntry = output.getEntry().get(1).getResponse(); + assertEquals("200 OK", respEntry.getStatus()); + createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Observation", createdObservationId.getResourceType()); + assertEquals("1", createdObservationId.getVersionIdPart()); + + /* + * Searches for both resources should still work and the reference + * should be substituted correctly + */ + // Patient + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Patient.SP_IDENTIFIER, new TokenParam("foo", "bar")); + search = myPatientDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdPatientId.toUnqualifiedVersionless().getValue())); + pat = (Patient) search.getResources(0, 1).get(0); + assertEquals("foo", pat.getIdentifierFirstRep().getSystem()); + // Observation + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Observation.SP_IDENTIFIER, new TokenParam("foo", "dog")); + search = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdObservationId.toUnqualifiedVersionless().getValue())); + obs = (Observation) search.getResources(0, 1).get(0); + assertEquals("foo", obs.getIdentifierFirstRep().getSystem()); + assertEquals(createdPatientId.toUnqualifiedVersionless().getValue(), obs.getSubject().getReference().getValue()); + + /* + * Now run the transaction, but this time with an actual + * change to the Observation + */ + + input = new Bundle(); + pat = new Patient(); + pat.setId(IdType.newRandomUuid()); + pat.addIdentifier().setSystem("foo").setValue("bar"); + input + .addEntry() + .setResource(pat) + .setFullUrl(pat.getId()) + .getRequest() + .setMethod(HTTPVerbEnum.POST) + .setUrl("/Patient") + .setIfNoneExist("Patient?identifier=foo|bar"); + obs = new Observation(); + obs.addIdentifier().setSystem("foo").setValue("dog"); + obs.setStatus(ObservationStatusEnum.FINAL); + obs.getSubject().setReference(pat.getId()); + input + .addEntry() + .setResource(obs) + .getRequest() + .setMethod(HTTPVerbEnum.PUT) + .setUrl("/Observation?identifier=foo|dog"); + output = mySystemDao.transaction(mySrd, input); + + /* + * Observation should now be version 2 + */ + respEntry = output.getEntry().get(0).getResponse(); + assertEquals("200 OK", respEntry.getStatus()); + createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Patient", createdObservationId.getResourceType()); + assertEquals("1", createdObservationId.getVersionIdPart()); + + respEntry = output.getEntry().get(1).getResponse(); + assertEquals("200 OK", respEntry.getStatus()); + createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Observation", createdObservationId.getResourceType()); + assertEquals("2", createdObservationId.getVersionIdPart()); + + /* + * Searches for both resources should still work and the reference + * should be substituted correctly + */ + // Patient + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Patient.SP_IDENTIFIER, new TokenParam("foo", "bar")); + search = myPatientDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdPatientId.toUnqualifiedVersionless().getValue())); + pat = (Patient) search.getResources(0, 1).get(0); + assertEquals("foo", pat.getIdentifierFirstRep().getSystem()); + // Observation + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Observation.SP_IDENTIFIER, new TokenParam("foo", "dog")); + search = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdObservationId.toUnqualifiedVersionless().getValue())); + obs = (Observation) search.getResources(0, 1).get(0); + assertEquals("foo", obs.getIdentifierFirstRep().getSystem()); + assertEquals(createdPatientId.toUnqualifiedVersionless().getValue(), obs.getSubject().getReference().getValue()); + assertEquals(ObservationStatusEnum.FINAL.getCode(), obs.getStatus()); + + } + @Test(expected = InvalidRequestException.class) public void testTransactionFailsWithDuplicateIds() { Bundle request = new Bundle(); @@ -1193,62 +1403,6 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { } - @Test - public void testTransactionUpdateResourceNewVersionCreatedWhenDataChanges() { - - Bundle request = new Bundle(); - String patientId = "Patient/IShouldUpdate"; - Patient p = new Patient(); - p.addName().addFamily("Hello"); - p.setId(patientId); - request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.PUT).setUrl(patientId); - - Bundle initialBundleResponse = mySystemDao.transaction(mySrd, request); - assertEquals(1, initialBundleResponse.getEntry().size()); - ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(initialBundleResponse)); - - Entry initialBundleResponseEntry = initialBundleResponse.getEntry().get(0); - assertEquals("201 Created", initialBundleResponseEntry.getResponse().getStatus()); - assertThat(initialBundleResponseEntry.getResponse().getEtag(), is(equalTo("1"))); - - p.addName().addFamily("AnotherName"); - - Bundle secondBundleResponse = mySystemDao.transaction(mySrd, request); - assertEquals(1, secondBundleResponse.getEntry().size()); - ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(secondBundleResponse)); - - Entry secondBundleResponseEntry = secondBundleResponse.getEntry().get(0); - assertEquals("200 OK", secondBundleResponseEntry.getResponse().getStatus()); - assertThat(secondBundleResponseEntry.getResponse().getEtag(), is(equalTo("2"))); - } - - @Test - public void testTransactionUpdateResourceNewVersionNotCreatedWhenDataNotChanged() { - - Bundle request = new Bundle(); - String patientId = "Patient/IShouldNotUpdate"; - Patient p = new Patient(); - p.addName().addFamily("Hello"); - p.setId(patientId); - request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.PUT).setUrl(patientId); - - Bundle initialBundleResponse = mySystemDao.transaction(mySrd, request); - assertEquals(1, initialBundleResponse.getEntry().size()); - ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(initialBundleResponse)); - - Entry initialBundleResponseEntry = initialBundleResponse.getEntry().get(0); - assertEquals("201 Created", initialBundleResponseEntry.getResponse().getStatus()); - assertThat(initialBundleResponseEntry.getResponse().getEtag(), is(equalTo("1"))); - - Bundle secondBundleResponse = mySystemDao.transaction(mySrd, request); - assertEquals(1, secondBundleResponse.getEntry().size()); - ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(secondBundleResponse)); - - Entry secondBundleResponseEntry = secondBundleResponse.getEntry().get(0); - assertEquals("200 OK", secondBundleResponseEntry.getResponse().getStatus()); - assertThat(secondBundleResponseEntry.getResponse().getEtag(), is(equalTo("1"))); - } - @Test public void testTransactionUpdateMatchUrlWithTwoMatch() { String methodName = "testTransactionUpdateMatchUrlWithTwoMatch"; @@ -1362,6 +1516,62 @@ public class FhirSystemDaoDstu2Test extends BaseJpaDstu2SystemTest { } + @Test + public void testTransactionUpdateResourceNewVersionCreatedWhenDataChanges() { + + Bundle request = new Bundle(); + String patientId = "Patient/IShouldUpdate"; + Patient p = new Patient(); + p.addName().addFamily("Hello"); + p.setId(patientId); + request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.PUT).setUrl(patientId); + + Bundle initialBundleResponse = mySystemDao.transaction(mySrd, request); + assertEquals(1, initialBundleResponse.getEntry().size()); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(initialBundleResponse)); + + Entry initialBundleResponseEntry = initialBundleResponse.getEntry().get(0); + assertEquals("201 Created", initialBundleResponseEntry.getResponse().getStatus()); + assertThat(initialBundleResponseEntry.getResponse().getEtag(), is(equalTo("1"))); + + p.addName().addFamily("AnotherName"); + + Bundle secondBundleResponse = mySystemDao.transaction(mySrd, request); + assertEquals(1, secondBundleResponse.getEntry().size()); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(secondBundleResponse)); + + Entry secondBundleResponseEntry = secondBundleResponse.getEntry().get(0); + assertEquals("200 OK", secondBundleResponseEntry.getResponse().getStatus()); + assertThat(secondBundleResponseEntry.getResponse().getEtag(), is(equalTo("2"))); + } + + @Test + public void testTransactionUpdateResourceNewVersionNotCreatedWhenDataNotChanged() { + + Bundle request = new Bundle(); + String patientId = "Patient/IShouldNotUpdate"; + Patient p = new Patient(); + p.addName().addFamily("Hello"); + p.setId(patientId); + request.addEntry().setResource(p).getRequest().setMethod(HTTPVerbEnum.PUT).setUrl(patientId); + + Bundle initialBundleResponse = mySystemDao.transaction(mySrd, request); + assertEquals(1, initialBundleResponse.getEntry().size()); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(initialBundleResponse)); + + Entry initialBundleResponseEntry = initialBundleResponse.getEntry().get(0); + assertEquals("201 Created", initialBundleResponseEntry.getResponse().getStatus()); + assertThat(initialBundleResponseEntry.getResponse().getEtag(), is(equalTo("1"))); + + Bundle secondBundleResponse = mySystemDao.transaction(mySrd, request); + assertEquals(1, secondBundleResponse.getEntry().size()); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(secondBundleResponse)); + + Entry secondBundleResponseEntry = secondBundleResponse.getEntry().get(0); + assertEquals("200 OK", secondBundleResponseEntry.getResponse().getStatus()); + assertThat(secondBundleResponseEntry.getResponse().getEtag(), is(equalTo("1"))); + } + @Test public void testTransactionWhichFailsPersistsNothing() { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java index 61b25430e87..7189e2548c6 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3Test.java @@ -12,6 +12,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.*; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.util.TestUtil; @@ -1439,6 +1440,213 @@ public class FhirSystemDaoDstu3Test extends BaseJpaDstu3SystemTest { assertTrue(p.getGeneralPractitionerFirstRep().getReferenceElement().isIdPartValidLong()); } + @Test + public void testTransactionDoesntUpdateUnchangesResourceWithPlaceholderIds() { + Bundle output, input; + BundleEntryResponseComponent respEntry; + IdType createdPatientId; + SearchParameterMap map; + IBundleProvider search; + + input = new Bundle(); + + /* + * Create a transaction with a patient and an observation using + * placeholder IDs in them + */ + Patient pat = new Patient(); + pat.setId(IdType.newRandomUuid()); + pat.addIdentifier().setSystem("foo").setValue("bar"); + input + .addEntry() + .setResource(pat) + .setFullUrl(pat.getId()) + .getRequest() + .setMethod(HTTPVerb.POST) + .setUrl("/Patient") + .setIfNoneExist("Patient?identifier=foo|bar"); + Observation obs = new Observation(); + obs.addIdentifier().setSystem("foo").setValue("dog"); + obs.getSubject().setReference(pat.getId()); + input + .addEntry() + .setResource(obs) + .getRequest() + .setMethod(HTTPVerb.PUT) + .setUrl("/Observation?identifier=foo|dog"); + output = mySystemDao.transaction(mySrd, input); + + /* + * Both resrouces should be created and have version 1 + */ + respEntry = output.getEntry().get(0).getResponse(); + assertEquals("201 Created", respEntry.getStatus()); + createdPatientId = new IdType(respEntry.getLocation()); + assertEquals("Patient", createdPatientId.getResourceType()); + assertEquals("1", createdPatientId.getVersionIdPart()); + + respEntry = output.getEntry().get(1).getResponse(); + assertEquals("201 Created", respEntry.getStatus()); + IdType createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Observation", createdObservationId.getResourceType()); + assertEquals("1", createdObservationId.getVersionIdPart()); + + /* + * Searches for both resources should work and the reference + * should be substituted correctly + */ + // Patient + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Patient.SP_IDENTIFIER, new TokenParam("foo", "bar")); + search = myPatientDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdPatientId.toUnqualifiedVersionless().getValue())); + pat = (Patient) search.getResources(0, 1).get(0); + assertEquals("foo", pat.getIdentifierFirstRep().getSystem()); + // Observation + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Observation.SP_IDENTIFIER, new TokenParam("foo", "dog")); + search = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdObservationId.toUnqualifiedVersionless().getValue())); + obs = (Observation) search.getResources(0, 1).get(0); + assertEquals("foo", obs.getIdentifierFirstRep().getSystem()); + assertEquals(createdPatientId.toUnqualifiedVersionless().getValue(), obs.getSubject().getReference()); + + /* + * Now run the same transaction, which should not make any changes this time + * around + */ + + input = new Bundle(); + pat = new Patient(); + pat.setId(IdType.newRandomUuid()); + pat.addIdentifier().setSystem("foo").setValue("bar"); + input + .addEntry() + .setResource(pat) + .setFullUrl(pat.getId()) + .getRequest() + .setMethod(HTTPVerb.POST) + .setUrl("/Patient") + .setIfNoneExist("Patient?identifier=foo|bar"); + obs = new Observation(); + obs.addIdentifier().setSystem("foo").setValue("dog"); + obs.getSubject().setReference(pat.getId()); + input + .addEntry() + .setResource(obs) + .getRequest() + .setMethod(HTTPVerb.PUT) + .setUrl("/Observation?identifier=foo|dog"); + output = mySystemDao.transaction(mySrd, input); + + /* + * Should still have version 1 of both resources + */ + respEntry = output.getEntry().get(0).getResponse(); + assertEquals("200 OK", respEntry.getStatus()); + createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Patient", createdObservationId.getResourceType()); + assertEquals("1", createdObservationId.getVersionIdPart()); + + respEntry = output.getEntry().get(1).getResponse(); + assertEquals("200 OK", respEntry.getStatus()); + createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Observation", createdObservationId.getResourceType()); + assertEquals("1", createdObservationId.getVersionIdPart()); + + /* + * Searches for both resources should still work and the reference + * should be substituted correctly + */ + // Patient + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Patient.SP_IDENTIFIER, new TokenParam("foo", "bar")); + search = myPatientDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdPatientId.toUnqualifiedVersionless().getValue())); + pat = (Patient) search.getResources(0, 1).get(0); + assertEquals("foo", pat.getIdentifierFirstRep().getSystem()); + // Observation + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Observation.SP_IDENTIFIER, new TokenParam("foo", "dog")); + search = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdObservationId.toUnqualifiedVersionless().getValue())); + obs = (Observation) search.getResources(0, 1).get(0); + assertEquals("foo", obs.getIdentifierFirstRep().getSystem()); + assertEquals(createdPatientId.toUnqualifiedVersionless().getValue(), obs.getSubject().getReference()); + + /* + * Now run the transaction, but this time with an actual + * change to the Observation + */ + + input = new Bundle(); + pat = new Patient(); + pat.setId(IdType.newRandomUuid()); + pat.addIdentifier().setSystem("foo").setValue("bar"); + input + .addEntry() + .setResource(pat) + .setFullUrl(pat.getId()) + .getRequest() + .setMethod(HTTPVerb.POST) + .setUrl("/Patient") + .setIfNoneExist("Patient?identifier=foo|bar"); + obs = new Observation(); + obs.addIdentifier().setSystem("foo").setValue("dog"); + obs.setStatus(ObservationStatus.FINAL); + obs.getSubject().setReference(pat.getId()); + input + .addEntry() + .setResource(obs) + .getRequest() + .setMethod(HTTPVerb.PUT) + .setUrl("/Observation?identifier=foo|dog"); + output = mySystemDao.transaction(mySrd, input); + + /* + * Observation should now be version 2 + */ + respEntry = output.getEntry().get(0).getResponse(); + assertEquals("200 OK", respEntry.getStatus()); + createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Patient", createdObservationId.getResourceType()); + assertEquals("1", createdObservationId.getVersionIdPart()); + + respEntry = output.getEntry().get(1).getResponse(); + assertEquals("200 OK", respEntry.getStatus()); + createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Observation", createdObservationId.getResourceType()); + assertEquals("2", createdObservationId.getVersionIdPart()); + + /* + * Searches for both resources should still work and the reference + * should be substituted correctly + */ + // Patient + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Patient.SP_IDENTIFIER, new TokenParam("foo", "bar")); + search = myPatientDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdPatientId.toUnqualifiedVersionless().getValue())); + pat = (Patient) search.getResources(0, 1).get(0); + assertEquals("foo", pat.getIdentifierFirstRep().getSystem()); + // Observation + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Observation.SP_IDENTIFIER, new TokenParam("foo", "dog")); + search = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdObservationId.toUnqualifiedVersionless().getValue())); + obs = (Observation) search.getResources(0, 1).get(0); + assertEquals("foo", obs.getIdentifierFirstRep().getSystem()); + assertEquals(createdPatientId.toUnqualifiedVersionless().getValue(), obs.getSubject().getReference()); + assertEquals(ObservationStatus.FINAL, obs.getStatus()); + + } + @Test public void testTransactionDoubleConditionalCreateOnlyCreatesOne() { Bundle inputBundle = new Bundle(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java index ef9da79462d..494c4a39985 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4Test.java @@ -11,6 +11,7 @@ import ca.uhn.fhir.rest.api.Constants; import ca.uhn.fhir.rest.api.RestOperationTypeEnum; import ca.uhn.fhir.rest.api.server.IBundleProvider; import ca.uhn.fhir.rest.param.ReferenceParam; +import ca.uhn.fhir.rest.param.TokenParam; import ca.uhn.fhir.rest.server.exceptions.*; import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor.ActionRequestDetails; import ca.uhn.fhir.util.TestUtil; @@ -1515,13 +1516,19 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { } @Test - public void testTransactionDoesntDoubleCreate() { + public void testTransactionDoesntUpdateUnchangesResourceWithPlaceholderIds() { Bundle output, input; BundleEntryResponseComponent respEntry; - IdType createdId; + IdType createdPatientId; + SearchParameterMap map; + IBundleProvider search; input = new Bundle(); + /* + * Create a transaction with a patient and an observation using + * placeholder IDs in them + */ Patient pat = new Patient(); pat.setId(IdType.newRandomUuid()); pat.addIdentifier().setSystem("foo").setValue("bar"); @@ -1542,24 +1549,51 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { .getRequest() .setMethod(HTTPVerb.PUT) .setUrl("/Observation?identifier=foo|dog"); - output = mySystemDao.transaction(mySrd, input); + + /* + * Both resrouces should be created and have version 1 + */ respEntry = output.getEntry().get(0).getResponse(); assertEquals("201 Created", respEntry.getStatus()); - createdId = new IdType(respEntry.getLocation()); - assertEquals("Patient", createdId.getResourceType()); - assertEquals("1", createdId.getVersionIdPart()); + createdPatientId = new IdType(respEntry.getLocation()); + assertEquals("Patient", createdPatientId.getResourceType()); + assertEquals("1", createdPatientId.getVersionIdPart()); respEntry = output.getEntry().get(1).getResponse(); assertEquals("201 Created", respEntry.getStatus()); - createdId = new IdType(respEntry.getLocation()); - assertEquals("Observation", createdId.getResourceType()); - assertEquals("1", createdId.getVersionIdPart()); + IdType createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Observation", createdObservationId.getResourceType()); + assertEquals("1", createdObservationId.getVersionIdPart()); - // Same bundle again + /* + * Searches for both resources should work and the reference + * should be substituted correctly + */ + // Patient + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Patient.SP_IDENTIFIER, new TokenParam("foo", "bar")); + search = myPatientDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdPatientId.toUnqualifiedVersionless().getValue())); + pat = (Patient) search.getResources(0,1).get(0); + assertEquals("foo", pat.getIdentifierFirstRep().getSystem()); + // Observation + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Observation.SP_IDENTIFIER, new TokenParam("foo", "dog")); + search = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdObservationId.toUnqualifiedVersionless().getValue())); + obs = (Observation) search.getResources(0,1).get(0); + assertEquals("foo", obs.getIdentifierFirstRep().getSystem()); + assertEquals(createdPatientId.toUnqualifiedVersionless().getValue(), obs.getSubject().getReference()); + + /* + * Now run the same transaction, which should not make any changes this time + * around + */ input = new Bundle(); - pat = new Patient(); pat.setId(IdType.newRandomUuid()); pat.addIdentifier().setSystem("foo").setValue("bar"); @@ -1580,20 +1614,111 @@ public class FhirSystemDaoR4Test extends BaseJpaR4SystemTest { .getRequest() .setMethod(HTTPVerb.PUT) .setUrl("/Observation?identifier=foo|dog"); - - output = mySystemDao.transaction(mySrd, input); + + /* + * Should still have version 1 of both resources + */ respEntry = output.getEntry().get(0).getResponse(); assertEquals("200 OK", respEntry.getStatus()); - createdId = new IdType(respEntry.getLocation()); - assertEquals("Patient", createdId.getResourceType()); - assertEquals("1", createdId.getVersionIdPart()); + createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Patient", createdObservationId.getResourceType()); + assertEquals("1", createdObservationId.getVersionIdPart()); respEntry = output.getEntry().get(1).getResponse(); assertEquals("200 OK", respEntry.getStatus()); - createdId = new IdType(respEntry.getLocation()); - assertEquals("Observation", createdId.getResourceType()); - assertEquals("1", createdId.getVersionIdPart()); + createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Observation", createdObservationId.getResourceType()); + assertEquals("1", createdObservationId.getVersionIdPart()); + + /* + * Searches for both resources should still work and the reference + * should be substituted correctly + */ + // Patient + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Patient.SP_IDENTIFIER, new TokenParam("foo", "bar")); + search = myPatientDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdPatientId.toUnqualifiedVersionless().getValue())); + pat = (Patient) search.getResources(0,1).get(0); + assertEquals("foo", pat.getIdentifierFirstRep().getSystem()); + // Observation + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Observation.SP_IDENTIFIER, new TokenParam("foo", "dog")); + search = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdObservationId.toUnqualifiedVersionless().getValue())); + obs = (Observation) search.getResources(0,1).get(0); + assertEquals("foo", obs.getIdentifierFirstRep().getSystem()); + assertEquals(createdPatientId.toUnqualifiedVersionless().getValue(), obs.getSubject().getReference()); + + /* + * Now run the transaction, but this time with an actual + * change to the Observation + */ + + input = new Bundle(); + pat = new Patient(); + pat.setId(IdType.newRandomUuid()); + pat.addIdentifier().setSystem("foo").setValue("bar"); + input + .addEntry() + .setResource(pat) + .setFullUrl(pat.getId()) + .getRequest() + .setMethod(HTTPVerb.POST) + .setUrl("/Patient") + .setIfNoneExist("Patient?identifier=foo|bar"); + obs = new Observation(); + obs.addIdentifier().setSystem("foo").setValue("dog"); + obs.setStatus(ObservationStatus.FINAL); + obs.getSubject().setReference(pat.getId()); + input + .addEntry() + .setResource(obs) + .getRequest() + .setMethod(HTTPVerb.PUT) + .setUrl("/Observation?identifier=foo|dog"); + output = mySystemDao.transaction(mySrd, input); + + /* + * Observation should now be version 2 + */ + respEntry = output.getEntry().get(0).getResponse(); + assertEquals("200 OK", respEntry.getStatus()); + createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Patient", createdObservationId.getResourceType()); + assertEquals("1", createdObservationId.getVersionIdPart()); + + respEntry = output.getEntry().get(1).getResponse(); + assertEquals("200 OK", respEntry.getStatus()); + createdObservationId = new IdType(respEntry.getLocation()); + assertEquals("Observation", createdObservationId.getResourceType()); + assertEquals("2", createdObservationId.getVersionIdPart()); + + /* + * Searches for both resources should still work and the reference + * should be substituted correctly + */ + // Patient + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Patient.SP_IDENTIFIER, new TokenParam("foo", "bar")); + search = myPatientDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdPatientId.toUnqualifiedVersionless().getValue())); + pat = (Patient) search.getResources(0,1).get(0); + assertEquals("foo", pat.getIdentifierFirstRep().getSystem()); + // Observation + map = new SearchParameterMap(); + map.setLoadSynchronous(true); + map.add(Observation.SP_IDENTIFIER, new TokenParam("foo", "dog")); + search = myObservationDao.search(map); + assertThat(toUnqualifiedVersionlessIdValues(search), contains(createdObservationId.toUnqualifiedVersionless().getValue())); + obs = (Observation) search.getResources(0,1).get(0); + assertEquals("foo", obs.getIdentifierFirstRep().getSystem()); + assertEquals(createdPatientId.toUnqualifiedVersionless().getValue(), obs.getSubject().getReference()); + assertEquals(ObservationStatus.FINAL, obs.getStatus()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java index 962286ef449..599b88dff55 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/BaseResourceProviderDstu3Test.java @@ -4,7 +4,6 @@ import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig; import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test; import ca.uhn.fhir.jpa.dao.dstu3.SearchParamRegistryDstu3; -import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.subscription.email.SubscriptionEmailInterceptor; @@ -54,16 +53,16 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test { protected static CloseableHttpClient ourHttpClient; protected static int ourPort; protected static RestfulServer ourRestServer; - private static Server ourServer; protected static String ourServerBase; protected static GenericWebApplicationContext ourWebApplicationContext; - private TerminologyUploaderProvider myTerminologyUploaderProvider; protected static SearchParamRegistryDstu3 ourSearchParamRegistry; protected static DatabaseBackedPagingProvider ourPagingProvider; protected static SubscriptionRestHookInterceptor ourRestHookSubscriptionInterceptor; protected static SubscriptionEmailInterceptor ourEmailSubscriptionInterceptor; protected static ISearchDao mySearchEntityDao; protected static ISearchCoordinatorSvc mySearchCoordinatorSvc; + private static Server ourServer; + private TerminologyUploaderProviderDstu3 myTerminologyUploaderProvider; public BaseResourceProviderDstu3Test() { super(); @@ -93,7 +92,7 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test { ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); - myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProvider.class); + myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderDstu3.class); ourRestServer.setPlainProviders(mySystemProvider, myTerminologyUploaderProvider); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java index dc224445819..134e41b2649 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java @@ -32,12 +32,6 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ResourceProviderDstu3CodeSystemTest.class); private IIdType myExtensionalVsId; - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } - - @Before @Transactional public void before02() throws IOException { @@ -47,11 +41,11 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst ValueSet upload = loadResourceFromClasspath(ValueSet.class, "/extensional-case-3-vs.xml"); myExtensionalVsId = myValueSetDao.create(upload, mySrd).getId().toUnqualifiedVersionless(); } - + @Test public void testLookupOnExternalCode() { ResourceProviderDstu3ValueSetTest.createExternalCs(myCodeSystemDao, myResourceTableDao, myTermSvc, mySrd); - + Parameters respParam = ourClient .operation() .onType(CodeSystem.class) @@ -62,9 +56,9 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - + assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); + assertEquals(("SYSTEM NAME"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals("Parent A", ((StringType)respParam.getParameter().get(1).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(2).getName()); @@ -82,9 +76,9 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - + assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); + assertEquals(("SYSTEM NAME"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); assertEquals("Parent A", ((StringType)respParam.getParameter().get(1).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(2).getName()); @@ -104,7 +98,7 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - + assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); @@ -145,7 +139,7 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - + assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); @@ -185,7 +179,7 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - + assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); @@ -247,7 +241,7 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst } //@formatter:on } - + @Test // @Ignore public void testLookupOperationForBuiltInCode() { @@ -263,7 +257,7 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - + assertEquals("name", respParam.getParameter().get(0).getName()); assertEquals("Unknown", ((StringType)respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); @@ -272,5 +266,10 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).booleanValue()); } + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); + } + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3Test.java index ba63454dc78..60b94ec3d2c 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/TerminologyUploaderProviderDstu3Test.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.provider.dstu3; +import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.Matchers.greaterThan; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; @@ -14,6 +15,7 @@ import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import org.apache.commons.io.IOUtils; import org.hl7.fhir.dstu3.model.Attachment; import org.hl7.fhir.dstu3.model.IntegerType; @@ -31,65 +33,81 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyUploaderProviderDstu3Test.class); - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); + private byte[] createLoincZip() throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ZipOutputStream zos = new ZipOutputStream(bos); + + zos.putNextEntry(new ZipEntry("loinc.csv")); + zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/loinc.csv"))); + zos.putNextEntry(new ZipEntry("LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV")); + zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV"))); + zos.close(); + + byte[] packageBytes = bos.toByteArray(); + return packageBytes; } + private byte[] createSctZip() throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ZipOutputStream zos = new ZipOutputStream(bos); + List inputNames = Arrays.asList("sct2_Concept_Full_INT_20160131.txt","sct2_Concept_Full-en_INT_20160131.txt","sct2_Description_Full-en_INT_20160131.txt","sct2_Identifier_Full_INT_20160131.txt","sct2_Relationship_Full_INT_20160131.txt","sct2_StatedRelationship_Full_INT_20160131.txt","sct2_TextDefinition_Full-en_INT_20160131.txt"); + for (String nextName : inputNames) { + zos.putNextEntry(new ZipEntry("SnomedCT_Release_INT_20160131_Full/Terminology/" + nextName)); + byte[] b = IOUtils.toByteArray(getClass().getResourceAsStream("/sct/" + nextName)); + zos.write(b); + } + zos.close(); + byte[] packageBytes = bos.toByteArray(); + return packageBytes; + } @Test - public void testUploadSct() throws Exception { + public void testUploadInvalidUrl() throws Exception { byte[] packageBytes = createSctZip(); - - //@formatter:off - Parameters respParam = ourClient - .operation() - .onServer() - .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) - .andParameter("package", new Attachment().setData(packageBytes)) - .execute(); - //@formatter:on - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); - ourLog.info(resp); - - assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); + try { + ourClient + .operation() + .onServer() + .named("upload-external-code-system") + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI + "FOO")) + .andParameter("package", new Attachment().setUrl("foo").setData(packageBytes)) + .execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Unknown URL: http://snomed.info/sctFOO", e.getMessage()); + } } @Test public void testUploadLoinc() throws Exception { byte[] packageBytes = createLoincZip(); - - //@formatter:off + Parameters respParam = ourClient .operation() .onServer() .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) - .andParameter("package", new Attachment().setData(packageBytes)) + .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) .execute(); - //@formatter:on String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); - + assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); - + /* * Try uploading a second time */ - - //@formatter:off + respParam = ourClient .operation() .onServer() .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) - .andParameter("package", new Attachment().setData(packageBytes)) + .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) .execute(); - //@formatter:on resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); @@ -97,73 +115,42 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs } @Test - public void testUploadSctLocalFile() throws Exception { - byte[] packageBytes = createSctZip(); - File tempFile = File.createTempFile("tmp", ".zip"); - tempFile.deleteOnExit(); - - FileOutputStream fos = new FileOutputStream(tempFile); - fos.write(packageBytes); - fos.close(); - - //@formatter:off - Parameters respParam = ourClient - .operation() - .onServer() - .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) - .andParameter("localfile", new StringType(tempFile.getAbsolutePath())) - .execute(); - //@formatter:on - - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); - ourLog.info(resp); - - assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); - } - - @Test - public void testUploadInvalidUrl() throws Exception { - byte[] packageBytes = createSctZip(); - - //@formatter:off + public void testUploadMissingPackage() throws Exception { + byte[] packageBytes = createLoincZip(); try { ourClient .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI + "FOO")) + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) .andParameter("package", new Attachment().setData(packageBytes)) .execute(); fail(); - } catch (InvalidRequestException e) { - assertEquals("HTTP 400 Bad Request: Unknown URL: http://snomed.info/sctFOO", e.getMessage()); + } catch (UnprocessableEntityException e) { + assertThat(e.getMessage(), containsString("Package is missing mandatory url element")); } - //@formatter:on } @Test public void testUploadMissingUrl() throws Exception { byte[] packageBytes = createSctZip(); - - //@formatter:off + try { ourClient .operation() .onServer() .named("upload-external-code-system") - .withParameter(Parameters.class, "package", new Attachment().setData(packageBytes)) + .withParameter(Parameters.class, "package", new Attachment().setUrl("foo").setData(packageBytes)) .execute(); fail(); } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: Unknown URL: ", e.getMessage()); } - //@formatter:on + } @Test - public void testUploadMissingPackage() throws Exception { - //@formatter:off + public void testUploadPackageMissingUrl() throws Exception { try { ourClient .operation() @@ -175,35 +162,55 @@ public class TerminologyUploaderProviderDstu3Test extends BaseResourceProviderDs } catch (InvalidRequestException e) { assertEquals("HTTP 400 Bad Request: No 'localfile' or 'package' parameter, or package had no data", e.getMessage()); } - //@formatter:on } - private byte[] createSctZip() throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ZipOutputStream zos = new ZipOutputStream(bos); - - List inputNames = Arrays.asList("sct2_Concept_Full_INT_20160131.txt","sct2_Concept_Full-en_INT_20160131.txt","sct2_Description_Full-en_INT_20160131.txt","sct2_Identifier_Full_INT_20160131.txt","sct2_Relationship_Full_INT_20160131.txt","sct2_StatedRelationship_Full_INT_20160131.txt","sct2_TextDefinition_Full-en_INT_20160131.txt"); - for (String nextName : inputNames) { - zos.putNextEntry(new ZipEntry("SnomedCT_Release_INT_20160131_Full/Terminology/" + nextName)); - zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/sct/" + nextName))); - } - zos.close(); - byte[] packageBytes = bos.toByteArray(); - return packageBytes; + @Test + public void testUploadSct() throws Exception { + byte[] packageBytes = createSctZip(); + + Parameters respParam = ourClient + .operation() + .onServer() + .named("upload-external-code-system") + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) + .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); } - - private byte[] createLoincZip() throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ZipOutputStream zos = new ZipOutputStream(bos); - - zos.putNextEntry(new ZipEntry("loinc.csv")); - zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/loinc.csv"))); - zos.putNextEntry(new ZipEntry("LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV")); - zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV"))); - zos.close(); - - byte[] packageBytes = bos.toByteArray(); - return packageBytes; + + @Test + public void testUploadSctLocalFile() throws Exception { + byte[] packageBytes = createSctZip(); + File tempFile = File.createTempFile("tmp", ".zip"); + tempFile.deleteOnExit(); + + FileOutputStream fos = new FileOutputStream(tempFile); + fos.write(packageBytes); + fos.close(); + + ourLog.info("File is: {}", tempFile.getAbsolutePath()); + + Parameters respParam = ourClient + .operation() + .onServer() + .named("upload-external-code-system") + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) + .andParameter("localfile", new StringType(tempFile.getAbsolutePath())) + .execute(); + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); + } + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java index 28f3fd52b29..12e8f7f0967 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/BaseResourceProviderR4Test.java @@ -4,7 +4,6 @@ import ca.uhn.fhir.jpa.config.WebsocketDispatcherConfig; import ca.uhn.fhir.jpa.dao.data.ISearchDao; import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test; import ca.uhn.fhir.jpa.dao.r4.SearchParamRegistryR4; -import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.jpa.search.ISearchCoordinatorSvc; import ca.uhn.fhir.jpa.subscription.resthook.SubscriptionRestHookInterceptor; @@ -59,7 +58,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test { protected static ISearchCoordinatorSvc mySearchCoordinatorSvc; private static Server ourServer; protected static GenericWebApplicationContext ourWebApplicationContext; - private TerminologyUploaderProvider myTerminologyUploaderProvider; + private TerminologyUploaderProviderR4 myTerminologyUploaderProvider; private Object ourGraphQLProvider; private boolean ourRestHookSubscriptionInterceptorRequested; @@ -90,7 +89,7 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test { ourRestServer.getFhirContext().setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); - myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProvider.class); + myTerminologyUploaderProvider = myAppCtx.getBean(TerminologyUploaderProviderR4.class); ourGraphQLProvider = myAppCtx.getBean("myGraphQLProvider"); ourRestServer.setPlainProviders(mySystemProvider, myTerminologyUploaderProvider, ourGraphQLProvider); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java index 62aeba4f74d..8b9ffbe4eb1 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java @@ -1,9 +1,12 @@ package ca.uhn.fhir.jpa.provider.r4; -import static org.hamcrest.Matchers.greaterThan; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; +import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.util.TestUtil; +import org.apache.commons.io.IOUtils; +import org.hl7.fhir.r4.model.*; +import org.junit.AfterClass; +import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.File; @@ -14,118 +17,45 @@ import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; -import org.apache.commons.io.IOUtils; -import org.hl7.fhir.r4.model.Attachment; -import org.hl7.fhir.r4.model.IntegerType; -import org.hl7.fhir.r4.model.Parameters; -import org.hl7.fhir.r4.model.StringType; -import org.hl7.fhir.r4.model.UriType; -import org.junit.AfterClass; -import org.junit.Test; - -import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.util.TestUtil; +import static org.hamcrest.Matchers.greaterThan; +import static org.junit.Assert.*; public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TerminologyUploaderProviderR4Test.class); - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); + private byte[] createLoincZip() throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ZipOutputStream zos = new ZipOutputStream(bos); + + zos.putNextEntry(new ZipEntry("loinc.csv")); + zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/loinc.csv"))); + zos.putNextEntry(new ZipEntry("LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV")); + zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV"))); + zos.close(); + + byte[] packageBytes = bos.toByteArray(); + return packageBytes; } + private byte[] createSctZip() throws IOException { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ZipOutputStream zos = new ZipOutputStream(bos); - - @Test - public void testUploadSct() throws Exception { - byte[] packageBytes = createSctZip(); - - //@formatter:off - Parameters respParam = myClient - .operation() - .onServer() - .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) - .andParameter("package", new Attachment().setData(packageBytes)) - .execute(); - //@formatter:on - - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); - ourLog.info(resp); - - assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); - } - - @Test - public void testUploadLoinc() throws Exception { - byte[] packageBytes = createLoincZip(); - - //@formatter:off - Parameters respParam = myClient - .operation() - .onServer() - .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) - .andParameter("package", new Attachment().setData(packageBytes)) - .execute(); - //@formatter:on - - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); - ourLog.info(resp); - - assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); - - /* - * Try uploading a second time - */ - - //@formatter:off - respParam = myClient - .operation() - .onServer() - .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) - .andParameter("package", new Attachment().setData(packageBytes)) - .execute(); - //@formatter:on - - resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); - ourLog.info(resp); - - } - - @Test - public void testUploadSctLocalFile() throws Exception { - byte[] packageBytes = createSctZip(); - File tempFile = File.createTempFile("tmp", ".zip"); - tempFile.deleteOnExit(); - - FileOutputStream fos = new FileOutputStream(tempFile); - fos.write(packageBytes); - fos.close(); - - //@formatter:off - Parameters respParam = myClient - .operation() - .onServer() - .named("upload-external-code-system") - .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) - .andParameter("localfile", new StringType(tempFile.getAbsolutePath())) - .execute(); - //@formatter:on - - String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); - ourLog.info(resp); - - assertThat(((IntegerType)respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); + List inputNames = Arrays.asList("sct2_Concept_Full_INT_20160131.txt", "sct2_Concept_Full-en_INT_20160131.txt", "sct2_Description_Full-en_INT_20160131.txt", "sct2_Identifier_Full_INT_20160131.txt", "sct2_Relationship_Full_INT_20160131.txt", "sct2_StatedRelationship_Full_INT_20160131.txt", "sct2_TextDefinition_Full-en_INT_20160131.txt"); + for (String nextName : inputNames) { + zos.putNextEntry(new ZipEntry("SnomedCT_Release_INT_20160131_Full/Terminology/" + nextName)); + zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/sct/" + nextName))); + } + zos.close(); + byte[] packageBytes = bos.toByteArray(); + return packageBytes; } @Test public void testUploadInvalidUrl() throws Exception { byte[] packageBytes = createSctZip(); - + //@formatter:off try { myClient @@ -133,7 +63,7 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes .onServer() .named("upload-external-code-system") .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI + "FOO")) - .andParameter("package", new Attachment().setData(packageBytes)) + .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) .execute(); fail(); } catch (InvalidRequestException e) { @@ -143,22 +73,41 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes } @Test - public void testUploadMissingUrl() throws Exception { - byte[] packageBytes = createSctZip(); - + public void testUploadLoinc() throws Exception { + byte[] packageBytes = createLoincZip(); + //@formatter:off - try { - myClient - .operation() - .onServer() - .named("upload-external-code-system") - .withParameter(Parameters.class, "package", new Attachment().setData(packageBytes)) - .execute(); - fail(); - } catch (InvalidRequestException e) { - assertEquals("HTTP 400 Bad Request: Unknown URL: ", e.getMessage()); - } + Parameters respParam = myClient + .operation() + .onServer() + .named("upload-external-code-system") + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) + .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) + .execute(); //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); + + /* + * Try uploading a second time + */ + + //@formatter:off + respParam = myClient + .operation() + .onServer() + .named("upload-external-code-system") + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.LOINC_URI)) + .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) + .execute(); + //@formatter:on + + resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + } @Test @@ -178,32 +127,74 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes //@formatter:on } - private byte[] createSctZip() throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ZipOutputStream zos = new ZipOutputStream(bos); - - List inputNames = Arrays.asList("sct2_Concept_Full_INT_20160131.txt","sct2_Concept_Full-en_INT_20160131.txt","sct2_Description_Full-en_INT_20160131.txt","sct2_Identifier_Full_INT_20160131.txt","sct2_Relationship_Full_INT_20160131.txt","sct2_StatedRelationship_Full_INT_20160131.txt","sct2_TextDefinition_Full-en_INT_20160131.txt"); - for (String nextName : inputNames) { - zos.putNextEntry(new ZipEntry("SnomedCT_Release_INT_20160131_Full/Terminology/" + nextName)); - zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/sct/" + nextName))); + @Test + public void testUploadMissingUrl() throws Exception { + byte[] packageBytes = createSctZip(); + + //@formatter:off + try { + myClient + .operation() + .onServer() + .named("upload-external-code-system") + .withParameter(Parameters.class, "package", new Attachment().setUrl("file.zip").setData(packageBytes)) + .execute(); + fail(); + } catch (InvalidRequestException e) { + assertEquals("HTTP 400 Bad Request: Unknown URL: ", e.getMessage()); } - zos.close(); - byte[] packageBytes = bos.toByteArray(); - return packageBytes; + //@formatter:on } - - private byte[] createLoincZip() throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ZipOutputStream zos = new ZipOutputStream(bos); - - zos.putNextEntry(new ZipEntry("loinc.csv")); - zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/loinc.csv"))); - zos.putNextEntry(new ZipEntry("LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV")); - zos.write(IOUtils.toByteArray(getClass().getResourceAsStream("/loinc/LOINC_2.54_MULTI-AXIAL_HIERARCHY.CSV"))); - zos.close(); - - byte[] packageBytes = bos.toByteArray(); - return packageBytes; + + @Test + public void testUploadSct() throws Exception { + byte[] packageBytes = createSctZip(); + + //@formatter:off + Parameters respParam = myClient + .operation() + .onServer() + .named("upload-external-code-system") + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) + .andParameter("package", new Attachment().setUrl("file.zip").setData(packageBytes)) + .execute(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); + } + + @Test + public void testUploadSctLocalFile() throws Exception { + byte[] packageBytes = createSctZip(); + File tempFile = File.createTempFile("tmp", ".zip"); + tempFile.deleteOnExit(); + + FileOutputStream fos = new FileOutputStream(tempFile); + fos.write(packageBytes); + fos.close(); + + //@formatter:off + Parameters respParam = myClient + .operation() + .onServer() + .named("upload-external-code-system") + .withParameter(Parameters.class, "url", new UriType(IHapiTerminologyLoaderSvc.SCT_URI)) + .andParameter("localfile", new StringType(tempFile.getAbsolutePath())) + .execute(); + //@formatter:on + + String resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); + ourLog.info(resp); + + assertThat(((IntegerType) respParam.getParameter().get(0).getValue()).getValue(), greaterThan(1)); + } + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java index 15bcb11848b..710a59ce74f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java @@ -75,7 +75,7 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(parameters)); assertEquals("SYSTEM", this.getPropertyPart(parameters, "property", "code").get().getValueAsString()); - assertEquals("Heart", this.getPropertyPart(parameters, "property", "value").get().getValueAsString()); + assertEquals("Heart", this.getPropertyPart(parameters, "property", "value").get().getValueAsString()); } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcSnomedCtTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcSnomedCtTest.java index 61c9db478f4..4fb9950230e 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcSnomedCtTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcSnomedCtTest.java @@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.TestUtil; import org.apache.commons.io.IOUtils; import org.hl7.fhir.r4.model.CodeSystem; @@ -126,8 +127,8 @@ public class TerminologyLoaderSvcSnomedCtTest { try { mySvc.loadSnomedCt(list(bos.toByteArray()), details); fail(); - } catch (InvalidRequestException e) { - assertEquals("Invalid input zip file, expected zip to contain the following name fragments: [Terminology/sct2_Description_Full-en, Terminology/sct2_Relationship_Full, Terminology/sct2_Concept_Full_] but found: []", e.getMessage()); + } catch (UnprocessableEntityException e) { + assertThat(e.getMessage(), containsString("Could not find the following mandatory files in input: ")); } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java index ff796a5ccb0..0fe0e96b7d6 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java @@ -11,8 +11,8 @@ import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.util.TestUtil; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; -import org.hl7.fhir.r4.model.ValueSet; import org.hl7.fhir.instance.model.api.IIdType; +import org.hl7.fhir.r4.model.ValueSet; import org.junit.AfterClass; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -28,136 +28,6 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { private static final String CS_URL = "http://example.com/my_code_system"; private static final String CS_URL_2 = "http://example.com/my_code_system2"; - - - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); - } - - - @Test - public void testStoreCodeSystemInvalidCyclicLoop() { - CodeSystem codeSystem = new CodeSystem(); - codeSystem.setUrl(CS_URL); - codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); - IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); - - ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong()); - - TermCodeSystemVersion cs = new TermCodeSystemVersion(); - cs.setResource(table); - - TermConcept parent = new TermConcept(); - parent.setCodeSystemVersion(cs); - parent.setCode("parent"); - cs.getConcepts().add(parent); - - TermConcept child = new TermConcept(); - child.setCodeSystemVersion(cs); - child.setCode("child"); - parent.addChild(child, RelationshipTypeEnum.ISA); - - child.addChild(parent, RelationshipTypeEnum.ISA); - - try { -// myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", , cs); - fail(); - } catch (InvalidRequestException e) { - assertEquals("CodeSystem contains circular reference around code parent", e.getMessage()); - } - } - - @Test - public void testFindCodesAboveAndBelowUnknown() { - createCodeSystem(); - - assertThat(myTermSvc.findCodesBelow("http://foo", "code"), empty()); - assertThat(myTermSvc.findCodesBelow(CS_URL, "code"), empty()); - assertThat(myTermSvc.findCodesAbove("http://foo", "code"), empty()); - assertThat(myTermSvc.findCodesAbove(CS_URL, "code"), empty()); - } - - @Test - public void testFindCodesBelowA() { - IIdType id = createCodeSystem(); - - Set concepts; - Set codes; - - concepts = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "ParentA"); - codes = toCodes(concepts); - assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA", "childAAB", "childAB")); - - concepts = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "childAA"); - codes = toCodes(concepts); - assertThat(codes, containsInAnyOrder("childAA", "childAAA", "childAAB")); - - // Try an unknown code - concepts = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "FOO_BAD_CODE"); - codes = toCodes(concepts); - assertThat(codes, empty()); - - } - - @Test - public void testFindCodesBelowBuiltInCodeSystem() { - List concepts; - Set codes; - - concepts = myTermSvc.findCodesBelow("http://hl7.org/fhir/allergy-clinical-status", "inactive"); - codes = toCodes(concepts); - assertThat(codes, containsInAnyOrder("inactive", "resolved")); - - concepts = myTermSvc.findCodesBelow("http://hl7.org/fhir/allergy-clinical-status", "resolved"); - codes = toCodes(concepts); - assertThat(codes, containsInAnyOrder("resolved")); - - // Unknown code - concepts = myTermSvc.findCodesBelow("http://hl7.org/fhir/allergy-clinical-status", "FOO"); - codes = toCodes(concepts); - assertThat(codes, empty()); - - // Unknown system - concepts = myTermSvc.findCodesBelow("http://hl7.org/fhir/allergy-clinical-status2222", "active"); - codes = toCodes(concepts); - assertThat(codes, empty()); - } - - @Test - public void testFindCodesAboveBuiltInCodeSystem() { - List concepts; - Set codes; - - concepts = myTermSvc.findCodesAbove("http://hl7.org/fhir/allergy-clinical-status", "active"); - codes = toCodes(concepts); - assertThat(codes, containsInAnyOrder("active")); - - concepts = myTermSvc.findCodesAbove("http://hl7.org/fhir/allergy-clinical-status", "resolved"); - codes = toCodes(concepts); - assertThat(codes, containsInAnyOrder("inactive", "resolved")); - - // Unknown code - concepts = myTermSvc.findCodesAbove("http://hl7.org/fhir/allergy-clinical-status", "FOO"); - codes = toCodes(concepts); - assertThat(codes, empty()); - - // Unknown system - concepts = myTermSvc.findCodesAbove("http://hl7.org/fhir/allergy-clinical-status2222", "active"); - codes = toCodes(concepts); - assertThat(codes, empty()); - } - - @Test - public void testReindexTerminology() { - IIdType id = createCodeSystem(); - - assertThat(mySystemDao.markAllResourcesForReindexing(), greaterThan(0)); - - assertThat(mySystemDao.performReindexingPass(100), greaterThan(0)); - } - - @Autowired private ITermCodeSystemDao myTermCodeSystemDao; @@ -181,16 +51,16 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { TermConcept childAAA = new TermConcept(cs, "childAAA"); childAAA.addPropertyString("propA", "valueAAA"); childAAA.addPropertyString("propB", "foo"); - childAAA.addDesignation() - .setUseSystem("http://designationsystem") - .setUseCode("somedesig") - .setUseDisplay("Designation Use") - .setValue("Bananas"); childAA.addChild(childAAA, RelationshipTypeEnum.ISA); TermConcept childAAB = new TermConcept(cs, "childAAB"); childAAB.addPropertyString("propA", "valueAAB"); childAAB.addPropertyString("propB", "foo"); + childAAB.addDesignation() + .setUseSystem("D1S") + .setUseCode("D1C") + .setUseDisplay("D1D") + .setValue("D1V"); childAA.addChild(childAAB, RelationshipTypeEnum.ISA); TermConcept childAB = new TermConcept(cs, "childAB"); @@ -199,7 +69,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { TermConcept parentB = new TermConcept(cs, "ParentB"); cs.getConcepts().add(parentB); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL,"SYSTEM NAME" , cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs); return id; } @@ -218,30 +88,48 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { TermConcept parentA = new TermConcept(cs, "CS2"); cs.getConcepts().add(parentA); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL_2,"SYSTEM NAME" , cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL_2, "SYSTEM NAME", cs); return id; } @Test - public void testFindCodesAbove() { - IIdType id = createCodeSystem(); + public void testCreateDuplicateCodeSystemUri() { + CodeSystem codeSystem = new CodeSystem(); + codeSystem.setUrl(CS_URL); + codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); - Set concepts; - Set codes; + ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong()); - concepts = myTermSvc.findCodesAbove(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "childAA"); - codes = toCodes(concepts); - assertThat(codes, containsInAnyOrder("ParentA", "childAA")); + TermCodeSystemVersion cs = new TermCodeSystemVersion(); + cs.setResource(table); + + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs); + + // Update + cs = new TermCodeSystemVersion(); + TermConcept parentA = new TermConcept(cs, "ParentA"); + cs.getConcepts().add(parentA); + id = myCodeSystemDao.update(codeSystem, null, true, true, mySrd).getId().toUnqualified(); + table = myResourceTableDao.findOne(id.getIdPartAsLong()); + cs.setResource(table); + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs); + + // Try to update to a different resource + codeSystem = new CodeSystem(); + codeSystem.setUrl(CS_URL); + codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); + id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); + table = myResourceTableDao.findOne(id.getIdPartAsLong()); + cs.setResource(table); + try { + myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs); + fail(); + } catch (UnprocessableEntityException e) { + assertThat(e.getMessage(), containsString("Can not create multiple code systems with URI \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/")); + } - concepts = myTermSvc.findCodesAbove(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "childAAB"); - codes = toCodes(concepts); - assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAB")); - - // Try an unknown code - concepts = myTermSvc.findCodesAbove(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "FOO_BAD_CODE"); - codes = toCodes(concepts); - assertThat(codes, empty()); } @Test @@ -310,18 +198,143 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { assertThat(codes, containsInAnyOrder("ParentA", "childAAA", "childAAB", "childAA", "childAB", "ParentB")); } - private List toCodesContains(List theContains) { - List retVal = new ArrayList<>(); + @Test + public void testPropertiesAndDesignationsPreservedInExpansion() { + createCodeSystem(); - for (ValueSet.ValueSetExpansionContainsComponent next : theContains) { - retVal.add(next.getCode()); - } + List codes; - return retVal; + ValueSet vs = new ValueSet(); + ValueSet.ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(CS_URL); + include.addConcept().setCode("childAAB"); + ValueSet outcome = myTermSvc.expandValueSet(vs); + + codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("childAAB")); + + ValueSet.ValueSetExpansionContainsComponent concept = outcome.getExpansion().getContains().get(0); + assertEquals("childAAB", concept.getCode()); + assertEquals("http://example.com/my_code_system", concept.getSystem()); + assertEquals(null, concept.getDisplay()); + assertEquals("D1S", concept.getDesignation().get(0).getUse().getSystem()); + assertEquals("D1C", concept.getDesignation().get(0).getUse().getCode()); + assertEquals("D1D", concept.getDesignation().get(0).getUse().getDisplay()); + assertEquals("D1V", concept.getDesignation().get(0).getValue()); } @Test - public void testCreateDuplicateCodeSystemUri() { + public void testFindCodesAbove() { + IIdType id = createCodeSystem(); + + Set concepts; + Set codes; + + concepts = myTermSvc.findCodesAbove(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "childAA"); + codes = toCodes(concepts); + assertThat(codes, containsInAnyOrder("ParentA", "childAA")); + + concepts = myTermSvc.findCodesAbove(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "childAAB"); + codes = toCodes(concepts); + assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAB")); + + // Try an unknown code + concepts = myTermSvc.findCodesAbove(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "FOO_BAD_CODE"); + codes = toCodes(concepts); + assertThat(codes, empty()); + } + + @Test + public void testFindCodesAboveAndBelowUnknown() { + createCodeSystem(); + + assertThat(myTermSvc.findCodesBelow("http://foo", "code"), empty()); + assertThat(myTermSvc.findCodesBelow(CS_URL, "code"), empty()); + assertThat(myTermSvc.findCodesAbove("http://foo", "code"), empty()); + assertThat(myTermSvc.findCodesAbove(CS_URL, "code"), empty()); + } + + @Test + public void testFindCodesAboveBuiltInCodeSystem() { + List concepts; + Set codes; + + concepts = myTermSvc.findCodesAbove("http://hl7.org/fhir/allergy-clinical-status", "active"); + codes = toCodes(concepts); + assertThat(codes, containsInAnyOrder("active")); + + concepts = myTermSvc.findCodesAbove("http://hl7.org/fhir/allergy-clinical-status", "resolved"); + codes = toCodes(concepts); + assertThat(codes, containsInAnyOrder("inactive", "resolved")); + + // Unknown code + concepts = myTermSvc.findCodesAbove("http://hl7.org/fhir/allergy-clinical-status", "FOO"); + codes = toCodes(concepts); + assertThat(codes, empty()); + + // Unknown system + concepts = myTermSvc.findCodesAbove("http://hl7.org/fhir/allergy-clinical-status2222", "active"); + codes = toCodes(concepts); + assertThat(codes, empty()); + } + + @Test + public void testFindCodesBelowA() { + IIdType id = createCodeSystem(); + + Set concepts; + Set codes; + + concepts = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "ParentA"); + codes = toCodes(concepts); + assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA", "childAAB", "childAB")); + + concepts = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "childAA"); + codes = toCodes(concepts); + assertThat(codes, containsInAnyOrder("childAA", "childAAA", "childAAB")); + + // Try an unknown code + concepts = myTermSvc.findCodesBelow(id.getIdPartAsLong(), id.getVersionIdPartAsLong(), "FOO_BAD_CODE"); + codes = toCodes(concepts); + assertThat(codes, empty()); + + } + + @Test + public void testFindCodesBelowBuiltInCodeSystem() { + List concepts; + Set codes; + + concepts = myTermSvc.findCodesBelow("http://hl7.org/fhir/allergy-clinical-status", "inactive"); + codes = toCodes(concepts); + assertThat(codes, containsInAnyOrder("inactive", "resolved")); + + concepts = myTermSvc.findCodesBelow("http://hl7.org/fhir/allergy-clinical-status", "resolved"); + codes = toCodes(concepts); + assertThat(codes, containsInAnyOrder("resolved")); + + // Unknown code + concepts = myTermSvc.findCodesBelow("http://hl7.org/fhir/allergy-clinical-status", "FOO"); + codes = toCodes(concepts); + assertThat(codes, empty()); + + // Unknown system + concepts = myTermSvc.findCodesBelow("http://hl7.org/fhir/allergy-clinical-status2222", "active"); + codes = toCodes(concepts); + assertThat(codes, empty()); + } + + @Test + public void testReindexTerminology() { + IIdType id = createCodeSystem(); + + assertThat(mySystemDao.markAllResourcesForReindexing(), greaterThan(0)); + + assertThat(mySystemDao.performReindexingPass(100), greaterThan(0)); + } + + @Test + public void testStoreCodeSystemInvalidCyclicLoop() { CodeSystem codeSystem = new CodeSystem(); codeSystem.setUrl(CS_URL); codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); @@ -332,31 +345,39 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { TermCodeSystemVersion cs = new TermCodeSystemVersion(); cs.setResource(table); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL,"SYSTEM NAME" , cs); + TermConcept parent = new TermConcept(); + parent.setCodeSystemVersion(cs); + parent.setCode("parent"); + cs.getConcepts().add(parent); - // Update - cs = new TermCodeSystemVersion(); - TermConcept parentA = new TermConcept(cs, "ParentA"); - cs.getConcepts().add(parentA); - id = myCodeSystemDao.update(codeSystem, null, true, true, mySrd).getId().toUnqualified(); - table = myResourceTableDao.findOne(id.getIdPartAsLong()); - cs.setResource(table); - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL,"SYSTEM NAME" , cs); + TermConcept child = new TermConcept(); + child.setCodeSystemVersion(cs); + child.setCode("child"); + parent.addChild(child, RelationshipTypeEnum.ISA); + + child.addChild(parent, RelationshipTypeEnum.ISA); - // Try to update to a different resource - codeSystem = new CodeSystem(); - codeSystem.setUrl(CS_URL); - codeSystem.setContent(CodeSystemContentMode.NOTPRESENT); - id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified(); - table = myResourceTableDao.findOne(id.getIdPartAsLong()); - cs.setResource(table); try { - myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL,"SYSTEM NAME" , cs); + myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://foo", "SYSTEM NAME", cs); fail(); - } catch (UnprocessableEntityException e) { - assertThat(e.getMessage(), containsString("Can not create multiple code systems with URI \"http://example.com/my_code_system\", already have one with resource ID: CodeSystem/")); + } catch (InvalidRequestException e) { + assertEquals("CodeSystem contains circular reference around code parent", e.getMessage()); + } + } + + private List toCodesContains(List theContains) { + List retVal = new ArrayList<>(); + + for (ValueSet.ValueSetExpansionContainsComponent next : theContains) { + retVal.add(next.getCode()); } + return retVal; + } + + @AfterClass + public static void afterClassClearContext() { + TestUtil.clearAllStaticFieldsForUnitTest(); } } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java index 184ae74ac78..a70c9399b8b 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/TestRestfulServer.java @@ -8,9 +8,10 @@ import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.JpaSystemProviderDstu2; import ca.uhn.fhir.jpa.provider.dstu3.JpaConformanceProviderDstu3; import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3; +import ca.uhn.fhir.jpa.provider.dstu3.TerminologyUploaderProviderDstu3; import ca.uhn.fhir.jpa.provider.r4.JpaConformanceProviderR4; import ca.uhn.fhir.jpa.provider.r4.JpaSystemProviderR4; -import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; +import ca.uhn.fhir.jpa.provider.r4.TerminologyUploaderProviderR4; import ca.uhn.fhir.jpa.search.DatabaseBackedPagingProvider; import ca.uhn.fhir.narrative.DefaultThymeleafNarrativeGenerator; import ca.uhn.fhir.rest.api.EncodingEnum; @@ -50,6 +51,13 @@ public class TestRestfulServer extends RestfulServer { private AnnotationConfigWebApplicationContext myAppCtx; + @Override + public void destroy() { + super.destroy(); + ourLog.info("Server is shutting down"); + myAppCtx.destroy(); + } + @SuppressWarnings("unchecked") @Override protected void initialize() throws ServletException { @@ -74,79 +82,79 @@ public class TestRestfulServer extends RestfulServer { ETagSupportEnum etagSupport; String baseUrlProperty; List plainProviders = new ArrayList(); - + switch (fhirVersionParam.trim().toUpperCase()) { - case "TDL2": - case "DSTU2": { - myAppCtx = new AnnotationConfigWebApplicationContext(); - myAppCtx.setServletConfig(getServletConfig()); - myAppCtx.setParent(parentAppCtx); - if ("TDL2".equals(fhirVersionParam.trim().toUpperCase())) { - myAppCtx.register(TdlDstu2Config.class); - baseUrlProperty = FHIR_BASEURL_TDL2; - } else { - myAppCtx.register(TestDstu2Config.class, WebsocketDispatcherConfig.class); - baseUrlProperty = FHIR_BASEURL_DSTU2; + case "TDL2": + case "DSTU2": { + myAppCtx = new AnnotationConfigWebApplicationContext(); + myAppCtx.setServletConfig(getServletConfig()); + myAppCtx.setParent(parentAppCtx); + if ("TDL2".equals(fhirVersionParam.trim().toUpperCase())) { + myAppCtx.register(TdlDstu2Config.class); + baseUrlProperty = FHIR_BASEURL_TDL2; + } else { + myAppCtx.register(TestDstu2Config.class, WebsocketDispatcherConfig.class); + baseUrlProperty = FHIR_BASEURL_DSTU2; + } + myAppCtx.refresh(); + setFhirContext(FhirContext.forDstu2()); + beans = myAppCtx.getBean("myResourceProvidersDstu2", List.class); + plainProviders.add(myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class)); + systemDao = myAppCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class); + etagSupport = ETagSupportEnum.ENABLED; + JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao, myAppCtx.getBean(DaoConfig.class)); + confProvider.setImplementationDescription(implDesc); + setServerConformanceProvider(confProvider); + break; } - myAppCtx.refresh(); - setFhirContext(FhirContext.forDstu2()); - beans = myAppCtx.getBean("myResourceProvidersDstu2", List.class); - plainProviders.add(myAppCtx.getBean("mySystemProviderDstu2", JpaSystemProviderDstu2.class)); - systemDao = myAppCtx.getBean("mySystemDaoDstu2", IFhirSystemDao.class); - etagSupport = ETagSupportEnum.ENABLED; - JpaConformanceProviderDstu2 confProvider = new JpaConformanceProviderDstu2(this, systemDao, myAppCtx.getBean(DaoConfig.class)); - confProvider.setImplementationDescription(implDesc); - setServerConformanceProvider(confProvider); - break; - } - case "TDL3": - case "DSTU3": { - myAppCtx = new AnnotationConfigWebApplicationContext(); - myAppCtx.setServletConfig(getServletConfig()); - myAppCtx.setParent(parentAppCtx); - if ("TDL3".equals(fhirVersionParam.trim().toUpperCase())) { - myAppCtx.register(TdlDstu3Config.class); - baseUrlProperty = FHIR_BASEURL_TDL3; - } else { - myAppCtx.register(TestDstu3Config.class, WebsocketDispatcherConfig.class); - baseUrlProperty = FHIR_BASEURL_DSTU3; + case "TDL3": + case "DSTU3": { + myAppCtx = new AnnotationConfigWebApplicationContext(); + myAppCtx.setServletConfig(getServletConfig()); + myAppCtx.setParent(parentAppCtx); + if ("TDL3".equals(fhirVersionParam.trim().toUpperCase())) { + myAppCtx.register(TdlDstu3Config.class); + baseUrlProperty = FHIR_BASEURL_TDL3; + } else { + myAppCtx.register(TestDstu3Config.class, WebsocketDispatcherConfig.class); + baseUrlProperty = FHIR_BASEURL_DSTU3; + } + myAppCtx.refresh(); + setFhirContext(FhirContext.forDstu3()); + beans = myAppCtx.getBean("myResourceProvidersDstu3", List.class); + plainProviders.add(myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class)); + systemDao = myAppCtx.getBean("mySystemDaoDstu3", IFhirSystemDao.class); + etagSupport = ETagSupportEnum.ENABLED; + JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao, myAppCtx.getBean(DaoConfig.class)); + confProvider.setImplementationDescription(implDesc); + setServerConformanceProvider(confProvider); + plainProviders.add(myAppCtx.getBean(TerminologyUploaderProviderDstu3.class)); + break; } - myAppCtx.refresh(); - setFhirContext(FhirContext.forDstu3()); - beans = myAppCtx.getBean("myResourceProvidersDstu3", List.class); - plainProviders.add(myAppCtx.getBean("mySystemProviderDstu3", JpaSystemProviderDstu3.class)); - systemDao = myAppCtx.getBean("mySystemDaoDstu3", IFhirSystemDao.class); - etagSupport = ETagSupportEnum.ENABLED; - JpaConformanceProviderDstu3 confProvider = new JpaConformanceProviderDstu3(this, systemDao, myAppCtx.getBean(DaoConfig.class)); - confProvider.setImplementationDescription(implDesc); - setServerConformanceProvider(confProvider); - plainProviders.add(myAppCtx.getBean(TerminologyUploaderProvider.class)); - break; + case "R4": { + myAppCtx = new AnnotationConfigWebApplicationContext(); + myAppCtx.setServletConfig(getServletConfig()); + myAppCtx.setParent(parentAppCtx); + myAppCtx.register(TestR4Config.class, WebsocketDispatcherConfig.class); + baseUrlProperty = FHIR_BASEURL_R4; + myAppCtx.refresh(); + setFhirContext(FhirContext.forR4()); + beans = myAppCtx.getBean("myResourceProvidersR4", List.class); + plainProviders.add(myAppCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class)); + systemDao = myAppCtx.getBean("mySystemDaoR4", IFhirSystemDao.class); + etagSupport = ETagSupportEnum.ENABLED; + JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(this, systemDao, myAppCtx.getBean(DaoConfig.class)); + confProvider.setImplementationDescription(implDesc); + setServerConformanceProvider(confProvider); + plainProviders.add(myAppCtx.getBean(TerminologyUploaderProviderR4.class)); + break; + } + default: + throw new ServletException("Unknown FHIR version specified in init-param[FhirVersion]: " + fhirVersionParam); } - case "R4": { - myAppCtx = new AnnotationConfigWebApplicationContext(); - myAppCtx.setServletConfig(getServletConfig()); - myAppCtx.setParent(parentAppCtx); - myAppCtx.register(TestR4Config.class, WebsocketDispatcherConfig.class); - baseUrlProperty = FHIR_BASEURL_R4; - myAppCtx.refresh(); - setFhirContext(FhirContext.forR4()); - beans = myAppCtx.getBean("myResourceProvidersR4", List.class); - plainProviders.add(myAppCtx.getBean("mySystemProviderR4", JpaSystemProviderR4.class)); - systemDao = myAppCtx.getBean("mySystemDaoR4", IFhirSystemDao.class); - etagSupport = ETagSupportEnum.ENABLED; - JpaConformanceProviderR4 confProvider = new JpaConformanceProviderR4(this, systemDao, myAppCtx.getBean(DaoConfig.class)); - confProvider.setImplementationDescription(implDesc); - setServerConformanceProvider(confProvider); - plainProviders.add(myAppCtx.getBean(TerminologyUploaderProvider.class)); - break; - } - default: - throw new ServletException("Unknown FHIR version specified in init-param[FhirVersion]: " + fhirVersionParam); - } - + /* - * On the DSTU2 endpoint, we want to enable ETag support + * On the DSTU2 endpoint, we want to enable ETag support */ setETagSupport(etagSupport); @@ -155,9 +163,9 @@ public class TestRestfulServer extends RestfulServer { */ FhirContext ctx = getFhirContext(); ctx.setNarrativeGenerator(new DefaultThymeleafNarrativeGenerator()); - + /* - * The resource and system providers (which actually implement the various FHIR + * The resource and system providers (which actually implement the various FHIR * operations in this server) are all retrieved from the spring context above * and are provided to the server here. */ @@ -187,17 +195,17 @@ public class TestRestfulServer extends RestfulServer { responseHighlighterInterceptor.setShowRequestHeaders(false); responseHighlighterInterceptor.setShowResponseHeaders(true); registerInterceptor(responseHighlighterInterceptor); - + registerInterceptor(new BanUnsupportedHttpMethodsInterceptor()); - + /* * Default to JSON with pretty printing */ setDefaultPrettyPrint(true); setDefaultResponseEncoding(EncodingEnum.JSON); - + /* - * The server's base URL (e.g. http://fhirtest.uhn.ca/baseDstu2) is + * The server's base URL (e.g. http://fhirtest.uhn.ca/baseDstu2) is * pulled from a system property, which is helpful if you want to try * hosting your own copy of this server. */ @@ -210,12 +218,12 @@ public class TestRestfulServer extends RestfulServer { } } setServerAddressStrategy(new MyHardcodedServerAddressStrategy(baseUrl)); - + /* - * Spool results to the database + * Spool results to the database */ setPagingProvider(myAppCtx.getBean(DatabaseBackedPagingProvider.class)); - + /* * Load interceptors for the server from Spring */ @@ -226,13 +234,6 @@ public class TestRestfulServer extends RestfulServer { } - @Override - public void destroy() { - super.destroy(); - ourLog.info("Server is shutting down"); - myAppCtx.destroy(); - } - /** * The public server is deployed to http://fhirtest.uhn.ca and the JEE webserver * where this FHIR server is deployed is actually fronted by an Apache HTTPd instance, @@ -248,7 +249,7 @@ public class TestRestfulServer extends RestfulServer { public String determineServerBase(ServletContext theServletContext, HttpServletRequest theRequest) { /* * This is a bit of a hack, but we want to support both HTTP and HTTPS seamlessly - * so we have the outer httpd proxy relay requests to the Java container on + * so we have the outer httpd proxy relay requests to the Java container on * port 28080 for http and 28081 for https. */ String retVal = super.determineServerBase(theServletContext, theRequest); @@ -257,8 +258,8 @@ public class TestRestfulServer extends RestfulServer { } return retVal; } - + } - - + + } diff --git a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java index 9e7ec5170de..f3acbc931ea 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java +++ b/hapi-fhir-jpaserver-uhnfhirtest/src/main/java/ca/uhn/fhirtest/interceptor/PublicSecurityInterceptor.java @@ -1,7 +1,7 @@ package ca.uhn.fhirtest.interceptor; import ca.uhn.fhir.jpa.provider.BaseJpaSystemProvider; -import ca.uhn.fhir.jpa.provider.TerminologyUploaderProvider; +import ca.uhn.fhir.jpa.provider.BaseTerminologyUploaderProvider; import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; import ca.uhn.fhir.rest.server.interceptor.auth.AuthorizationInterceptor; @@ -16,8 +16,8 @@ import static org.apache.commons.lang3.StringUtils.isBlank; public class PublicSecurityInterceptor extends AuthorizationInterceptor { - private HashSet myTokens; private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PublicSecurityInterceptor.class); + private HashSet myTokens; public PublicSecurityInterceptor() { String passwordsString = System.getProperty("fhir.tdlpass"); @@ -34,7 +34,7 @@ public class PublicSecurityInterceptor extends AuthorizationInterceptor { if (isBlank(authHeader)) { return new RuleBuilder() .deny().operation().named(BaseJpaSystemProvider.MARK_ALL_RESOURCES_FOR_REINDEXING).onServer().andThen() - .deny().operation().named(TerminologyUploaderProvider.UPLOAD_EXTERNAL_CODE_SYSTEM).onServer().andThen() + .deny().operation().named(BaseTerminologyUploaderProvider.UPLOAD_EXTERNAL_CODE_SYSTEM).onServer().andThen() .allowAll() .build(); } diff --git a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java index 52d998878dd..ee4b0af5013 100644 --- a/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java +++ b/hapi-fhir-server/src/main/java/ca/uhn/fhir/rest/server/RestfulServer.java @@ -279,8 +279,11 @@ public class RestfulServer extends HttpServlet implements IRestfulServer myStructureDefinitions; private Map myValueSets; + private void addConcepts(ConceptSetComponent theInclude, ValueSetExpansionComponent theRetVal, Set theWantCodes, List theConcepts) { + for (ConceptDefinitionComponent next : theConcepts) { + if (theWantCodes.isEmpty() || theWantCodes.contains(next.getCode())) { + theRetVal + .addContains() + .setSystem(theInclude.getSystem()) + .setCode(next.getCode()) + .setDisplay(next.getDisplay()); + } + addConcepts(theInclude, theRetVal, theWantCodes, next.getConcept()); + } + } + @Override public ValueSetExpansionComponent expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) { ValueSetExpansionComponent retVal = new ValueSetExpansionComponent(); - Set wantCodes = new HashSet(); + Set wantCodes = new HashSet<>(); for (ConceptReferenceComponent next : theInclude.getConcept()) { wantCodes.add(next.getCode()); } CodeSystem system = fetchCodeSystem(theContext, theInclude.getSystem()); - for (ConceptDefinitionComponent next : system.getConcept()) { - if (wantCodes.isEmpty() || wantCodes.contains(next.getCode())) { - retVal.addContains().setSystem(theInclude.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay()); + if (system != null) { + List concepts = system.getConcept(); + addConcepts(theInclude, retVal, wantCodes, concepts); + } + + for (UriType next : theInclude.getValueSet()) { + ValueSet vs = myValueSets.get(defaultString(next.getValueAsString())); + if (vs != null) { + for (ConceptSetComponent nextInclude : vs.getCompose().getInclude()) { + ValueSetExpansionComponent contents = expandValueSet(theContext, nextInclude); + retVal.getContains().addAll(contents.getContains()); + } } } diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupport.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupport.java index 8bcc81dde0e..332e2dab9e1 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupport.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/DefaultProfileValidationSupport.java @@ -20,6 +20,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.*; +import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.isNotBlank; public class DefaultProfileValidationSupport implements IValidationSupport { @@ -44,15 +45,37 @@ public class DefaultProfileValidationSupport implements IValidationSupport { } CodeSystem system = fetchCodeSystem(theContext, theInclude.getSystem()); - for (ConceptDefinitionComponent next : system.getConcept()) { - if (wantCodes.isEmpty() || wantCodes.contains(next.getCode())) { - retVal.addContains().setSystem(theInclude.getSystem()).setCode(next.getCode()).setDisplay(next.getDisplay()); + if (system != null) { + List concepts = system.getConcept(); + addConcepts(theInclude, retVal, wantCodes, concepts); + } + + for (UriType next: theInclude.getValueSet()) { + ValueSet vs = myValueSets.get(defaultString(next.getValueAsString())); + if (vs != null) { + for (ConceptSetComponent nextInclude : vs.getCompose().getInclude()) { + ValueSetExpansionComponent contents = expandValueSet(theContext, nextInclude); + retVal.getContains().addAll(contents.getContains()); + } } } return retVal; } + private void addConcepts(ConceptSetComponent theInclude, ValueSetExpansionComponent theRetVal, Set theWantCodes, List theConcepts) { + for (ConceptDefinitionComponent next : theConcepts) { + if (theWantCodes.isEmpty() || theWantCodes.contains(next.getCode())) { + theRetVal + .addContains() + .setSystem(theInclude.getSystem()) + .setCode(next.getCode()) + .setDisplay(next.getDisplay()); + } + addConcepts(theInclude, theRetVal, theWantCodes, next.getConcept()); + } + } + @Override public List fetchAllConformanceResources(FhirContext theContext) { ArrayList retVal = new ArrayList<>(); diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/ValidationSupportChain.java index e30d2267e74..c4a87b4f8d5 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/ValidationSupportChain.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/dstu3/hapi/validation/ValidationSupportChain.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.context.FhirContext; import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport; import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.StructureDefinition; +import org.hl7.fhir.dstu3.model.UriType; import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionComponent; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -14,131 +15,140 @@ import java.util.List; import java.util.Set; import static org.apache.commons.lang3.StringUtils.isBlank; +import static org.apache.commons.lang3.StringUtils.isNotBlank; public class ValidationSupportChain implements IValidationSupport { - private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidationSupportChain.class); + private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidationSupportChain.class); - private List myChain; + private List myChain; - /** - * Constructor - */ - public ValidationSupportChain() { - myChain = new ArrayList<>(); - } + /** + * Constructor + */ + public ValidationSupportChain() { + myChain = new ArrayList<>(); + } - /** - * Constructor - */ - public ValidationSupportChain(IValidationSupport... theValidationSupportModules) { - this(); - for (IValidationSupport next : theValidationSupportModules) { - if (next != null) { - myChain.add(next); - } - } - } + /** + * Constructor + */ + public ValidationSupportChain(IValidationSupport... theValidationSupportModules) { + this(); + for (IValidationSupport next : theValidationSupportModules) { + if (next != null) { + myChain.add(next); + } + } + } - public void addValidationSupport(IValidationSupport theValidationSupport) { - myChain.add(theValidationSupport); - } + public void addValidationSupport(IValidationSupport theValidationSupport) { + myChain.add(theValidationSupport); + } - @Override - public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) { - for (IValidationSupport next : myChain) { - if (next.isCodeSystemSupported(theCtx, theInclude.getSystem())) { - return next.expandValueSet(theCtx, theInclude); - } - } - return myChain.get(0).expandValueSet(theCtx, theInclude); - } + @Override + public ValueSetExpansionComponent expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) { + for (IValidationSupport next : myChain) { + if (isNotBlank(theInclude.getSystem())) { + if (next.isCodeSystemSupported(theCtx, theInclude.getSystem())) { + return next.expandValueSet(theCtx, theInclude); + } + } + for (UriType nextValueSet : theInclude.getValueSet()) { + ValueSetExpansionComponent retVal = next.expandValueSet(theCtx, theInclude); + if (retVal != null && retVal.getContains().size() > 0) { + return retVal; + } + } + } + return myChain.get(0).expandValueSet(theCtx, theInclude); + } - @Override - public List fetchAllConformanceResources(FhirContext theContext) { - List retVal = new ArrayList<>(); - for (IValidationSupport next : myChain) { - List candidates = next.fetchAllConformanceResources(theContext); - if (candidates != null) { - retVal.addAll(candidates); - } - } - return retVal; - } + @Override + public List fetchAllConformanceResources(FhirContext theContext) { + List retVal = new ArrayList<>(); + for (IValidationSupport next : myChain) { + List candidates = next.fetchAllConformanceResources(theContext); + if (candidates != null) { + retVal.addAll(candidates); + } + } + return retVal; + } - @Override - public List fetchAllStructureDefinitions(FhirContext theContext) { - ArrayList retVal = new ArrayList(); - Set urls = new HashSet(); - for (IValidationSupport nextSupport : myChain) { - for (StructureDefinition next : nextSupport.fetchAllStructureDefinitions(theContext)) { - if (isBlank(next.getUrl()) || urls.add(next.getUrl())) { - retVal.add(next); - } - } - } - return retVal; - } + @Override + public List fetchAllStructureDefinitions(FhirContext theContext) { + ArrayList retVal = new ArrayList(); + Set urls = new HashSet(); + for (IValidationSupport nextSupport : myChain) { + for (StructureDefinition next : nextSupport.fetchAllStructureDefinitions(theContext)) { + if (isBlank(next.getUrl()) || urls.add(next.getUrl())) { + retVal.add(next); + } + } + } + return retVal; + } - @Override - public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) { - for (IValidationSupport next : myChain) { - CodeSystem retVal = next.fetchCodeSystem(theCtx, theSystem); - if (retVal != null) { - return retVal; - } - } - return null; - } + @Override + public CodeSystem fetchCodeSystem(FhirContext theCtx, String theSystem) { + for (IValidationSupport next : myChain) { + CodeSystem retVal = next.fetchCodeSystem(theCtx, theSystem); + if (retVal != null) { + return retVal; + } + } + return null; + } - @Override - public T fetchResource(FhirContext theContext, Class theClass, String theUri) { - for (IValidationSupport next : myChain) { - T retVal = next.fetchResource(theContext, theClass, theUri); - if (retVal != null) { - return retVal; - } - } - return null; - } + @Override + public T fetchResource(FhirContext theContext, Class theClass, String theUri) { + for (IValidationSupport next : myChain) { + T retVal = next.fetchResource(theContext, theClass, theUri); + if (retVal != null) { + return retVal; + } + } + return null; + } - @Override - public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) { - for (IValidationSupport next : myChain) { - StructureDefinition retVal = next.fetchStructureDefinition(theCtx, theUrl); - if (retVal != null) { - return retVal; - } - } - return null; - } + @Override + public StructureDefinition fetchStructureDefinition(FhirContext theCtx, String theUrl) { + for (IValidationSupport next : myChain) { + StructureDefinition retVal = next.fetchStructureDefinition(theCtx, theUrl); + if (retVal != null) { + return retVal; + } + } + return null; + } - @Override - public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) { - for (IValidationSupport next : myChain) { - if (next.isCodeSystemSupported(theCtx, theSystem)) { - return true; - } - } - return false; - } + @Override + public boolean isCodeSystemSupported(FhirContext theCtx, String theSystem) { + for (IValidationSupport next : myChain) { + if (next.isCodeSystemSupported(theCtx, theSystem)) { + return true; + } + } + return false; + } - @Override - public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) { + @Override + public CodeValidationResult validateCode(FhirContext theCtx, String theCodeSystem, String theCode, String theDisplay) { - ourLog.debug("Validating code {} in chain with {} items", theCode, myChain.size()); + ourLog.debug("Validating code {} in chain with {} items", theCode, myChain.size()); - for (IValidationSupport next : myChain) { - if (next.isCodeSystemSupported(theCtx, theCodeSystem)) { - CodeValidationResult result = next.validateCode(theCtx, theCodeSystem, theCode, theDisplay); - ourLog.debug("Chain item {} returned outcome {}", next, result.isOk()); - return result; - } else { - ourLog.debug("Chain item {} does not support code system {}", next, theCodeSystem); - } - } - return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay); - } + for (IValidationSupport next : myChain) { + if (next.isCodeSystemSupported(theCtx, theCodeSystem)) { + CodeValidationResult result = next.validateCode(theCtx, theCodeSystem, theCode, theDisplay); + ourLog.debug("Chain item {} returned outcome {}", next, result.isOk()); + return result; + } else { + ourLog.debug("Chain item {} does not support code system {}", next, theCodeSystem); + } + } + return myChain.get(0).validateCode(theCtx, theCodeSystem, theCode, theDisplay); + } } From 33ea06b98302f9e73437c009910d3d23ac1f88b8 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Tue, 3 Apr 2018 06:29:14 -0400 Subject: [PATCH 03/19] Fix compile issues --- .../jpa/term/BaseHapiTerminologySvcImpl.java | 12 --- .../ResourceProviderDstu3CodeSystemTest.java | 89 ++++++++----------- 2 files changed, 36 insertions(+), 65 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index 79e90e4ba36..1e9b3eaa10c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -130,18 +130,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } } - private void addConceptsToList(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set theAddedCodes, String theSystem, List theConcept) { - for (CodeSystem.ConceptDefinitionComponent next : theConcept) { - if (!theAddedCodes.contains(next.getCode())) { - theAddedCodes.add(next.getCode()); - ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains(); - contains.setCode(next.getCode()); - contains.setSystem(theSystem); - contains.setDisplay(next.getDisplay()); - } - addConceptsToList(theExpansionComponent, theAddedCodes, theSystem, next.getConcept()); - } - } private void addDisplayFilterExact(QueryBuilder qb, BooleanJunction bool, ValueSet.ConceptSetFilterComponent nextFilter) { bool.must(qb.phrase().onField("myDisplay").sentence(nextFilter.getValue()).createQuery()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java index ded3314ee5a..c535f1fdfb4 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/dstu3/ResourceProviderDstu3CodeSystemTest.java @@ -1,31 +1,19 @@ package ca.uhn.fhir.jpa.provider.dstu3; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.Matchers.stringContainsInOrder; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.fail; - -import java.io.IOException; - -import org.hl7.fhir.dstu3.model.BooleanType; -import org.hl7.fhir.dstu3.model.CodeSystem; -import org.hl7.fhir.dstu3.model.CodeType; -import org.hl7.fhir.dstu3.model.Coding; -import org.hl7.fhir.dstu3.model.Parameters; -import org.hl7.fhir.dstu3.model.StringType; -import org.hl7.fhir.dstu3.model.UriType; -import org.hl7.fhir.dstu3.model.ValueSet; +import ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest; +import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; +import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.util.TestUtil; +import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.instance.model.api.IIdType; import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; import org.springframework.transaction.annotation.Transactional; -import ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoDstu3TerminologyTest; -import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; -import ca.uhn.fhir.util.TestUtil; +import java.io.IOException; + +import static org.junit.Assert.*; public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDstu3Test { @@ -58,34 +46,34 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("SYSTEM NAME"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); + assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals("Parent A", ((StringType)respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue()); // With HTTP GET respParam = ourClient - .operation() - .onType(CodeSystem.class) - .named("lookup") - .withParameter(Parameters.class, "code", new CodeType("ParentA")) - .andParameter("system", new UriType(FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM)) - .useHttpGet() - .execute(); + .operation() + .onType(CodeSystem.class) + .named("lookup") + .withParameter(Parameters.class, "code", new CodeType("ParentA")) + .andParameter("system", new UriType(FhirResourceDaoDstu3TerminologyTest.URL_MY_CODE_SYSTEM)) + .useHttpGet() + .execute(); resp = myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(respParam); ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("SYSTEM NAME"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); + assertEquals(("SYSTEM NAME"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals("Parent A", ((StringType)respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("Parent A", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue()); } - + @Test public void testLookupOperationByCodeAndSystemBuiltInCode() { Parameters respParam = ourClient @@ -100,13 +88,13 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); + assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals("Accession ID", ((StringType)respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("Accession ID", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue()); } - + @Test public void testLookupOperationByCodeAndSystemBuiltInNonexistantCode() { //@formatter:off @@ -141,11 +129,11 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); + assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals(("Systolic blood pressure--expiration"), ((StringType)respParam.getParameter().get(1).getValue()).getValue()); + assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue()); } @Test @@ -181,11 +169,11 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals(("Unknown"), ((StringType)respParam.getParameter().get(0).getValue()).getValue()); + assertEquals(("Unknown"), ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals(("Systolic blood pressure--expiration"), ((StringType)respParam.getParameter().get(1).getValue()).getValue()); + assertEquals(("Systolic blood pressure--expiration"), ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).getValue().booleanValue()); + assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).getValue().booleanValue()); } @Test @@ -259,16 +247,11 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst ourLog.info(resp); assertEquals("name", respParam.getParameter().get(0).getName()); - assertEquals("Unknown", ((StringType)respParam.getParameter().get(0).getValue()).getValue()); + assertEquals("Unknown", ((StringType) respParam.getParameter().get(0).getValue()).getValue()); assertEquals("display", respParam.getParameter().get(1).getName()); - assertEquals("Married", ((StringType)respParam.getParameter().get(1).getValue()).getValue()); + assertEquals("Married", ((StringType) respParam.getParameter().get(1).getValue()).getValue()); assertEquals("abstract", respParam.getParameter().get(2).getName()); - assertEquals(false, ((BooleanType)respParam.getParameter().get(2).getValue()).booleanValue()); - } - - @AfterClass - public static void afterClassClearContext() { - TestUtil.clearAllStaticFieldsForUnitTest(); + assertEquals(false, ((BooleanType) respParam.getParameter().get(2).getValue()).booleanValue()); } @AfterClass @@ -276,5 +259,5 @@ public class ResourceProviderDstu3CodeSystemTest extends BaseResourceProviderDst TestUtil.clearAllStaticFieldsForUnitTest(); } - + } From b8b3ff8a8f18a6a3e1d77ba179171eb87c953342 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Wed, 4 Apr 2018 09:34:16 -0400 Subject: [PATCH 04/19] Loinc property updates --- .../uhn/fhir/context/support/IContextValidationSupport.java | 4 ++-- .../java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java | 4 ++-- .../src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java | 4 ++-- .../src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java | 4 ++-- .../src/main/java/ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java | 4 ++-- .../src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java | 4 ++-- .../java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java | 4 ++-- .../ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java | 4 ++-- .../ca/uhn/fhir/jpa/dao/data/ITermConceptDesignationDao.java | 4 ++-- .../ca/uhn/fhir/jpa/dao/data/ITermConceptPropertyDao.java | 4 ++-- .../fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java | 4 ++-- .../src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java | 4 ++-- .../java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java | 4 ++-- .../src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java | 4 ++-- .../java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java | 4 ++-- .../main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java | 4 ++-- .../uhn/fhir/jpa/entity/TermConceptPropertyFieldBridge.java | 4 ++-- .../ca/uhn/fhir/jpa/entity/TermConceptPropertyTypeEnum.java | 4 ++-- .../fhir/jpa/provider/BaseTerminologyUploaderProvider.java | 4 ++-- .../java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java | 4 ++-- .../java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu2.java | 4 ++-- .../java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java | 4 ++-- .../main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java | 4 ++-- .../java/ca/uhn/fhir/jpa/term/IHapiTerminologyLoaderSvc.java | 4 ++-- .../main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java | 4 ++-- .../main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvcR4.java | 4 ++-- .../src/main/java/ca/uhn/fhir/jpa/term/IRecordHandler.java | 4 ++-- .../java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java | 4 ++-- .../src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java | 4 ++-- .../jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java | 4 ++-- .../ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java | 4 ++-- .../uhn/fhir/jpa/term/loinc/LoincAnswerListLinkHandler.java | 4 ++-- .../uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java | 4 ++-- .../main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java | 4 ++-- .../ca/uhn/fhir/jpa/term/loinc/LoincHierarchyHandler.java | 4 ++-- .../jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java | 4 ++-- .../fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java | 4 ++-- .../java/ca/uhn/fhir/jpa/term/loinc/LoincPartHandler.java | 4 ++-- .../java/ca/uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java | 4 ++-- .../jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java | 4 ++-- .../ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java | 4 ++-- .../fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java | 4 ++-- .../fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java | 4 ++-- .../fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java | 4 ++-- .../java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerConcept.java | 4 ++-- .../ca/uhn/fhir/jpa/term/snomedct/SctHandlerDescription.java | 4 ++-- .../ca/uhn/fhir/jpa/term/snomedct/SctHandlerRelationship.java | 4 ++-- 47 files changed, 94 insertions(+), 94 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IContextValidationSupport.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IContextValidationSupport.java index bcebc8bbbda..19a79b37b82 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IContextValidationSupport.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/IContextValidationSupport.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.context.support; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java index 945e8227379..807512adef5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/dstu3/BaseDstu3Config.java @@ -35,9 +35,9 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java index c9ff4f94920..723fd56e949 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/config/r4/BaseR4Config.java @@ -38,9 +38,9 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java index 5ae96597123..930329fffb5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/BaseHapiFhirDao.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java index a1a06ad597d..e9809f98873 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/DaoMethodOutcome.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java index a037de2706e..b6a5e7e8de9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java index 1a2b12b3210..ca5fc4d5231 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java @@ -21,9 +21,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java index 9814a199364..05d4e147704 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermCodeSystemVersionDao.java @@ -18,9 +18,9 @@ import java.util.List; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDesignationDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDesignationDao.java index ad7ca7265d4..4be607a9390 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDesignationDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptDesignationDao.java @@ -12,9 +12,9 @@ import org.springframework.data.jpa.repository.JpaRepository; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptPropertyDao.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptPropertyDao.java index c9f8ed09029..d0906cef88f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptPropertyDao.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/data/ITermConceptPropertyDao.java @@ -21,9 +21,9 @@ import java.util.List; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java index 55fc51cec67..8bc6722ee72 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoCodeSystemDstu3.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao.dstu3; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java index bba63698fd1..04e42e6d6e4 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.dao.r4; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java index ea021ed1820..f5d0e9c4b5f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermCodeSystemVersion.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.entity; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java index ff799c97dc1..96f2efb1109 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConcept.java @@ -28,9 +28,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java index 51b64484155..ae6d5d16910 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.entity; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java index b3a5bbbe2c3..852141838eb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.entity; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyFieldBridge.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyFieldBridge.java index d10a1ac9734..917dd88268d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyFieldBridge.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyFieldBridge.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.entity; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyTypeEnum.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyTypeEnum.java index 49062476c44..b83a3b158d0 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyTypeEnum.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyTypeEnum.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.entity; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseTerminologyUploaderProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseTerminologyUploaderProvider.java index e4318e8ed7e..0667cb44889 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseTerminologyUploaderProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/BaseTerminologyUploaderProvider.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.provider; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index 1e9b3eaa10c..4b48843c246 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu2.java index b26aa5e9a28..ce5a4d4f80b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu2.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java index 5087fc312b5..67b8c86dcb1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java @@ -40,9 +40,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java index 41182ebcfa5..5800b10fa76 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java @@ -39,9 +39,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologyLoaderSvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologyLoaderSvc.java index 7d025b66755..de2f12d6813 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologyLoaderSvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologyLoaderSvc.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java index 26f9e47c6e0..c9ec2679a97 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvc.java @@ -18,9 +18,9 @@ import java.util.Set; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvcR4.java index 818d758126f..a0c6d5b40de 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvcR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IHapiTerminologySvcR4.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IRecordHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IRecordHandler.java index 35e0b65f34b..c0c811153d8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IRecordHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/IRecordHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java index c0112139d1b..c2d9673ae95 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java @@ -46,9 +46,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java index 9dbc98dc6c7..31af0aeff9b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java index 8e431107827..3a937e3deb6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java index 29a174827c2..a7772664ba6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListLinkHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListLinkHandler.java index f16ed0cbed8..322976d40c9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListLinkHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListLinkHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java index 09ff81684de..fa3d23b1341 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java index be3597489f9..62f971d8cb6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHierarchyHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHierarchyHandler.java index 02373716541..dc53a641c2a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHierarchyHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHierarchyHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java index a15bc66332c..db42e7eff6b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java index ffae5fa14c7..d6fb64d19bc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartHandler.java index bfcd1aea80d..0b1600aa143 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java index 5ec6963bd84..2aaf41a297b 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java index 627048108f7..c2d48d7eae6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java index b2b7ba74631..ff5b85e02d5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java index 8930694a490..33678c1c402 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java index 1068349bf1b..d5f65073d1a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java index 71439449f02..cce69e825fe 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerConcept.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerConcept.java index c1fbdb30aa9..da4cd9255f9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerConcept.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerConcept.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.snomedct; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerDescription.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerDescription.java index 2aa2ebc8fd8..66699d48cb8 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerDescription.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerDescription.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.snomedct; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerRelationship.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerRelationship.java index cc58cecba43..736e7161c25 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerRelationship.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/snomedct/SctHandlerRelationship.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.snomedct; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. From 22bec81bffd3e0f1db52081d9a9e20bed4e60864 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Wed, 4 Apr 2018 10:11:19 -0400 Subject: [PATCH 05/19] Fix an intermittent test failure --- .../dstu3/FhirResourceDaoDstu3UniqueSearchParamTest.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3UniqueSearchParamTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3UniqueSearchParamTest.java index 375771e53fc..be99f609aa3 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3UniqueSearchParamTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/dao/dstu3/FhirResourceDaoDstu3UniqueSearchParamTest.java @@ -18,6 +18,7 @@ import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; +import org.springframework.test.context.TestPropertySource; import java.util.Collections; import java.util.List; @@ -27,6 +28,11 @@ import static org.hamcrest.Matchers.empty; import static org.junit.Assert.*; @SuppressWarnings({"unchecked", "deprecation"}) +@TestPropertySource(properties = { + // Since scheduled tasks can cause searches, which messes up the + // value returned by SearchBuilder.getLastHandlerMechanismForUnitTest() + "scheduling_disabled=true" +}) public class FhirResourceDaoDstu3UniqueSearchParamTest extends BaseJpaDstu3Test { private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3UniqueSearchParamTest.class); From 2c818d5b4a12fae98a89b82ea722d3e807f746a8 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Fri, 6 Apr 2018 09:12:22 -0400 Subject: [PATCH 06/19] Update handler names --- .../fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java | 4 ++-- .../fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java | 4 ++-- pom.xml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java index 33678c1c402..91c7e959491 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java @@ -29,8 +29,8 @@ import java.util.Map; public class LoincTop2000LabResultsSiHandler extends BaseLoincTop2000LabResultsHandler { - public static final String TOP_2000_SI_VS_ID = "TOP-2000-LABRESULTS-SI"; - public static final String TOP_2000_SI_VS_URI = "http://loinc.org/top-2000-lab-results-si"; + public static final String TOP_2000_SI_VS_ID = "top-2000-lab-observations-si"; + public static final String TOP_2000_SI_VS_URI = "http://loinc.org/vs/top-2000-lab-observations-si"; public static final String TOP_2000_SI_VS_NAME = "Top 2000 Lab Results SI"; public LoincTop2000LabResultsSiHandler(Map theCode2concept, List theValueSets, List theConceptMaps) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java index d5f65073d1a..64f11b92303 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java @@ -29,8 +29,8 @@ import java.util.Map; public class LoincTop2000LabResultsUsHandler extends BaseLoincTop2000LabResultsHandler { - public static final String TOP_2000_US_VS_ID = "TOP-2000-LABRESULTS-US"; - public static final String TOP_2000_US_VS_URI = "http://loinc.org/top-2000-lab-results-us"; + public static final String TOP_2000_US_VS_ID = "top-2000-lab-observations-us"; + public static final String TOP_2000_US_VS_URI = "http://loinc.org/vs/top-2000-lab-observations-us"; public static final String TOP_2000_US_VS_NAME = "Top 2000 Lab Results US"; public LoincTop2000LabResultsUsHandler(Map theCode2concept, List theValueSets, List theConceptMaps) { diff --git a/pom.xml b/pom.xml index 6064b0c4ba5..2b6f569bed9 100644 --- a/pom.xml +++ b/pom.xml @@ -998,7 +998,7 @@ org.javassist javassist - 3.20.0-GA + 3.22.0-GA org.mockito From 832558b411599d1d05ed65481577cf53065b439c Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 6 Apr 2018 09:12:52 -0400 Subject: [PATCH 07/19] Updates to handler --- .../java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java | 2 +- src/changes/changes.xml | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java index be3597489f9..664f807cfb7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java @@ -74,7 +74,7 @@ public class LoincHandler implements IRecordHandler { } } - Validate.isTrue(!myCode2Concept.containsKey(code)); + Validate.isTrue(!myCode2Concept.containsKey(code), "The code {0} has appeared more than once", code); myCode2Concept.put(code, concept); } } diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 1e41560b15d..e9db9158f81 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -7,6 +7,15 @@ + + The version of a few dependencies have been bumped to the + latest versions (dependent HAPI modules listed in brackets): + +
  • Javassist (JPA): 3.20.0-GA -> 3.22.0-GA
  • + + ]]> +
    When performing a FHIR resource update in the JPA server where the update happens within a transaction, and the From d624556e182624831e9c043b7d5e58e243385443 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 6 Apr 2018 09:50:18 -0400 Subject: [PATCH 08/19] Some updates to the loinc mapper --- .../uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java | 10 +++++++++- .../term/loinc/LoincPartRelatedCodeMappingHandler.java | 2 +- pom.xml | 2 +- src/changes/changes.xml | 1 + 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java index 2aaf41a297b..4b6b63bf594 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartLinkHandler.java @@ -36,6 +36,7 @@ public class LoincPartLinkHandler implements IRecordHandler { private static final Logger ourLog = LoggerFactory.getLogger(LoincPartLinkHandler.class); private final Map myCode2Concept; private final TermCodeSystemVersion myCodeSystemVersion; + private Long myPartCount; public LoincPartLinkHandler(TermCodeSystemVersion theCodeSystemVersion, Map theCode2concept) { myCodeSystemVersion = theCodeSystemVersion; @@ -57,7 +58,14 @@ public class LoincPartLinkHandler implements IRecordHandler { return; } if (partConcept == null) { - ourLog.warn("No part code: {}", partNumber); + if (myPartCount == null) { + myPartCount = myCode2Concept + .keySet() + .stream() + .filter(t->t.startsWith("LP")) + .count(); + } + ourLog.debug("No part code: {} - Have {} part codes", partNumber, myPartCount); return; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java index c2d48d7eae6..08a38bc88c5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java @@ -81,7 +81,7 @@ public class LoincPartRelatedCodeMappingHandler extends BaseHandler implements I equivalence = Enumerations.ConceptMapEquivalence.WIDER; break; default: - throw new InternalErrorException("Unknown MapType: " + mapType); + throw new InternalErrorException("Unknown MapType '" + mapType + "' for PartNumber: " + partNumber); } addConceptMapEntry( diff --git a/pom.xml b/pom.xml index 2b6f569bed9..4e90c4737d8 100644 --- a/pom.xml +++ b/pom.xml @@ -451,7 +451,7 @@ 9.4.8.v20171121 3.0.2 - 5.2.12.Final + 5.2.16.Final 5.4.1.Final 5.7.1.Final diff --git a/src/changes/changes.xml b/src/changes/changes.xml index e9db9158f81..387de638fb3 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -12,6 +12,7 @@ latest versions (dependent HAPI modules listed in brackets): +
  • Hibernate (JPA): 5.2.12.Final -> 5.2.16.Final
  • Javassist (JPA): 3.20.0-GA -> 3.22.0-GA
  • ]]> From 733480068446708843457de929d7d4668147b0fb Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Fri, 6 Apr 2018 09:50:41 -0400 Subject: [PATCH 09/19] Better logging --- .../src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java index f7325accbff..bbdf5583efa 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java @@ -74,7 +74,7 @@ public class LoincHandler implements IRecordHandler { } } - Validate.isTrue(!myCode2Concept.containsKey(code), "The code {0} has appeared more than once", code); + Validate.isTrue(!myCode2Concept.containsKey(code), "The code %s has appeared more than once", code); myCode2Concept.put(code, concept); } } From 2f4bcfe5d633ed727bfb4be81927bfb102e556eb Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 6 Apr 2018 11:25:03 -0400 Subject: [PATCH 10/19] Understand equal mappings --- .../jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java index 08a38bc88c5..2988328a4c6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java @@ -33,6 +33,7 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.List; import java.util.Map; +import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.trim; public class LoincPartRelatedCodeMappingHandler extends BaseHandler implements IRecordHandler { @@ -69,7 +70,8 @@ public class LoincPartRelatedCodeMappingHandler extends BaseHandler implements I String extCodeSystemCopyrightNotice = trim(theRecord.get("ExtCodeSystemCopyrightNotice")); Enumerations.ConceptMapEquivalence equivalence; - switch (mapType) { + switch (trim(defaultString(mapType))) { + case "": case "Exact": // 'equal' is more exact than 'equivalent' in the equivalence codes equivalence = Enumerations.ConceptMapEquivalence.EQUAL; From 3eab3c5fabf728c8112f1513e2a4edfa197318a7 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 6 Apr 2018 12:23:10 -0400 Subject: [PATCH 11/19] More mapping for loinc RADS --- .../term/loinc/LoincRsnaPlaybookHandler.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java index ff5b85e02d5..a9555fd78b5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java @@ -126,6 +126,51 @@ public class LoincRsnaPlaybookHandler extends BaseHandler implements IRecordHand case "Rad.Modality.Modality type": loincCodePropName = "rad-modality-modality-type"; break; + case "Rad.Modality.Modality subtype": + loincCodePropName = "rad-modality-modality-subtype"; + break; + case "Rad.Anatomic Location.Laterality": + loincCodePropName = "rad-anatomic-location-laterality"; + break; + case "Rad.Anatomic Location.Laterality.Presence": + loincCodePropName = "rad-anatomic-location-laterality-presence"; + break; + case "Rad.Guidance for.Action": + loincCodePropName = "rad-guidance-for-action"; + break; + case "Rad.Guidance for.Approach": + loincCodePropName = "rad-guidance-for-approach"; + break; + case "Rad.Guidance for.Object": + loincCodePropName = "rad-guidance-for-object"; + break; + case "Rad.Guidance for.Presence": + loincCodePropName = "rad-guidance-for-presence"; + break; + case "Rad.Maneuver.Maneuver type": + loincCodePropName = "rad-maneuver-maneuver-type"; + break; + case "Rad.Pharmaceutical.Route": + loincCodePropName = "rad-pharmaceutical-route"; + break; + case "Rad.Pharmaceutical.Substance Given": + loincCodePropName = "rad-pharmaceutical-substance-given"; + break; + case "Rad.Reason for Exam": + loincCodePropName = "rad-reason-for-exam"; + break; + case "Rad.Subject": + loincCodePropName = "rad-subject"; + break; + case "Rad.Timing": + loincCodePropName = "rad-timing"; + break; + case "Rad.View.Aggregation": + loincCodePropName = "rad-view-view-aggregation"; + break; + case "Rad.View.View type": + loincCodePropName = "rad-view-view-type"; + break; default: throw new InternalErrorException("Unknown PartTypeName: " + partTypeName); } From 0f269a1ab118bc1de034c3bdda3b98a065c8c7f5 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 6 Apr 2018 14:23:41 -0400 Subject: [PATCH 12/19] Correctly save deferred concepts --- .../jpa/term/BaseHapiTerminologySvcImpl.java | 16 +++--- .../jpa/term/TerminologySvcImplDstu3Test.java | 51 +++++++++++++++++++ 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index 4b48843c246..210b8207ff5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -528,14 +528,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } } - for (TermConceptProperty next : theConcept.getProperties()) { - myConceptPropertyDao.save(next); - } - - for (TermConceptDesignation next : theConcept.getDesignations()) { - myConceptDesignationDao.save(next); - } - } private void populateVersion(TermConcept theNext, TermCodeSystemVersion theCodeSystemVersion) { @@ -681,6 +673,14 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc retVal++; theConcept.setIndexStatus(BaseHapiFhirDao.INDEX_STATUS_INDEXED); myConceptDao.save(theConcept); + + for (TermConceptProperty next : theConcept.getProperties()) { + myConceptPropertyDao.save(next); + } + + for (TermConceptDesignation next : theConcept.getDesignations()) { + myConceptDesignationDao.save(next); + } } ourLog.trace("Saved {} and got PID {}", theConcept.getCode(), theConcept.getId()); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java index 0fe0e96b7d6..cf6154b7532 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.term; +import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test; import ca.uhn.fhir.jpa.entity.ResourceTable; @@ -13,6 +14,7 @@ import org.hl7.fhir.dstu3.model.CodeSystem; import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.ValueSet; +import org.junit.After; import org.junit.AfterClass; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -42,6 +44,14 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { TermCodeSystemVersion cs = new TermCodeSystemVersion(); cs.setResource(table); + TermConcept parent; + parent = new TermConcept(cs, "ParentWithNoChildrenA"); + cs.getConcepts().add(parent); + parent = new TermConcept(cs, "ParentWithNoChildrenB"); + cs.getConcepts().add(parent); + parent = new TermConcept(cs, "ParentWithNoChildrenC"); + cs.getConcepts().add(parent); + TermConcept parentA = new TermConcept(cs, "ParentA"); cs.getConcepts().add(parentA); @@ -223,6 +233,47 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { assertEquals("D1V", concept.getDesignation().get(0).getValue()); } + @After + public void after() { + myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize()); + BaseHapiTerminologySvcImpl.setForceSaveDeferredAlwaysForUnitTest(false); + } + + + @Test + public void testCreatePropertiesAndDesignationsWithDeferredConcepts() { + myDaoConfig.setDeferIndexingForCodesystemsOfSize(1); + BaseHapiTerminologySvcImpl.setForceSaveDeferredAlwaysForUnitTest(true); + + createCodeSystem(); + + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + myTermSvc.saveDeferred(); + + ValueSet vs = new ValueSet(); + ValueSet.ConceptSetComponent include = vs.getCompose().addInclude(); + include.setSystem(CS_URL); + include.addConcept().setCode("childAAB"); + ValueSet outcome = myTermSvc.expandValueSet(vs); + + List codes = toCodesContains(outcome.getExpansion().getContains()); + assertThat(codes, containsInAnyOrder("childAAB")); + + ValueSet.ValueSetExpansionContainsComponent concept = outcome.getExpansion().getContains().get(0); + assertEquals("childAAB", concept.getCode()); + assertEquals("http://example.com/my_code_system", concept.getSystem()); + assertEquals(null, concept.getDisplay()); + assertEquals("D1S", concept.getDesignation().get(0).getUse().getSystem()); + assertEquals("D1C", concept.getDesignation().get(0).getUse().getCode()); + assertEquals("D1D", concept.getDesignation().get(0).getUse().getDisplay()); + assertEquals("D1V", concept.getDesignation().get(0).getValue()); + } + + @Test public void testFindCodesAbove() { IIdType id = createCodeSystem(); From 22b8e05b1ffeb389d6786994ee41873ba10a3695 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 6 Apr 2018 16:14:59 -0400 Subject: [PATCH 13/19] Add some logging --- .../java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index 210b8207ff5..f497f0612e7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -836,10 +836,12 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemResource.getName(), theCodeSystemVersion); for (ValueSet nextValueSet : theValueSets) { + ourLog.info("Creating ValueSet: {}", nextValueSet.getId()); createOrUpdateValueSet(nextValueSet, theRequestDetails); } for (ConceptMap nextConceptMap : theConceptMaps) { + ourLog.info("Creating ConceptMap: {}", nextConceptMap.getId()); createOrUpdateConceptMap(nextConceptMap, theRequestDetails); } From 7d6582fe7346c3253fef230bf755da066468fd66 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 6 Apr 2018 16:41:28 -0400 Subject: [PATCH 14/19] More updates to loinc uploading --- .../jpa/term/HapiTerminologySvcDstu3.java | 26 +++++++++++++---- .../fhir/jpa/term/HapiTerminologySvcR4.java | 29 ++++++++++++++----- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java index 67b8c86dcb1..46b2e809487 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; /* @@ -104,38 +105,51 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen @Override protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { - String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); CodeSystem resourceToStore; try { resourceToStore = VersionConvertor_30_40.convertCodeSystem(theCodeSystemResource); } catch (FHIRException e) { throw new InternalErrorException(e); } - return myCodeSystemResourceDao.update(resourceToStore, matchUrl, theRequestDetails).getId(); + if (isBlank(resourceToStore.getIdElement().getIdPart())) { + String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); + return myCodeSystemResourceDao.update(resourceToStore, matchUrl, theRequestDetails).getId(); + } else { + return myCodeSystemResourceDao.update(resourceToStore, theRequestDetails).getId(); + } } @Override protected void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap, RequestDetails theRequestDetails) { - String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl()); ConceptMap resourceToStore; try { resourceToStore = VersionConvertor_30_40.convertConceptMap(theConceptMap); } catch (FHIRException e) { throw new InternalErrorException(e); } - myConceptMapResourceDao.update(resourceToStore, matchUrl, theRequestDetails).getId(); + if (isBlank(resourceToStore.getIdElement().getIdPart())) { + String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl()); + myConceptMapResourceDao.update(resourceToStore, matchUrl, theRequestDetails).getId(); + } else { + myConceptMapResourceDao.update(resourceToStore, theRequestDetails).getId(); + } } @Override protected void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet, RequestDetails theRequestDetails) { - String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl()); ValueSet valueSetDstu3; try { valueSetDstu3 = VersionConvertor_30_40.convertValueSet(theValueSet); } catch (FHIRException e) { throw new InternalErrorException(e); } - myValueSetResourceDao.update(valueSetDstu3, matchUrl, theRequestDetails); + + if (isBlank(valueSetDstu3.getIdElement().getIdPart())) { + String matchUrl = "ValueSet?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl()); + myValueSetResourceDao.update(valueSetDstu3, matchUrl, theRequestDetails); + } else { + myValueSetResourceDao.update(valueSetDstu3, theRequestDetails); + } } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java index 5800b10fa76..301244b96d7 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; /* @@ -95,21 +96,33 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements } @Override - protected IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { - String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); - return myCodeSystemResourceDao.update(theCodeSystemResource, matchUrl, theRequestDetails).getId(); + protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { + if (isBlank(theCodeSystemResource.getIdElement().getIdPart())) { + String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); + return myCodeSystemResourceDao.update(theCodeSystemResource, matchUrl, theRequestDetails).getId(); + } else { + return myCodeSystemResourceDao.update(theCodeSystemResource, theRequestDetails).getId(); + } } @Override protected void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap, RequestDetails theRequestDetails) { - String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl()); - myConceptMapResourceDao.update(theConceptMap, matchUrl, theRequestDetails); + if (isBlank(theConceptMap.getIdElement().getIdPart())) { + String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl()); + myConceptMapResourceDao.update(theConceptMap, matchUrl, theRequestDetails).getId(); + } else { + myConceptMapResourceDao.update(theConceptMap, theRequestDetails).getId(); + } } @Override - protected void createOrUpdateValueSet(ValueSet theValueSet, RequestDetails theRequestDetails) { - String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl()); - myValueSetResourceDao.update(theValueSet, matchUrl, theRequestDetails); + protected void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet, RequestDetails theRequestDetails) { + if (isBlank(theValueSet.getIdElement().getIdPart())) { + String matchUrl = "ValueSet?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl()); + myValueSetResourceDao.update(theValueSet, matchUrl, theRequestDetails); + } else { + myValueSetResourceDao.update(theValueSet, theRequestDetails); + } } @Override From f013448917cfdf9d2e5bc566f8a14207bfc47aa0 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 6 Apr 2018 17:06:34 -0400 Subject: [PATCH 15/19] More work on updating term services --- .../jpa/term/BaseHapiTerminologySvcImpl.java | 96 +++++++++++-------- .../jpa/term/HapiTerminologySvcDstu2.java | 7 +- .../jpa/term/HapiTerminologySvcDstu3.java | 19 ++-- .../fhir/jpa/term/HapiTerminologySvcR4.java | 19 ++-- 4 files changed, 78 insertions(+), 63 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index f497f0612e7..820e368af17 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -89,7 +89,9 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc private List myConceptLinksToSaveLater = new ArrayList<>(); @Autowired private ITermConceptParentChildLinkDao myConceptParentChildLinkDao; - private List myConceptsToSaveLater = new ArrayList<>(); + private List myDeferredConcepts = Collections.synchronizedList(new ArrayList<>()); + private List myDeferredValueSets = Collections.synchronizedList(new ArrayList<>()); + private List myDeferredConceptMaps = Collections.synchronizedList(new ArrayList<>()); @Autowired private DaoConfig myDaoConfig; private long myNextReindexPass; @@ -130,7 +132,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } } - private void addDisplayFilterExact(QueryBuilder qb, BooleanJunction bool, ValueSet.ConceptSetFilterComponent nextFilter) { bool.must(qb.phrase().onField("myDisplay").sentence(nextFilter.getValue()).createQuery()); } @@ -158,11 +159,11 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc return retVal; } - protected abstract IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource, RequestDetails theRequestDetails); + protected abstract IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource); - protected abstract void createOrUpdateConceptMap(ConceptMap theNextConceptMap, RequestDetails theRequestDetails); + protected abstract void createOrUpdateConceptMap(ConceptMap theNextConceptMap); - abstract void createOrUpdateValueSet(ValueSet theValueSet, RequestDetails theRequestDetails); + abstract void createOrUpdateValueSet(ValueSet theValueSet); @Override public void deleteCodeSystem(TermCodeSystem theCodeSystem) { @@ -188,7 +189,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } myCodeSystemVersionDao.delete(next); - if (i % 1000 == 0) { + if (i++ % 1000 == 0) { myEntityManager.flush(); } } @@ -243,12 +244,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc TermConcept code = findCode(system, nextCode); if (code != null) { addCodeIfNotAlreadyAdded(system, expansionComponent, addedCodes, code); -// -// addedCodes.add(nextCode); -// ValueSet.ValueSetExpansionContainsComponent contains = expansionComponent.addContains(); -// contains.setCode(nextCode); -// contains.setSystem(system); -// contains.setDisplay(code.getDisplay()); } } } @@ -513,7 +508,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc if (theConceptsStack.size() <= myDaoConfig.getDeferIndexingForCodesystemsOfSize()) { saveConcept(theConcept); } else { - myConceptsToSaveLater.add(theConcept); + myDeferredConcepts.add(theConcept); } for (TermConceptParentChildLink next : theConcept.getChildren()) { @@ -544,16 +539,16 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc int codeCount = 0, relCount = 0; StopWatch stopwatch = new StopWatch(); - int count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myConceptsToSaveLater.size()); + int count = Math.min(myDaoConfig.getDeferIndexingForCodesystemsOfSize(), myDeferredConcepts.size()); ourLog.info("Saving {} deferred concepts...", count); - while (codeCount < count && myConceptsToSaveLater.size() > 0) { - TermConcept next = myConceptsToSaveLater.remove(0); + while (codeCount < count && myDeferredConcepts.size() > 0) { + TermConcept next = myDeferredConcepts.remove(0); codeCount += saveConcept(next); } if (codeCount > 0) { ourLog.info("Saved {} deferred concepts ({} codes remain and {} relationships remain) in {}ms ({}ms / code)", - new Object[] {codeCount, myConceptsToSaveLater.size(), myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)}); + new Object[] {codeCount, myDeferredConcepts.size(), myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)}); } if (codeCount == 0) { @@ -577,7 +572,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc new Object[] {relCount, myConceptLinksToSaveLater.size(), stopwatch.getMillis(), stopwatch.getMillisPerOperation(codeCount)}); } - if ((myConceptsToSaveLater.size() + myConceptLinksToSaveLater.size()) == 0) { + if ((myDeferredConcepts.size() + myConceptLinksToSaveLater.size()) == 0) { ourLog.info("All deferred concepts and relationships have now been synchronized to the database"); } } @@ -699,20 +694,51 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc public synchronized void saveDeferred() { if (!myProcessDeferred) { return; - } else if (myConceptsToSaveLater.isEmpty() && myConceptLinksToSaveLater.isEmpty()) { + } else if (myDeferredConcepts.isEmpty() && myConceptLinksToSaveLater.isEmpty()) { processReindexing(); return; } TransactionTemplate tt = new TransactionTemplate(myTransactionMgr); tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW); - tt.execute(new TransactionCallbackWithoutResult() { - @Override - protected void doInTransactionWithoutResult(TransactionStatus theArg0) { + tt.execute(t->{ processDeferredConcepts(); - } - + return null; }); + + if (myDeferredValueSets.size() > 0) { + tt.execute(t -> { + processDeferredValueSets(); + return null; + }); + } + if (myDeferredConceptMaps.size() > 0) { + tt.execute(t -> { + processDeferredConceptMaps(); + return null; + }); + } + + } + + private void processDeferredValueSets() { + int count = Math.min(myDeferredValueSets.size(), 5); + for (ValueSet nextValueSet : myDeferredValueSets.subList(0, count)) { + ourLog.info("Creating ValueSet: {}", nextValueSet.getId()); + createOrUpdateValueSet(nextValueSet); + myDeferredValueSets.remove(nextValueSet); + } + ourLog.info("Saved {} deferred ValueSet resources, have {} remaining", count, myDeferredConceptMaps.size()); + } + + private void processDeferredConceptMaps() { + int count = Math.min(myDeferredConceptMaps.size(), 5); + for (ConceptMap nextConceptMap : myDeferredConceptMaps.subList(0, count)) { + ourLog.info("Creating ConceptMap: {}", nextConceptMap.getId()); + createOrUpdateConceptMap(nextConceptMap); + myDeferredConceptMaps.remove(nextConceptMap); + } + ourLog.info("Saved {} deferred ConceptMap resources, have {} remaining", count, myDeferredConceptMaps.size()); } @Override @@ -815,8 +841,8 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc ourLog.info("Done deleting old code system versions"); - 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()); + if (myDeferredConcepts.size() > 0 || myConceptLinksToSaveLater.size() > 0) { + ourLog.info("Note that some concept saving was deferred - still have {} concepts and {} relationships", myDeferredConcepts.size(), myConceptLinksToSaveLater.size()); } } @@ -825,7 +851,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc public void storeNewCodeSystemVersion(CodeSystem theCodeSystemResource, TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails, List theValueSets, List theConceptMaps) { Validate.notBlank(theCodeSystemResource.getUrl(), "theCodeSystemResource must have a URL"); - IIdType csId = createOrUpdateCodeSystem(theCodeSystemResource, theRequestDetails); + IIdType csId = createOrUpdateCodeSystem(theCodeSystemResource); ResourceTable resource = (ResourceTable) myCodeSystemResourceDao.readEntity(csId); Long codeSystemResourcePid = resource.getId(); @@ -835,16 +861,8 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc theCodeSystemVersion.setResource(resource); storeNewCodeSystemVersion(codeSystemResourcePid, theCodeSystemResource.getUrl(), theCodeSystemResource.getName(), theCodeSystemVersion); - for (ValueSet nextValueSet : theValueSets) { - ourLog.info("Creating ValueSet: {}", nextValueSet.getId()); - createOrUpdateValueSet(nextValueSet, theRequestDetails); - } - - for (ConceptMap nextConceptMap : theConceptMaps) { - ourLog.info("Creating ConceptMap: {}", nextConceptMap.getId()); - createOrUpdateConceptMap(nextConceptMap, theRequestDetails); - } - + myDeferredConceptMaps.addAll(theConceptMaps); + myDeferredValueSets.addAll(theValueSets); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu2.java index ce5a4d4f80b..8d4a6e056e5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu2.java @@ -20,7 +20,6 @@ package ca.uhn.fhir.jpa.term; * #L% */ -import ca.uhn.fhir.rest.api.server.RequestDetails; import org.hl7.fhir.instance.hapi.validation.IValidationSupport; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.CodeSystem; @@ -62,17 +61,17 @@ public class HapiTerminologySvcDstu2 extends BaseHapiTerminologySvcImpl { } @Override - protected IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { + protected IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource) { throw new UnsupportedOperationException(); } @Override - protected void createOrUpdateConceptMap(ConceptMap theNextConceptMap, RequestDetails theRequestDetails) { + protected void createOrUpdateConceptMap(ConceptMap theNextConceptMap) { throw new UnsupportedOperationException(); } @Override - protected void createOrUpdateValueSet(ValueSet theValueSet, RequestDetails theRequestDetails) { + protected void createOrUpdateValueSet(ValueSet theValueSet) { throw new UnsupportedOperationException(); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java index 46b2e809487..d19a864aa2f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcDstu3.java @@ -5,7 +5,6 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; import ca.uhn.fhir.jpa.entity.TermConcept; -import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.UrlUtil; @@ -104,7 +103,7 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen } @Override - protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { + protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) { CodeSystem resourceToStore; try { resourceToStore = VersionConvertor_30_40.convertCodeSystem(theCodeSystemResource); @@ -113,14 +112,14 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen } if (isBlank(resourceToStore.getIdElement().getIdPart())) { String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); - return myCodeSystemResourceDao.update(resourceToStore, matchUrl, theRequestDetails).getId(); + return myCodeSystemResourceDao.update(resourceToStore, matchUrl).getId(); } else { - return myCodeSystemResourceDao.update(resourceToStore, theRequestDetails).getId(); + return myCodeSystemResourceDao.update(resourceToStore).getId(); } } @Override - protected void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap, RequestDetails theRequestDetails) { + protected void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap) { ConceptMap resourceToStore; try { resourceToStore = VersionConvertor_30_40.convertConceptMap(theConceptMap); @@ -129,14 +128,14 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen } if (isBlank(resourceToStore.getIdElement().getIdPart())) { String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl()); - myConceptMapResourceDao.update(resourceToStore, matchUrl, theRequestDetails).getId(); + myConceptMapResourceDao.update(resourceToStore, matchUrl); } else { - myConceptMapResourceDao.update(resourceToStore, theRequestDetails).getId(); + myConceptMapResourceDao.update(resourceToStore); } } @Override - protected void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet, RequestDetails theRequestDetails) { + protected void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet) { ValueSet valueSetDstu3; try { valueSetDstu3 = VersionConvertor_30_40.convertValueSet(theValueSet); @@ -146,9 +145,9 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen if (isBlank(valueSetDstu3.getIdElement().getIdPart())) { String matchUrl = "ValueSet?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl()); - myValueSetResourceDao.update(valueSetDstu3, matchUrl, theRequestDetails); + myValueSetResourceDao.update(valueSetDstu3, matchUrl); } else { - myValueSetResourceDao.update(valueSetDstu3, theRequestDetails); + myValueSetResourceDao.update(valueSetDstu3); } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java index 301244b96d7..c8602b643da 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/HapiTerminologySvcR4.java @@ -4,7 +4,6 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.dao.IFhirResourceDao; import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao; import ca.uhn.fhir.jpa.entity.TermConcept; -import ca.uhn.fhir.rest.api.server.RequestDetails; import ca.uhn.fhir.util.CoverageIgnore; import ca.uhn.fhir.util.UrlUtil; import org.hl7.fhir.instance.model.api.IBaseResource; @@ -96,32 +95,32 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements } @Override - protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, RequestDetails theRequestDetails) { + protected IIdType createOrUpdateCodeSystem(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource) { if (isBlank(theCodeSystemResource.getIdElement().getIdPart())) { String matchUrl = "CodeSystem?url=" + UrlUtil.escapeUrlParam(theCodeSystemResource.getUrl()); - return myCodeSystemResourceDao.update(theCodeSystemResource, matchUrl, theRequestDetails).getId(); + return myCodeSystemResourceDao.update(theCodeSystemResource, matchUrl).getId(); } else { - return myCodeSystemResourceDao.update(theCodeSystemResource, theRequestDetails).getId(); + return myCodeSystemResourceDao.update(theCodeSystemResource).getId(); } } @Override - protected void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap, RequestDetails theRequestDetails) { + protected void createOrUpdateConceptMap(org.hl7.fhir.r4.model.ConceptMap theConceptMap) { if (isBlank(theConceptMap.getIdElement().getIdPart())) { String matchUrl = "ConceptMap?url=" + UrlUtil.escapeUrlParam(theConceptMap.getUrl()); - myConceptMapResourceDao.update(theConceptMap, matchUrl, theRequestDetails).getId(); + myConceptMapResourceDao.update(theConceptMap, matchUrl); } else { - myConceptMapResourceDao.update(theConceptMap, theRequestDetails).getId(); + myConceptMapResourceDao.update(theConceptMap); } } @Override - protected void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet, RequestDetails theRequestDetails) { + protected void createOrUpdateValueSet(org.hl7.fhir.r4.model.ValueSet theValueSet) { if (isBlank(theValueSet.getIdElement().getIdPart())) { String matchUrl = "ValueSet?url=" + UrlUtil.escapeUrlParam(theValueSet.getUrl()); - myValueSetResourceDao.update(theValueSet, matchUrl, theRequestDetails); + myValueSetResourceDao.update(theValueSet, matchUrl); } else { - myValueSetResourceDao.update(theValueSet, theRequestDetails); + myValueSetResourceDao.update(theValueSet); } } From 26710f760897d009a2347f02cc59db0f7ec49690 Mon Sep 17 00:00:00 2001 From: jamesagnew Date: Fri, 6 Apr 2018 17:29:03 -0400 Subject: [PATCH 16/19] Increase column lengths --- .../ca/uhn/fhir/jpa/entity/TermConceptDesignation.java | 10 +++++----- .../ca/uhn/fhir/jpa/entity/TermConceptProperty.java | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java index ae6d5d16910..0bdaa4ad492 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptDesignation.java @@ -38,15 +38,15 @@ public class TermConceptDesignation implements Serializable { @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_DESIG_PID") @Column(name = "PID") private Long myId; - @Column(name = "LANG", length = 50, nullable = true) + @Column(name = "LANG", length = 500, nullable = true) private String myLanguage; - @Column(name = "USE_SYSTEM", length = 50, nullable = true) + @Column(name = "USE_SYSTEM", length = 500, nullable = true) private String myUseSystem; - @Column(name = "USE_CODE", length = 50, nullable = true) + @Column(name = "USE_CODE", length = 500, nullable = true) private String myUseCode; - @Column(name = "USE_DISPLAY", length = 50, nullable = true) + @Column(name = "USE_DISPLAY", length = 500, nullable = true) private String myUseDisplay; - @Column(name = "VAL", length = 50, nullable = false) + @Column(name = "VAL", length = 500, nullable = false) private String myValue; public String getLanguage() { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java index 852141838eb..71f8c985d9d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptProperty.java @@ -43,22 +43,22 @@ public class TermConceptProperty implements Serializable { @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_PROP_PID") @Column(name = "PID") private Long myId; - @Column(name = "PROP_KEY", length = 200, nullable = false) + @Column(name = "PROP_KEY", length = 500, nullable = false) @NotBlank private String myKey; - @Column(name = "PROP_VAL", length = 200, nullable = true) + @Column(name = "PROP_VAL", length = 500, nullable = true) private String myValue; @Column(name = "PROP_TYPE", length = MAX_PROPTYPE_ENUM_LENGTH, nullable = false) private TermConceptPropertyTypeEnum myType; /** * Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING} */ - @Column(name = "PROP_CODESYSTEM", length = 200, nullable = true) + @Column(name = "PROP_CODESYSTEM", length = 500, nullable = true) private String myCodeSystem; /** * Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING} */ - @Column(name = "PROP_DISPLAY", length = 200, nullable = true) + @Column(name = "PROP_DISPLAY", length = 500, nullable = true) private String myDisplay; /** From 260aa9e9fa44bb27d25f10e0ff799f6e36e5c0f8 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Fri, 6 Apr 2018 17:34:52 -0400 Subject: [PATCH 17/19] Improve the loinc uploader --- .../jpa/term/BaseHapiTerminologySvcImpl.java | 58 +++++++++++-------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index 820e368af17..b837e22d30d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -63,6 +63,7 @@ import javax.persistence.PersistenceContext; import javax.persistence.PersistenceContextType; import java.util.*; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -535,6 +536,16 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } } + private void processDeferredConceptMaps() { + int count = Math.min(myDeferredConceptMaps.size(), 5); + for (ConceptMap nextConceptMap : new ArrayList<>(myDeferredConceptMaps.subList(0, count))) { + ourLog.info("Creating ConceptMap: {}", nextConceptMap.getId()); + createOrUpdateConceptMap(nextConceptMap); + myDeferredConceptMaps.remove(nextConceptMap); + } + ourLog.info("Saved {} deferred ConceptMap resources, have {} remaining", count, myDeferredConceptMaps.size()); + } + private void processDeferredConcepts() { int codeCount = 0, relCount = 0; StopWatch stopwatch = new StopWatch(); @@ -577,6 +588,16 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } } + private void processDeferredValueSets() { + int count = Math.min(myDeferredValueSets.size(), 5); + for (ValueSet nextValueSet : new ArrayList<>(myDeferredValueSets.subList(0, count))) { + ourLog.info("Creating ValueSet: {}", nextValueSet.getId()); + createOrUpdateValueSet(nextValueSet); + myDeferredValueSets.remove(nextValueSet); + } + ourLog.info("Saved {} deferred ValueSet resources, have {} remaining", count, myDeferredConceptMaps.size()); + } + private void processReindexing() { if (System.currentTimeMillis() < myNextReindexPass && !ourForceSaveDeferredAlwaysForUnitTest) { return; @@ -701,9 +722,9 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc TransactionTemplate tt = new TransactionTemplate(myTransactionMgr); tt.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRES_NEW); - tt.execute(t->{ - processDeferredConcepts(); - return null; + tt.execute(t -> { + processDeferredConcepts(); + return null; }); if (myDeferredValueSets.size() > 0) { @@ -721,26 +742,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } - private void processDeferredValueSets() { - int count = Math.min(myDeferredValueSets.size(), 5); - for (ValueSet nextValueSet : myDeferredValueSets.subList(0, count)) { - ourLog.info("Creating ValueSet: {}", nextValueSet.getId()); - createOrUpdateValueSet(nextValueSet); - myDeferredValueSets.remove(nextValueSet); - } - ourLog.info("Saved {} deferred ValueSet resources, have {} remaining", count, myDeferredConceptMaps.size()); - } - - private void processDeferredConceptMaps() { - int count = Math.min(myDeferredConceptMaps.size(), 5); - for (ConceptMap nextConceptMap : myDeferredConceptMaps.subList(0, count)) { - ourLog.info("Creating ConceptMap: {}", nextConceptMap.getId()); - createOrUpdateConceptMap(nextConceptMap); - myDeferredConceptMaps.remove(nextConceptMap); - } - ourLog.info("Saved {} deferred ConceptMap resources, have {} remaining", count, myDeferredConceptMaps.size()); - } - @Override public void setProcessDeferred(boolean theProcessDeferred) { myProcessDeferred = theProcessDeferred; @@ -757,6 +758,8 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc // Grab the existing versions so we can delete them later List existing = myCodeSystemVersionDao.findByCodeSystemResource(theCodeSystemResourcePid); + verifyNoDuplicates(theCodeSystemVersion.getConcepts(), new HashSet()); + /* * For now we always delete old versions.. At some point it would be nice to allow configuration to keep old versions */ @@ -908,6 +911,15 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc return retVal; } + private void verifyNoDuplicates(Collection theConcepts, Set theCodes) { + for (TermConcept next : theConcepts) { + if (!theCodes.add(next.getCode())) { + throw new InvalidRequestException("Duplicate code " + next.getCode() + " found in codesystem after checking " + theCodes.size() + " codes"); + } + verifyNoDuplicates(next.getChildren().stream().map(TermConceptParentChildLink::getChild).collect(Collectors.toList()), theCodes); + } + } + /** * This method is present only for unit tests, do not call from client code */ From b3a9ea263ff419d4bbe59669eea9e3f25a9ee4db Mon Sep 17 00:00:00 2001 From: James Agnew Date: Tue, 10 Apr 2018 21:19:00 -0400 Subject: [PATCH 18/19] Fixes to loinc mapping --- .../ca/uhn/fhir/jpa/dao/SearchBuilder.java | 21 ++++++++----------- .../jpa/term/BaseHapiTerminologySvcImpl.java | 15 ++----------- .../ca/uhn/fhir/jpa/config/TestR4Config.java | 3 ++- .../provider/r4/ResourceProviderR4Test.java | 9 ++++++++ .../jpa/term/TerminologySvcImplDstu3Test.java | 2 +- 5 files changed, 23 insertions(+), 27 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java index de3746e753a..e4e3cf21a70 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/SearchBuilder.java @@ -815,7 +815,7 @@ public class SearchBuilder implements ISearchBuilder { continue; } - predicate = join.get("myUri").as(String.class).in(toFind); + predicate = join.get("myUri").as(String.class).in(toFind); } else if (param.getQualifier() == UriParamQualifierEnum.BELOW) { predicate = myBuilder.like(join.get("myUri").as(String.class), createLeftMatchLikeExpression(value)); @@ -953,8 +953,8 @@ public class SearchBuilder implements ISearchBuilder { Predicate lb = null; if (lowerBound != null) { - Predicate gt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueLow"), lowerBound); - Predicate lt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueHigh"), lowerBound); + Predicate gt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueLow"), lowerBound); + Predicate lt = theBuilder.greaterThanOrEqualTo(theFrom.get("myValueHigh"), lowerBound); if (theRange.getLowerBound().getPrefix() == ParamPrefixEnum.STARTS_AFTER || theRange.getLowerBound().getPrefix() == ParamPrefixEnum.EQUAL) { lb = gt; } else { @@ -964,8 +964,8 @@ public class SearchBuilder implements ISearchBuilder { Predicate ub = null; if (upperBound != null) { - Predicate gt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueLow"), upperBound); - Predicate lt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueHigh"), upperBound); + Predicate gt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueLow"), upperBound); + Predicate lt = theBuilder.lessThanOrEqualTo(theFrom.get("myValueHigh"), upperBound); if (theRange.getUpperBound().getPrefix() == ParamPrefixEnum.ENDS_BEFORE || theRange.getUpperBound().getPrefix() == ParamPrefixEnum.EQUAL) { ub = lt; } else { @@ -2008,15 +2008,15 @@ public class SearchBuilder implements ISearchBuilder { } private static List createLastUpdatedPredicates(final DateRangeParam theLastUpdated, CriteriaBuilder builder, From from) { - List lastUpdatedPredicates = new ArrayList(); + List lastUpdatedPredicates = new ArrayList<>(); if (theLastUpdated != null) { if (theLastUpdated.getLowerBoundAsInstant() != null) { ourLog.debug("LastUpdated lower bound: {}", new InstantDt(theLastUpdated.getLowerBoundAsInstant())); - Predicate predicateLower = builder.greaterThanOrEqualTo(from.get("myUpdated"), theLastUpdated.getLowerBoundAsInstant()); + Predicate predicateLower = builder.greaterThanOrEqualTo(from.get("myUpdated"), theLastUpdated.getLowerBoundAsInstant()); lastUpdatedPredicates.add(predicateLower); } if (theLastUpdated.getUpperBoundAsInstant() != null) { - Predicate predicateUpper = builder.lessThanOrEqualTo(from.get("myUpdated"), theLastUpdated.getUpperBoundAsInstant()); + Predicate predicateUpper = builder.lessThanOrEqualTo(from.get("myUpdated"), theLastUpdated.getUpperBoundAsInstant()); lastUpdatedPredicates.add(predicateUpper); } } @@ -2262,10 +2262,7 @@ public class SearchBuilder implements ISearchBuilder { if (myNext == null) { fetchNext(); } - if (myNext == NO_MORE) { - return false; - } - return true; + return myNext != NO_MORE; } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index b837e22d30d..c1b43c2ebbc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -758,8 +758,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc // Grab the existing versions so we can delete them later List existing = myCodeSystemVersionDao.findByCodeSystemResource(theCodeSystemResourcePid); - verifyNoDuplicates(theCodeSystemVersion.getConcepts(), new HashSet()); - /* * For now we always delete old versions.. At some point it would be nice to allow configuration to keep old versions */ @@ -808,8 +806,8 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc ourLog.info("Validating all codes in CodeSystem for storage (this can take some time for large sets)"); // Validate the code system - ArrayList conceptsStack = new ArrayList(); - IdentityHashMap allConcepts = new IdentityHashMap(); + ArrayList conceptsStack = new ArrayList<>(); + IdentityHashMap allConcepts = new IdentityHashMap<>(); int totalCodeCount = 0; for (TermConcept next : theCodeSystemVersion.getConcepts()) { totalCodeCount += validateConceptForStorage(next, theCodeSystemVersion, conceptsStack, allConcepts); @@ -911,15 +909,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc return retVal; } - private void verifyNoDuplicates(Collection theConcepts, Set theCodes) { - for (TermConcept next : theConcepts) { - if (!theCodes.add(next.getCode())) { - throw new InvalidRequestException("Duplicate code " + next.getCode() + " found in codesystem after checking " + theCodes.size() + " codes"); - } - verifyNoDuplicates(next.getChildren().stream().map(TermConceptParentChildLink::getChild).collect(Collectors.toList()), theCodes); - } - } - /** * This method is present only for unit tests, do not call from client code */ diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java index 4d886041433..1998380c96f 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/config/TestR4Config.java @@ -4,6 +4,7 @@ import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.rest.server.interceptor.RequestValidatingInterceptor; import ca.uhn.fhir.validation.ResultSeverityEnum; import net.ttddyy.dsproxy.listener.ThreadQueryCountHolder; +import net.ttddyy.dsproxy.listener.logging.SLF4JLogLevel; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.jpa.HibernatePersistenceProvider; @@ -104,7 +105,7 @@ public class TestR4Config extends BaseJavaConfigR4 { DataSource dataSource = ProxyDataSourceBuilder .create(retVal) -// .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL") + .logQueryBySlf4j(SLF4JLogLevel.INFO, "SQL") .logSlowQueryBySlf4j(10, TimeUnit.SECONDS) .countQuery(new ThreadQueryCountHolder()) .build(); diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java index b6557c4243c..f5a032617c6 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4Test.java @@ -182,6 +182,15 @@ public class ResourceProviderR4Test extends BaseResourceProviderR4Test { return ids; } + @Test + @Ignore + public void test() throws IOException { + HttpGet get = new HttpGet(ourServerBase + "/QuestionnaireResponse?_count=50&status=completed&questionnaire=ARIncenterAbsRecord&_lastUpdated=%3E"+UrlUtil.escapeUrlParam("=2018-01-01")+"&context.organization=O3435"); + ourLog.info("*** MAKING QUERY"); + ourHttpClient.execute(get); + System.exit(0); + } + @Test public void testBundleCreate() throws Exception { IGenericClient client = myClient; diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java index cf6154b7532..0b0214562b2 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologySvcImplDstu3Test.java @@ -205,7 +205,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test { ValueSet outcome = myTermSvc.expandValueSet(vs); codes = toCodesContains(outcome.getExpansion().getContains()); - assertThat(codes, containsInAnyOrder("ParentA", "childAAA", "childAAB", "childAA", "childAB", "ParentB")); + assertThat(codes, containsInAnyOrder("ParentWithNoChildrenA", "ParentWithNoChildrenB", "ParentWithNoChildrenC", "ParentA", "childAAA", "childAAB", "childAA", "childAB", "ParentB")); } @Test From 8bfd7913c8e9c091be37ce6dc64300a780c4b9a4 Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sun, 15 Apr 2018 17:19:34 -0400 Subject: [PATCH 19/19] More work on loinc loader --- .../uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java | 2 +- .../jpa/dao/dstu3/FhirSystemDaoDstu3.java | 2 +- .../uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java | 2 +- .../BaseSubscriptionInterceptor.java | 26 ++++++++++ .../subscription/CanonicalSubscription.java | 51 ++++++++++++++++--- ...scriptionDeliveringRestHookSubscriber.java | 44 +++++++++++++--- .../jpa/term/BaseHapiTerminologySvcImpl.java | 4 +- .../ca/uhn/fhir/jpa/util/JpaConstants.java | 29 +++++++++++ .../subscription/r4/RestHookTestR4Test.java | 35 +++++++++++++ .../derby_maintenance.txt | 1 - 10 files changed, 174 insertions(+), 22 deletions(-) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java index b6a5e7e8de9..b6893c13e14 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/FhirSystemDaoDstu2.java @@ -209,7 +209,7 @@ public class FhirSystemDaoDstu2 extends BaseHapiFhirSystemDao { for (int i = 0; i < theRequest.getEntry().size(); i++) { if (i % 100 == 0) { - ourLog.info("Processed {} non-GET entries out of {}", i, theRequest.getEntry().size()); + ourLog.debug("Processed {} non-GET entries out of {}", i, theRequest.getEntry().size()); } Entry nextReqEntry = theRequest.getEntry().get(i); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java index 19ac3bf479a..3bbb31c17e2 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/dstu3/FhirSystemDaoDstu3.java @@ -318,7 +318,7 @@ public class FhirSystemDaoDstu3 extends BaseHapiFhirSystemDao { for (int i = 0; i < theEntries.size(); i++) { if (i % 100 == 0) { - ourLog.info("Processed {} non-GET entries out of {}", i, theEntries.size()); + ourLog.debug("Processed {} non-GET entries out of {}", i, theEntries.size()); } BundleEntryComponent nextReqEntry = theEntries.get(i); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java index 04e42e6d6e4..fa241a6b37e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/r4/FhirSystemDaoR4.java @@ -333,7 +333,7 @@ public class FhirSystemDaoR4 extends BaseHapiFhirSystemDao { for (int i = 0; i < theEntries.size(); i++) { if (i % 100 == 0) { - ourLog.info("Processed {} non-GET entries out of {}", i, theEntries.size()); + ourLog.debug("Processed {} non-GET entries out of {}", i, theEntries.size()); } BundleEntryComponent nextReqEntry = theEntries.get(i); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionInterceptor.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionInterceptor.java index a22396e22ff..c427ed579a9 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionInterceptor.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/BaseSubscriptionInterceptor.java @@ -168,6 +168,19 @@ public abstract class BaseSubscriptionInterceptor exten retVal.getEmailDetails().setSubjectTemplate(subjectTemplate); } + if (retVal.getChannelType() == Subscription.SubscriptionChannelType.RESTHOOK) { + String stripVersionIds; + String deliverLatestVersion; + try { + stripVersionIds = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS); + deliverLatestVersion = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION); + } catch (FHIRException theE) { + throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE); + } + retVal.getRestHookDetails().setStripVersionId(Boolean.parseBoolean(stripVersionIds)); + retVal.getRestHookDetails().setDeliverLatestVersion(Boolean.parseBoolean(deliverLatestVersion)); + } + } catch (FHIRException theE) { throw new InternalErrorException(theE); } @@ -201,6 +214,19 @@ public abstract class BaseSubscriptionInterceptor exten retVal.getEmailDetails().setSubjectTemplate(subjectTemplate); } + if (retVal.getChannelType() == Subscription.SubscriptionChannelType.RESTHOOK) { + String stripVersionIds; + String deliverLatestVersion; + try { + stripVersionIds = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS); + deliverLatestVersion = subscription.getChannel().getExtensionString(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION); + } catch (FHIRException theE) { + throw new ConfigurationException("Failed to extract subscription extension(s): " + theE.getMessage(), theE); + } + retVal.getRestHookDetails().setStripVersionId(Boolean.parseBoolean(stripVersionIds)); + retVal.getRestHookDetails().setDeliverLatestVersion(Boolean.parseBoolean(deliverLatestVersion)); + } + List topicExts = subscription.getExtensionsByUrl("http://hl7.org/fhir/subscription/topics"); if (topicExts.size() > 0) { IBaseReference ref = (IBaseReference) topicExts.get(0).getValueAsPrimitive(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/CanonicalSubscription.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/CanonicalSubscription.java index 0c54a644790..aa75ce9dfb6 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/CanonicalSubscription.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/CanonicalSubscription.java @@ -67,6 +67,8 @@ public class CanonicalSubscription implements Serializable { private CanonicalEventDefinition myTrigger; @JsonProperty("emailDetails") private EmailDetails myEmailDetails; + @JsonProperty("restHookDetails") + private RestHookDetails myRestHookDetails; /** * For now we're using the R4 TriggerDefinition, but this @@ -131,12 +133,10 @@ public class CanonicalSubscription implements Serializable { return myHeaders; } - public void setHeaders(List> theHeader) { + public void setHeaders(String theHeaders) { myHeaders = new ArrayList<>(); - for (IPrimitiveType next : theHeader) { - if (isNotBlank(next.getValueAsString())) { - myHeaders.add(next.getValueAsString()); - } + if (isNotBlank(theHeaders)) { + myHeaders.add(theHeaders); } } @@ -160,6 +160,13 @@ public class CanonicalSubscription implements Serializable { myPayloadString = thePayloadString; } + public RestHookDetails getRestHookDetails() { + if (myRestHookDetails == null) { + myRestHookDetails = new RestHookDetails(); + } + return myRestHookDetails; + } + public Subscription.SubscriptionStatus getStatus() { return myStatus; } @@ -191,10 +198,12 @@ public class CanonicalSubscription implements Serializable { } } - public void setHeaders(String theHeaders) { + public void setHeaders(List> theHeader) { myHeaders = new ArrayList<>(); - if (isNotBlank(theHeaders)) { - myHeaders.add(theHeaders); + for (IPrimitiveType next : theHeader) { + if (isNotBlank(next.getValueAsString())) { + myHeaders.add(next.getValueAsString()); + } } } @@ -230,6 +239,32 @@ public class CanonicalSubscription implements Serializable { } } + @JsonInclude(JsonInclude.Include.NON_NULL) + @JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE) + public static class RestHookDetails { + @JsonProperty("stripVersionId") + private boolean myStripVersionId; + @JsonProperty("deliverLatestVersion") + private boolean myDeliverLatestVersion; + + public boolean isDeliverLatestVersion() { + return myDeliverLatestVersion; + } + + public void setDeliverLatestVersion(boolean theDeliverLatestVersion) { + myDeliverLatestVersion = theDeliverLatestVersion; + } + + public boolean isStripVersionId() { + return myStripVersionId; + } + + public void setStripVersionId(boolean theStripVersionId) { + myStripVersionId = theStripVersionId; + } + + } + @JsonInclude(JsonInclude.Include.NON_NULL) @JsonAutoDetect(creatorVisibility = JsonAutoDetect.Visibility.NONE, fieldVisibility = JsonAutoDetect.Visibility.NONE, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE) public static class CanonicalEventDefinition { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java index a04b44af8c1..65881b7d2b3 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/subscription/resthook/SubscriptionDeliveringRestHookSubscriber.java @@ -28,8 +28,10 @@ import ca.uhn.fhir.rest.api.RequestTypeEnum; import ca.uhn.fhir.rest.client.api.*; import ca.uhn.fhir.rest.client.interceptor.SimpleRequestHeaderInterceptor; import ca.uhn.fhir.rest.gclient.IClientExecutable; +import ca.uhn.fhir.rest.server.exceptions.ResourceGoneException; import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.r4.model.Subscription; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,21 +53,26 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe } protected void deliverPayload(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, EncodingEnum thePayloadType, IGenericClient theClient) { - IBaseResource payloadResource = theMsg.getPayload(getContext()); + IBaseResource payloadResource = getAndMassagePayload(theMsg, theSubscription); + if (payloadResource == null) return; + doDelivery(theMsg, theSubscription, thePayloadType, theClient, payloadResource); + } + + protected void doDelivery(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription, EncodingEnum thePayloadType, IGenericClient theClient, IBaseResource thePayloadResource) { IClientExecutable operation; switch (theMsg.getOperationType()) { case CREATE: - if (payloadResource == null || payloadResource.isEmpty()) { + if (thePayloadResource == null || thePayloadResource.isEmpty()) { if (thePayloadType != null ) { - operation = theClient.create().resource(payloadResource); + operation = theClient.create().resource(thePayloadResource); } else { sendNotification(theMsg); return; } } else { if (thePayloadType != null ) { - operation = theClient.update().resource(payloadResource); + operation = theClient.update().resource(thePayloadResource); } else { sendNotification(theMsg); return; @@ -73,16 +80,16 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe } break; case UPDATE: - if (payloadResource == null || payloadResource.isEmpty()) { + if (thePayloadResource == null || thePayloadResource.isEmpty()) { if (thePayloadType != null ) { - operation = theClient.create().resource(payloadResource); + operation = theClient.create().resource(thePayloadResource); } else { sendNotification(theMsg); return; } } else { if (thePayloadType != null ) { - operation = theClient.update().resource(payloadResource); + operation = theClient.update().resource(thePayloadResource); } else { sendNotification(theMsg); return; @@ -101,7 +108,7 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe operation.encoded(thePayloadType); } - ourLog.info("Delivering {} rest-hook payload {} for {}", theMsg.getOperationType(), payloadResource.getIdElement().toUnqualified().getValue(), theSubscription.getIdElement(getContext()).toUnqualifiedVersionless().getValue()); + ourLog.info("Delivering {} rest-hook payload {} for {}", theMsg.getOperationType(), thePayloadResource.getIdElement().toUnqualified().getValue(), theSubscription.getIdElement(getContext()).toUnqualifiedVersionless().getValue()); try { operation.execute(); @@ -112,6 +119,27 @@ public class SubscriptionDeliveringRestHookSubscriber extends BaseSubscriptionDe } } + protected IBaseResource getAndMassagePayload(ResourceDeliveryMessage theMsg, CanonicalSubscription theSubscription) { + IBaseResource payloadResource = theMsg.getPayload(getContext()); + + if (theSubscription.getRestHookDetails().isDeliverLatestVersion()) { + IFhirResourceDao dao = getSubscriptionDao().getDao(payloadResource.getClass()); + try { + payloadResource = dao.read(payloadResource.getIdElement().toVersionless()); + } catch (ResourceGoneException e) { + ourLog.warn("Resource {} is deleted, not going to deliver for subscription {}", payloadResource.getIdElement(), theSubscription.getIdElement(getContext())); + return null; + } + } + + IIdType resourceId = payloadResource.getIdElement(); + if (theSubscription.getRestHookDetails().isStripVersionId()) { + resourceId = resourceId.toVersionless(); + payloadResource.setId(resourceId); + } + return payloadResource; + } + @Override public void handleMessage(ResourceDeliveryMessage theMessage) throws MessagingException { CanonicalSubscription subscription = theMessage.getSubscription(); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index c1b43c2ebbc..80330fcc85e 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java index 39a3a2cea09..a9b7206c9ce 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/util/JpaConstants.java @@ -40,4 +40,33 @@ public class JpaConstants { */ public static final String EXT_SUBSCRIPTION_SUBJECT_TEMPLATE = "http://hapifhir.io/fhir/StructureDefinition/subscription-email-subject-template"; + + /** + * This extension URL indicates whether a REST HOOK delivery should + * include the version ID when delivering. + *

    + * This extension should be of type boolean and should be + * placed on the Subscription.channel element. + *

    + */ + public static final String EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS = "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-strip-version-ids"; + + /** + * This extension URL indicates whether a REST HOOK delivery should + * reload the resource and deliver the latest version always. This + * could be useful for example if a resource which triggers a + * subscription gets updated many times in short succession and there + * is no value in delivering the older versions. + *

    + * Note that if the resource is now deleted, this may cause + * the delivery to be cancelled altogether. + *

    + * + *

    + * This extension should be of type boolean and should be + * placed on the Subscription.channel element. + *

    + */ + public static final String EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION = "http://hapifhir.io/fhir/StructureDefinition/subscription-resthook-deliver-latest-version"; + } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/r4/RestHookTestR4Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/r4/RestHookTestR4Test.java index 5a9c817e71e..4de1ab758ac 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/r4/RestHookTestR4Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/subscription/r4/RestHookTestR4Test.java @@ -2,8 +2,10 @@ package ca.uhn.fhir.jpa.subscription.r4; import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.jpa.dao.DaoConfig; +import ca.uhn.fhir.jpa.provider.JpaConformanceProviderDstu2; import ca.uhn.fhir.jpa.provider.r4.BaseResourceProviderR4Test; import ca.uhn.fhir.jpa.subscription.RestHookTestDstu2Test; +import ca.uhn.fhir.jpa.util.JpaConstants; import ca.uhn.fhir.rest.annotation.Create; import ca.uhn.fhir.rest.annotation.ResourceParam; import ca.uhn.fhir.rest.annotation.Update; @@ -172,7 +174,38 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test { } + @Test + public void testRestHookSubscriptionApplicationJsonDisableVersionIdInDelivery() throws Exception { + String payload = "application/json"; + + String code = "1000000050"; + String criteria1 = "Observation?code=SNOMED-CT|" + code + "&_format=xml"; + + Subscription subscription1 = createSubscription(criteria1, payload, ourListenerServerBase); + subscription1 + .getChannel() + .addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_STRIP_VERSION_IDS, new BooleanType("true")); + subscription1 + .getChannel() + .addExtension(JpaConstants.EXT_SUBSCRIPTION_RESTHOOK_DELIVER_LATEST_VERSION, new BooleanType("true")); + myClient.update().resource(subscription1).execute(); + waitForQueueToDrain(); + + Observation observation1 = sendObservation(code, "SNOMED-CT"); + + // Should see 1 subscription notification + waitForQueueToDrain(); + waitForSize(0, ourCreatedObservations); + waitForSize(1, ourUpdatedObservations); + assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0)); + + assertEquals(observation1.getIdElement().getIdPart(), ourUpdatedObservations.get(0).getIdElement().getIdPart()); + assertEquals(null, ourUpdatedObservations.get(0).getIdElement().getVersionIdPart()); + } + + + @Test public void testRestHookSubscriptionApplicationJson() throws Exception { String payload = "application/json"; @@ -191,6 +224,8 @@ public class RestHookTestR4Test extends BaseResourceProviderR4Test { waitForSize(1, ourUpdatedObservations); assertEquals(Constants.CT_FHIR_JSON_NEW, ourContentTypes.get(0)); + assertEquals("1", ourUpdatedObservations.get(0).getIdElement().getVersionIdPart()); + Subscription subscriptionTemp = myClient.read(Subscription.class, subscription2.getId()); Assert.assertNotNull(subscriptionTemp); diff --git a/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt b/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt index a8fe4c2edd0..a09a63c8e1a 100644 --- a/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt +++ b/hapi-fhir-jpaserver-uhnfhirtest/derby_maintenance.txt @@ -108,7 +108,6 @@ where res_id in ( drop table hfj_history_tag cascade constraints; drop table hfj_forced_id cascade constraints; -drop table HFJ_SUBSCRIPTION_STATS cascade constraints; drop table hfj_res_link cascade constraints; drop table hfj_spidx_coords cascade constraints; drop table hfj_spidx_date cascade constraints;