Port changes to master from release branch 6_4 (#4785)

* Start porting changes to master that were lost from the release branch.

* Add remainder of tag changes from release branch.

* Changelog changes.

* Fix schema migration.

* Remove duplicate lines.

* Update core to 6.0.1.

* Fixes for new core.

* Bump to snapshot 19 to mirror James' changes.

* Fix migration tasks.

* Fix some of the tests due to the core upgrade.

* Fix rest of tests.
This commit is contained in:
Luke deGruchy 2023-04-28 15:56:02 -04:00 committed by GitHub
parent 81854baa02
commit 79c96dc1bb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 395 additions and 79 deletions

View File

@ -19,8 +19,6 @@
*/
package ca.uhn.fhir.model.api;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import java.net.URI;
import org.apache.commons.lang3.StringUtils;

View File

@ -0,0 +1,6 @@
---
type: fix
issue: 4786
jira: SMILE-4688
title: "With default configuration, Resource meta.tag properties: `userSelected` and `version`, were not stored in the database.
This is now fixed."

View File

@ -147,6 +147,7 @@ import javax.persistence.PersistenceContextType;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.xml.stream.events.Characters;
import javax.xml.stream.events.XMLEvent;
@ -163,7 +164,9 @@ import java.util.Set;
import java.util.StringTokenizer;
import java.util.stream.Collectors;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
import static org.apache.commons.lang3.BooleanUtils.isFalse;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.left;
@ -279,7 +282,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
TagList tagList = ResourceMetadataKeyEnum.TAG_LIST.get(theResource);
if (tagList != null) {
for (Tag next : tagList) {
TagDefinition def = getTagOrNull(theTransactionDetails, TagTypeEnum.TAG, next.getScheme(), next.getTerm(), next.getLabel());
TagDefinition def = getTagOrNull(theTransactionDetails, TagTypeEnum.TAG, next.getScheme(), next.getTerm(),
next.getLabel(), next.getVersion(), next.getUserSelected());
if (def != null) {
ResourceTag tag = theEntity.addTag(def);
allDefs.add(tag);
@ -291,7 +295,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
List<BaseCodingDt> securityLabels = ResourceMetadataKeyEnum.SECURITY_LABELS.get(theResource);
if (securityLabels != null) {
for (BaseCodingDt next : securityLabels) {
TagDefinition def = getTagOrNull(theTransactionDetails, TagTypeEnum.SECURITY_LABEL, next.getSystemElement().getValue(), next.getCodeElement().getValue(), next.getDisplayElement().getValue());
TagDefinition def = getTagOrNull(theTransactionDetails, TagTypeEnum.SECURITY_LABEL, next.getSystemElement().getValue(),
next.getCodeElement().getValue(), next.getDisplayElement().getValue(), null, null);
if (def != null) {
ResourceTag tag = theEntity.addTag(def);
allDefs.add(tag);
@ -303,7 +308,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
List<IdDt> profiles = ResourceMetadataKeyEnum.PROFILES.get(theResource);
if (profiles != null) {
for (IIdType next : profiles) {
TagDefinition def = getTagOrNull(theTransactionDetails, TagTypeEnum.PROFILE, NS_JPA_PROFILE, next.getValue(), null);
TagDefinition def = getTagOrNull(theTransactionDetails, TagTypeEnum.PROFILE, NS_JPA_PROFILE, next.getValue(), null, null, null);
if (def != null) {
ResourceTag tag = theEntity.addTag(def);
allDefs.add(tag);
@ -317,7 +322,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
List<? extends IBaseCoding> tagList = theResource.getMeta().getTag();
if (tagList != null) {
for (IBaseCoding next : tagList) {
TagDefinition def = getTagOrNull(theTransactionDetails, TagTypeEnum.TAG, next.getSystem(), next.getCode(), next.getDisplay());
TagDefinition def = getTagOrNull(theTransactionDetails, TagTypeEnum.TAG, next.getSystem(), next.getCode(),
next.getDisplay(), next.getVersion(), next.getUserSelected());
if (def != null) {
ResourceTag tag = theEntity.addTag(def);
theAllTags.add(tag);
@ -329,7 +335,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
List<? extends IBaseCoding> securityLabels = theResource.getMeta().getSecurity();
if (securityLabels != null) {
for (IBaseCoding next : securityLabels) {
TagDefinition def = getTagOrNull(theTransactionDetails, TagTypeEnum.SECURITY_LABEL, next.getSystem(), next.getCode(), next.getDisplay());
TagDefinition def = getTagOrNull(theTransactionDetails, TagTypeEnum.SECURITY_LABEL, next.getSystem(), next.getCode(), next.getDisplay(), null, null);
if (def != null) {
ResourceTag tag = theEntity.addTag(def);
theAllTags.add(tag);
@ -341,7 +347,7 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
List<? extends IPrimitiveType<String>> profiles = theResource.getMeta().getProfile();
if (profiles != null) {
for (IPrimitiveType<String> next : profiles) {
TagDefinition def = getTagOrNull(theTransactionDetails, TagTypeEnum.PROFILE, NS_JPA_PROFILE, next.getValue(), null);
TagDefinition def = getTagOrNull(theTransactionDetails, TagTypeEnum.PROFILE, NS_JPA_PROFILE, next.getValue(), null, null, null);
if (def != null) {
ResourceTag tag = theEntity.addTag(def);
theAllTags.add(tag);
@ -379,22 +385,25 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
/**
* <code>null</code> will only be returned if the scheme and tag are both blank
*/
protected TagDefinition getTagOrNull(TransactionDetails theTransactionDetails, TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
protected TagDefinition getTagOrNull(TransactionDetails theTransactionDetails, TagTypeEnum theTagType, String theScheme,
String theTerm, String theLabel, String theVersion, Boolean theUserSelected) {
if (isBlank(theScheme) && isBlank(theTerm) && isBlank(theLabel)) {
return null;
}
MemoryCacheService.TagDefinitionCacheKey key = toTagDefinitionMemoryCacheKey(theTagType, theScheme, theTerm);
MemoryCacheService.TagDefinitionCacheKey key = toTagDefinitionMemoryCacheKey(theTagType, theScheme, theTerm, theVersion, theUserSelected);
TagDefinition retVal = myMemoryCacheService.getIfPresent(MemoryCacheService.CacheEnum.TAG_DEFINITION, key);
if (retVal == null) {
HashMap<MemoryCacheService.TagDefinitionCacheKey, TagDefinition> resolvedTagDefinitions = theTransactionDetails.getOrCreateUserData(HapiTransactionService.XACT_USERDATA_KEY_RESOLVED_TAG_DEFINITIONS, HashMap::new);
HashMap<MemoryCacheService.TagDefinitionCacheKey, TagDefinition> resolvedTagDefinitions = theTransactionDetails
.getOrCreateUserData(HapiTransactionService.XACT_USERDATA_KEY_RESOLVED_TAG_DEFINITIONS, HashMap::new);
retVal = resolvedTagDefinitions.get(key);
if (retVal == null) {
// actual DB hit(s) happen here
retVal = getOrCreateTag(theTagType, theScheme, theTerm, theLabel);
retVal = getOrCreateTag(theTagType, theScheme, theTerm, theLabel, theVersion, theUserSelected);
TransactionSynchronization sync = new AddTagDefinitionToCacheAfterCommitSynchronization(key, retVal);
TransactionSynchronizationManager.registerSynchronization(sync);
@ -412,26 +421,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
* <p>
* Can also throw an InternalErrorException if something bad happens.
*/
private TagDefinition getOrCreateTag(TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel) {
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<TagDefinition> cq = builder.createQuery(TagDefinition.class);
Root<TagDefinition> from = cq.from(TagDefinition.class);
private TagDefinition getOrCreateTag(TagTypeEnum theTagType, String theScheme, String theTerm, String theLabel,
String theVersion, Boolean theUserSelected) {
if (isNotBlank(theScheme)) {
cq.where(
builder.and(
builder.equal(from.get("myTagType"), theTagType),
builder.equal(from.get("mySystem"), theScheme),
builder.equal(from.get("myCode"), theTerm)));
} else {
cq.where(
builder.and(
builder.equal(from.get("myTagType"), theTagType),
builder.isNull(from.get("mySystem")),
builder.equal(from.get("myCode"), theTerm)));
}
TypedQuery<TagDefinition> q = myEntityManager.createQuery(cq);
TypedQuery<TagDefinition> q = buildTagQuery(theTagType, theScheme, theTerm, theVersion, theUserSelected);
TransactionTemplate template = new TransactionTemplate(myTransactionManager);
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
@ -453,6 +446,8 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
val = q.getSingleResult();
} catch (NoResultException e) {
val = new TagDefinition(theTagType, theScheme, theTerm, theLabel);
val.setVersion(theVersion);
val.setUserSelected(theUserSelected);
myEntityManager.persist(val);
}
return val;
@ -510,6 +505,34 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
return retVal;
}
private TypedQuery<TagDefinition> buildTagQuery(TagTypeEnum theTagType, String theScheme, String theTerm,
String theVersion, Boolean theUserSelected) {
CriteriaBuilder builder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<TagDefinition> cq = builder.createQuery(TagDefinition.class);
Root<TagDefinition> from = cq.from(TagDefinition.class);
List<Predicate> predicates = new ArrayList<>();
predicates.add(
builder.and(
builder.equal(from.get("myTagType"), theTagType),
builder.equal(from.get("myCode"), theTerm)));
predicates.add( isBlank(theScheme)
? builder.isNull(from.get("mySystem"))
: builder.equal(from.get("mySystem"), theScheme));
predicates.add( isBlank(theVersion)
? builder.isNull(from.get("myVersion"))
: builder.equal(from.get("myVersion"), theVersion));
predicates.add( isNull(theUserSelected) || isFalse(theUserSelected)
? builder.isFalse(from.get("myUserSelected"))
: builder.isTrue(from.get("myUserSelected")));
cq.where(predicates.toArray(new Predicate[0]));
return myEntityManager.createQuery(cq);
}
void incrementId(T theResource, ResourceTable theSavedEntity, IIdType theResourceId) {
String newVersion;
@ -761,10 +784,10 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
}
RuntimeResourceDefinition def = myContext.getResourceDefinition(theResource);
if (def.isStandardType() == false) {
if ( ! def.isStandardType()) {
String profile = def.getResourceProfile("");
if (isNotBlank(profile)) {
TagDefinition profileDef = getTagOrNull(theTransactionDetails, TagTypeEnum.PROFILE, NS_JPA_PROFILE, profile, null);
TagDefinition profileDef = getTagOrNull(theTransactionDetails, TagTypeEnum.PROFILE, NS_JPA_PROFILE, profile, null, null, null);
ResourceTag tag = theEntity.addTag(profileDef);
allDefs.add(tag);
@ -797,7 +820,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
theResource.getMeta()
.addTag()
.setCode(tag.getTag().getCode())
.setSystem(tag.getTag().getSystem());
.setSystem(tag.getTag().getSystem())
.setVersion(tag.getTag().getVersion())
.setUserSelected(tag.getTag().getUserSelected());
});
theEntity.setHasTags(!allTagsNew.isEmpty());
@ -1604,8 +1629,9 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> extends BaseStora
}
@Nonnull
public static MemoryCacheService.TagDefinitionCacheKey toTagDefinitionMemoryCacheKey(TagTypeEnum theTagType, String theScheme, String theTerm) {
return new MemoryCacheService.TagDefinitionCacheKey(theTagType, theScheme, theTerm);
public static MemoryCacheService.TagDefinitionCacheKey toTagDefinitionMemoryCacheKey(
TagTypeEnum theTagType, String theScheme, String theTerm, String theVersion, Boolean theUserSelected) {
return new MemoryCacheService.TagDefinitionCacheKey(theTagType, theScheme, theTerm, theVersion, theUserSelected);
}
@SuppressWarnings("unchecked")

View File

@ -824,9 +824,11 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
boolean hasTag = false;
for (BaseTag next : new ArrayList<>(theEntity.getTags())) {
if (ObjectUtil.equals(next.getTag().getTagType(), nextDef.getTagType()) &&
ObjectUtil.equals(next.getTag().getSystem(), nextDef.getSystem()) &&
ObjectUtil.equals(next.getTag().getCode(), nextDef.getCode())) {
if (Objects.equals(next.getTag().getTagType(), nextDef.getTagType()) &&
Objects.equals(next.getTag().getSystem(), nextDef.getSystem()) &&
Objects.equals(next.getTag().getCode(), nextDef.getCode()) &&
Objects.equals(next.getTag().getVersion(), nextDef.getVersion()) &&
Objects.equals(next.getTag().getUserSelected(), nextDef.getUserSelected())) {
hasTag = true;
break;
}
@ -835,7 +837,8 @@ public abstract class BaseHapiFhirResourceDao<T extends IBaseResource> extends B
if (!hasTag) {
theEntity.setHasTags(true);
TagDefinition def = getTagOrNull(theTransactionDetails, nextDef.getTagType(), nextDef.getSystem(), nextDef.getCode(), nextDef.getDisplay());
TagDefinition def = getTagOrNull(theTransactionDetails, nextDef.getTagType(), nextDef.getSystem(),
nextDef.getCode(), nextDef.getDisplay(), nextDef.getVersion(), nextDef.getUserSelected());
if (def != null) {
BaseTag newEntity = theEntity.addTag(def);
if (newEntity.getTagId() == null) {

View File

@ -433,6 +433,8 @@ public class JpaStorageResourceParser implements IJpaStorageResourceParser {
tag.setSystem(next.getTag().getSystem());
tag.setCode(next.getTag().getCode());
tag.setDisplay(next.getTag().getDisplay());
tag.setVersion(next.getTag().getVersion());
tag.setUserSelected(next.getTag().getUserSelected());
break;
}
}

View File

@ -111,6 +111,33 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
version.onTable("BT2_WORK_CHUNK")
.migratePostgresTextClobToBinaryClob("20230208.3", "CHUNK_DATA");
{
Builder.BuilderWithTableName tagDefTable = version.onTable("HFJ_TAG_DEF");
// add columns
tagDefTable
.addColumn("20230209.1", "TAG_VERSION")
.nullable()
.type(ColumnTypeEnum.STRING, 30);
tagDefTable
.addColumn("20230209.2", "TAG_USER_SELECTED")
.nullable()
.type(ColumnTypeEnum.BOOLEAN);
// Update indexing
tagDefTable.dropIndex("20230209.3", "IDX_TAGDEF_TYPESYSCODE");
tagDefTable.dropIndex("20230209.4", "IDX_TAGDEF_TYPESYSCODEVERUS");
Map<DriverTypeEnum, String> addTagDefConstraint = new HashMap<>();
addTagDefConstraint.put(DriverTypeEnum.H2_EMBEDDED, "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODEVERUS UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM, TAG_VERSION, TAG_USER_SELECTED)");
addTagDefConstraint.put(DriverTypeEnum.MARIADB_10_1, "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODEVERUS UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM, TAG_VERSION, TAG_USER_SELECTED)");
addTagDefConstraint.put(DriverTypeEnum.MSSQL_2012, "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODEVERUS UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM, TAG_VERSION, TAG_USER_SELECTED)");
addTagDefConstraint.put(DriverTypeEnum.MYSQL_5_7, "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODEVERUS UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM, TAG_VERSION, TAG_USER_SELECTED)");
addTagDefConstraint.put(DriverTypeEnum.ORACLE_12C, "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODEVERUS UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM, TAG_VERSION, TAG_USER_SELECTED)");
addTagDefConstraint.put(DriverTypeEnum.POSTGRES_9_4, "ALTER TABLE HFJ_TAG_DEF ADD CONSTRAINT IDX_TAGDEF_TYPESYSCODEVERUS UNIQUE (TAG_TYPE, TAG_CODE, TAG_SYSTEM, TAG_VERSION, TAG_USER_SELECTED)");
version.executeRawSql("20230209.5", addTagDefConstraint);
}
version
.onTable(Search.HFJ_SEARCH)
.addColumn("20230215.1", Search.SEARCH_UUID)

View File

@ -19,6 +19,7 @@
*/
package ca.uhn.fhir.jpa.model.entity;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
@ -48,7 +49,8 @@ import java.util.Collection;
@Index(name = "IDX_TAG_DEF_TP_CD_SYS", columnList = "TAG_TYPE, TAG_CODE, TAG_SYSTEM, TAG_ID"),
},
uniqueConstraints = {
@UniqueConstraint(name = "IDX_TAGDEF_TYPESYSCODE", columnNames = {"TAG_TYPE", "TAG_SYSTEM", "TAG_CODE"})
@UniqueConstraint(name = "IDX_TAGDEF_TYPESYSCODEVERUS",
columnNames = {"TAG_TYPE", "TAG_SYSTEM", "TAG_CODE", "TAG_VERSION", "TAG_USER_SELECTED"})
}
)
public class TagDefinition implements Serializable {
@ -56,22 +58,35 @@ public class TagDefinition implements Serializable {
private static final long serialVersionUID = 1L;
@Column(name = "TAG_CODE", length = 200)
private String myCode;
@Column(name = "TAG_DISPLAY", length = 200)
private String myDisplay;
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_TAGDEF_ID")
@SequenceGenerator(name = "SEQ_TAGDEF_ID", sequenceName = "SEQ_TAGDEF_ID")
@Column(name = "TAG_ID")
private Long myId;
@OneToMany(cascade = {}, fetch = FetchType.LAZY, mappedBy = "myTag")
private Collection<ResourceTag> myResources;
@OneToMany(cascade = {}, fetch = FetchType.LAZY, mappedBy = "myTag")
private Collection<ResourceHistoryTag> myResourceVersions;
@Column(name = "TAG_SYSTEM", length = 200)
private String mySystem;
@Column(name = "TAG_TYPE", nullable = false)
@Enumerated(EnumType.ORDINAL)
private TagTypeEnum myTagType;
@Column(name = "TAG_VERSION", length = 30)
private String myVersion;
@Column(name = "TAG_USER_SELECTED")
private Boolean myUserSelected;
@Transient
private transient Integer myHashCode;
@ -132,6 +147,31 @@ public class TagDefinition implements Serializable {
myHashCode = null;
}
public String getVersion() {
return myVersion;
}
public void setVersion(String theVersion) {
setVersionAfterTrim(theVersion);
}
private void setVersionAfterTrim(String theVersion) {
if (theVersion != null) {
myVersion = StringUtils.truncate(theVersion, 30);
}
}
public Boolean getUserSelected() {
// TODO: LD: this is not ideal as we are implicitly assuming null is false.
// Ideally we should fix IBaseCoding to return wrapper Boolean but that will involve another core/hapi release
return myUserSelected != null ? myUserSelected : false;
}
public void setUserSelected(Boolean theUserSelected) {
myUserSelected = theUserSelected != null && theUserSelected;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
@ -150,6 +190,8 @@ public class TagDefinition implements Serializable {
b.append(myTagType, other.myTagType);
b.append(mySystem, other.mySystem);
b.append(myCode, other.myCode);
b.append(myVersion, other.myVersion);
b.append(myUserSelected, other.myUserSelected);
}
return b.isEquals();
@ -162,6 +204,8 @@ public class TagDefinition implements Serializable {
b.append(myTagType);
b.append(mySystem);
b.append(myCode);
b.append(myVersion);
b.append(myUserSelected);
myHashCode = b.toHashCode();
}
return myHashCode;
@ -174,6 +218,8 @@ public class TagDefinition implements Serializable {
retVal.append("system", mySystem);
retVal.append("code", myCode);
retVal.append("display", myDisplay);
retVal.append("version", myVersion);
retVal.append("userSelected", myUserSelected);
return retVal.build();
}
}

View File

@ -13,7 +13,11 @@ public class TagDefinitionTest {
def.setCode("my_code");
def.setSystem("my_system");
def.setDisplay("my_display");
assertEquals("TagDefinition[id=<null>,system=my_system,code=my_code,display=my_display]", def.toString());
def.setVersion("V 1.0");
def.setUserSelected(true);
assertEquals(
"TagDefinition[id=<null>,system=my_system,code=my_code,display=my_display,version=V 1.0,userSelected=true]",
def.toString());
}
@Test
@ -39,7 +43,8 @@ public class TagDefinitionTest {
def.setCode("my_code");
def.setSystem("my_system");
def.setDisplay("my_display");
assertEquals (-2125810377,def.hashCode());
assertEquals (-2125810377,def.hashCode());
def.setVersion("V 1.0");
def.setUserSelected(true);
assertEquals (434167707,def.hashCode());
}
}

View File

@ -63,8 +63,8 @@ public class ExpandResourcesStepJpaTest extends BaseJpaR4Test {
.boxed()
.map(t -> {
Patient p = new Patient();
p.getMeta().addTag().setSystem("http://static").setCode("tag");
p.getMeta().addTag().setSystem("http://dynamic").setCode("tag" + t);
p.getMeta().addTag().setSystem("http://static").setCode("tag").setUserSelected(true).setVersion("1");
p.getMeta().addTag().setSystem("http://dynamic").setCode("tag" + t).setUserSelected(true).setVersion("1");
return myPatientDao.create(p, mySrd).getId().getIdPartAsLong();
}).toList();
assertEquals(count, ids.size());
@ -89,10 +89,8 @@ public class ExpandResourcesStepJpaTest extends BaseJpaR4Test {
verify(mySink, times(1)).accept(myWorkChunkCaptor.capture());
ExpandedResourcesList expandedResourceList = myWorkChunkCaptor.getValue();
assertEquals(10, expandedResourceList.getStringifiedResources().size());
assertThat(expandedResourceList.getStringifiedResources().get(0), containsString("{\"system\":\"http://static\",\"code\":\"tag\"}"));
assertThat(expandedResourceList.getStringifiedResources().get(0), containsString("{\"system\":\"http://dynamic\",\"code\":\"tag0\"}"));
assertThat(expandedResourceList.getStringifiedResources().get(1), containsString("{\"system\":\"http://static\",\"code\":\"tag\"}"));
assertThat(expandedResourceList.getStringifiedResources().get(1), containsString("{\"system\":\"http://dynamic\",\"code\":\"tag1\"}"));
assertThat(expandedResourceList.getStringifiedResources().get(0), containsString("{\"system\":\"http://static\",\"version\":\"1\",\"code\":\"tag\",\"userSelected\":true}"));
assertThat(expandedResourceList.getStringifiedResources().get(1), containsString("{\"system\":\"http://static\",\"version\":\"1\",\"code\":\"tag\",\"userSelected\":true}"));
// Verify query counts
assertEquals(theExpectedSelectQueries, myCaptureQueriesListener.countSelectQueries());

View File

@ -78,12 +78,14 @@ public class BaseHapiFhirDaoTest {
TagTypeEnum theEnum,
String theScheme,
String theTerm,
String theLabel) {
String theLabel,
String theVersion,
Boolean theUserSelected ) {
// we need to init synchronization due to what
// the underlying class is doing
try {
TransactionSynchronizationManager.initSynchronization();
return super.getTagOrNull(theDetails, theEnum, theScheme, theTerm, theLabel);
return super.getTagOrNull(theDetails, theEnum, theScheme, theTerm, theLabel, theVersion, theUserSelected);
} finally {
TransactionSynchronizationManager.clearSynchronization();
}
@ -176,12 +178,13 @@ public class BaseHapiFhirDaoTest {
String scheme = "http://localhost";
String term = "code123";
String label = "hollow world";
String version = "v1.0";
Boolean userSelected = true;
String raceConditionError = "Entity exists; if this is logged, you have race condition issues!";
TagDefinition tagDefinition = new TagDefinition(tagType,
scheme,
term,
label);
TagDefinition tagDefinition = new TagDefinition(tagType, scheme, term, label);
tagDefinition.setVersion(version);
tagDefinition.setUserSelected(userSelected);
// mock objects
CriteriaBuilder builder = getMockedCriteriaBuilder();
@ -271,7 +274,7 @@ public class BaseHapiFhirDaoTest {
Runnable task = () -> {
latch.countDown();
try {
TagDefinition retTag = myTestDao.getTagOrNull(new TransactionDetails(), tagType, scheme, term, label);
TagDefinition retTag = myTestDao.getTagOrNull(new TransactionDetails(), tagType, scheme, term, label, version, userSelected);
outcomes.put(retTag.hashCode(), retTag);
counter.incrementAndGet();
} catch (Exception ex) {
@ -331,6 +334,8 @@ public class BaseHapiFhirDaoTest {
String scheme = "http://localhost";
String term = "code123";
String label = "hollow world";
String version = "v1.0";
Boolean userSelected = true;
TransactionDetails transactionDetails = new TransactionDetails();
String exMsg = "Hi there";
String readError = "No read for you";
@ -359,7 +364,7 @@ public class BaseHapiFhirDaoTest {
// test
try {
myTestDao.getTagOrNull(transactionDetails, tagType, scheme, term, label);
myTestDao.getTagOrNull(transactionDetails, tagType, scheme, term, label, version, userSelected);
fail();
} catch (Exception ex) {
// verify

View File

@ -1,12 +1,17 @@
package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.api.config.JpaStorageSettings;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTag;
import ca.uhn.fhir.jpa.model.entity.TagDefinition;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.api.server.SystemRequestDetails;
import ca.uhn.fhir.rest.param.TokenParam;
import ca.uhn.fhir.system.HapiTestSystemProperties;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.InstantType;
import org.hl7.fhir.r4.model.Meta;
import org.hl7.fhir.r4.model.Patient;
@ -14,12 +19,16 @@ import org.hl7.fhir.r4.model.StringType;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@ -29,7 +38,9 @@ import java.util.stream.Collectors;
import static ca.uhn.fhir.rest.api.Constants.PARAM_TAG;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
@ -142,6 +153,163 @@ public class FhirResourceDaoR4MetaTest extends BaseJpaR4Test {
assertEquals("bar", patient2.getMeta().getSecurityFirstRep().getCode());
}
@Nested
public class TestTagWithVersionAndUserSelected {
private static final String expectedSystem1 = "http://foo";
private static final String expectedCode1 = "code1";
private static final String expectedVersion1 = "testVersion1";
private static final String expectedDisplay1 = "Test1";
private static final boolean expectedUserSelected1 = true;
private static final String expectedSystem2 = "http://another.system";
private static final String expectedCode2 = "code2";
private static final String expectedVersion2 = "testVersion2";
private static final String expectedDisplay2 = "Test2";
private static final boolean expectedUserSelected2 = false;
private static final class TagQtyExpectations {
public int theTagQty;
public int theTagV1Qty;
public int theTagV2Qty;
public TagQtyExpectations(int theTheTagV1Qty, int theTheTagV2Qty) {
theTagQty = theTheTagV1Qty + theTheTagV2Qty;
theTagV1Qty = theTheTagV1Qty;
theTagV2Qty = theTheTagV2Qty;
}
}
private static final Map<JpaStorageSettings.TagStorageModeEnum, TagQtyExpectations> expectedTagCountMap = Map.of(
JpaStorageSettings.TagStorageModeEnum.INLINE, new TagQtyExpectations(0, 1),
JpaStorageSettings.TagStorageModeEnum.NON_VERSIONED, new TagQtyExpectations(1, 1),
JpaStorageSettings.TagStorageModeEnum.VERSIONED, new TagQtyExpectations(1, 1)
);
private static final Map<JpaStorageSettings.TagStorageModeEnum, TagQtyExpectations> expectedHistoryTagCountMap = Map.of(
JpaStorageSettings.TagStorageModeEnum.INLINE, new TagQtyExpectations(0, 0),
JpaStorageSettings.TagStorageModeEnum.NON_VERSIONED, new TagQtyExpectations(0, 0),
JpaStorageSettings.TagStorageModeEnum.VERSIONED, new TagQtyExpectations(2, 1)
);
@ParameterizedTest
@EnumSource(JpaStorageSettings.TagStorageModeEnum.class)
public void testAddTagWithVersionAndUserSelected(JpaStorageSettings.TagStorageModeEnum theTagStorageModeEnum) {
myStorageSettings.setTagStorageMode(theTagStorageModeEnum);
final Patient savedPatient = new Patient();
final Coding newTag = savedPatient.getMeta()
.addTag()
.setSystem(expectedSystem1)
.setCode(expectedCode1)
.setDisplay(expectedDisplay1);
assertFalse(newTag.getUserSelected());
newTag.setVersion(expectedVersion1)
.setUserSelected(expectedUserSelected1);
savedPatient.setActive(true);
final IIdType pid1 = myPatientDao.create(savedPatient, new SystemRequestDetails()).getId().toVersionless();
final Patient retrievedPatient = myPatientDao.read(pid1, new SystemRequestDetails());
validateSavedPatientTags(retrievedPatient);
// Update the patient to create a ResourceHistoryTag record
final List<Coding> tagsFromDbPatient = retrievedPatient.getMeta().getTag();
assertEquals(1, tagsFromDbPatient.size());
tagsFromDbPatient.get(0)
.setCode(expectedCode2)
.setSystem(expectedSystem2)
.setVersion(expectedVersion2)
.setDisplay(expectedDisplay2)
.setUserSelected(expectedUserSelected2);
myPatientDao.update(retrievedPatient, new SystemRequestDetails());
final Patient retrievedUpdatedPatient = myPatientDao.read(pid1, new SystemRequestDetails());
final Meta meta = retrievedUpdatedPatient.getMeta();
final List<Coding> tags = meta.getTag();
tags.forEach(innerTag -> ourLog.info("TAGS: version: {}, userSelected: {}, code: {}, display: {}, system: {}",
innerTag.getVersion(), innerTag.getUserSelected(), innerTag.getCode(), innerTag.getDisplay(), innerTag.getSystem()));
final Coding tagFirstRep = meta.getTagFirstRep();
ourLog.info("TAG FIRST REP: version: {}, userSelected: {}, code: {}, display: {}, system: {}",
tagFirstRep.getVersion(), tagFirstRep.getUserSelected(), tagFirstRep.getCode(), tagFirstRep.getDisplay(), tagFirstRep.getSystem());
TagQtyExpectations expectedCounts = expectedTagCountMap.get(theTagStorageModeEnum);
validateUpdatedPatientTags(expectedCounts, tags);
final List<ResourceHistoryTag> resourceHistoryTags2 = myResourceHistoryTagDao.findAll();
resourceHistoryTags2.forEach(historyTag -> {
final TagDefinition tag = historyTag.getTag();
ourLog.info("tagId: {}, resourceId: {}, version: {}, userSelected: {}, system: {}, code: {}, display: {}",
historyTag.getTagId(), historyTag.getResourceId(), /*tag.getVersion()*/ null, /*tag.getUserSelected()*/ null, tag.getSystem(), tag.getCode(), tag.getDisplay());
});
TagQtyExpectations expectedHistoryCounts = expectedHistoryTagCountMap.get(theTagStorageModeEnum);
validateHistoryTags(expectedHistoryCounts, resourceHistoryTags2);
}
private void validateSavedPatientTags(Patient thePatient) {
assertAll(
() -> assertEquals(1, thePatient.getMeta().getTag().size()),
() -> assertEquals(0, thePatient.getMeta().getSecurity().size()),
() -> assertEquals(0, thePatient.getMeta().getProfile().size()),
() -> assertEquals(expectedSystem1, thePatient.getMeta().getTagFirstRep().getSystem()),
() -> assertTrue(thePatient.getMeta().getTagFirstRep().getUserSelected()),
() -> assertEquals(expectedCode1, thePatient.getMeta().getTagFirstRep().getCode()),
() -> assertEquals(expectedVersion1, thePatient.getMeta().getTagFirstRep().getVersion()),
() -> assertEquals(expectedUserSelected1, thePatient.getMeta().getTagFirstRep().getUserSelected())
);
}
private void validateUpdatedPatientTags(TagQtyExpectations theExpectedCounts, List<Coding> tags) {
assertAll(
() -> assertEquals(theExpectedCounts.theTagQty, tags.size()),
() -> assertEquals(theExpectedCounts.theTagV1Qty, tags.stream()
.filter(tag -> expectedSystem1.equals(tag.getSystem()))
.filter(tag -> expectedCode1.equals(tag.getCode()))
.filter(tag -> expectedDisplay1.equals(tag.getDisplay()))
.filter(tag -> expectedVersion1.equals(tag.getVersion()))
.filter(tag -> expectedUserSelected1 == tag.getUserSelected())
.count()),
() -> assertEquals(theExpectedCounts.theTagV2Qty, tags.stream()
.filter(tag -> expectedSystem2.equals(tag.getSystem()))
.filter(tag -> expectedCode2.equals(tag.getCode()))
.filter(tag -> expectedDisplay2.equals(tag.getDisplay()))
.filter(tag -> expectedVersion2.equals(tag.getVersion()))
.filter(tag -> expectedUserSelected2 == tag.getUserSelected())
.count())
);
}
private void validateHistoryTags(TagQtyExpectations theExpectedHistoryCounts, List<ResourceHistoryTag> theHistoryTags) {
// validating this way because tags are a set so can be in any order
assertAll(
() -> assertEquals(theExpectedHistoryCounts.theTagQty, theHistoryTags.size()),
() -> assertEquals(theExpectedHistoryCounts.theTagV1Qty, theHistoryTags.stream()
.filter(resourceHistoryTag -> expectedSystem1.equals(resourceHistoryTag.getTag().getSystem()))
.filter(resourceHistoryTag -> expectedCode1.equals(resourceHistoryTag.getTag().getCode()))
.filter(resourceHistoryTag -> expectedDisplay1.equals(resourceHistoryTag.getTag().getDisplay()))
.filter(resourceHistoryTag -> expectedVersion1.equals(resourceHistoryTag.getTag().getVersion()))
.filter(resourceHistoryTag -> expectedUserSelected1 == resourceHistoryTag.getTag().getUserSelected())
.count()),
() -> assertEquals(theExpectedHistoryCounts.theTagV2Qty, theHistoryTags.stream()
.filter(resourceHistoryTag -> expectedSystem2.equals(resourceHistoryTag.getTag().getSystem()))
.filter(resourceHistoryTag -> expectedCode2.equals(resourceHistoryTag.getTag().getCode()))
.filter(resourceHistoryTag -> expectedDisplay2.equals(resourceHistoryTag.getTag().getDisplay()))
.filter(resourceHistoryTag -> expectedVersion2.equals(resourceHistoryTag.getTag().getVersion()))
.filter(resourceHistoryTag -> expectedUserSelected2 == resourceHistoryTag.getTag().getUserSelected())
.count())
);
}
}
@Disabled // TODO JA: This test fails regularly, need to get a dedicated connection pool for tag creation
@Test

View File

@ -47,13 +47,18 @@ class MemoryCacheServiceTest {
String system = "http://example.com";
TagTypeEnum type = TagTypeEnum.TAG;
String code = "t";
String version = "Ver 3.0";
Boolean userSelected = true;
MemoryCacheService.TagDefinitionCacheKey cacheKey = new MemoryCacheService.TagDefinitionCacheKey(type, system, code);
MemoryCacheService.TagDefinitionCacheKey cacheKey = new MemoryCacheService.TagDefinitionCacheKey(
type, system, code, version, userSelected);
TagDefinition retVal = mySvc.getIfPresent(MemoryCacheService.CacheEnum.TAG_DEFINITION, cacheKey);
assertThat(retVal, nullValue());
TagDefinition tagDef = new TagDefinition(type, system, code, "theLabel");
tagDef.setVersion(version);
tagDef.setUserSelected(userSelected);
mySvc.put(MemoryCacheService.CacheEnum.TAG_DEFINITION, cacheKey, tagDef);
retVal = mySvc.getIfPresent(MemoryCacheService.CacheEnum.TAG_DEFINITION, cacheKey);

View File

@ -216,16 +216,22 @@ public class MemoryCacheService {
private final TagTypeEnum myType;
private final String mySystem;
private final String myCode;
private final String myVersion;
private Boolean myUserSelected;
private final int myHashCode;
public TagDefinitionCacheKey(TagTypeEnum theType, String theSystem, String theCode) {
public TagDefinitionCacheKey(TagTypeEnum theType, String theSystem, String theCode, String theVersion, Boolean theUserSelected) {
myType = theType;
mySystem = theSystem;
myCode = theCode;
myVersion = theVersion;
myUserSelected = theUserSelected;
myHashCode = new HashCodeBuilder(17, 37)
.append(myType)
.append(mySystem)
.append(myCode)
.append(myVersion)
.append(myUserSelected)
.toHashCode();
}

View File

@ -222,7 +222,7 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
@Override
public ValidationResult validateCode(ValidationOptions theOptions, String code, ValueSet vs) {
ValidationOptions options = theOptions.guessSystem();
ValidationOptions options = theOptions.withGuessSystem();
return validateCode(options, null, code, null, vs);
}

View File

@ -90,6 +90,16 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
}
}
@Override
public CodeSystem fetchSupplementedCodeSystem(String theS) {
return null;
}
@Override
public CodeSystem fetchSupplementedCodeSystem(String theS, String theS1) {
return null;
}
@Override
public List<String> getResourceNames() {
@ -131,7 +141,7 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
}
}
return new ValidationResult(IssueSeverity.ERROR, null);
return new ValidationResult(IssueSeverity.ERROR, null, null);
}
@Override
@ -170,7 +180,7 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
severity = IssueSeverity.fromCode(result.getSeverityCode());
}
ConceptDefinitionComponent definition = new ConceptDefinitionComponent().setCode(result.getCode());
return new ValidationResult(severity, result.getMessage(), theSystem, definition);
return new ValidationResult(severity, result.getMessage(), theSystem, definition, null, null);
}
@Override
@ -189,11 +199,11 @@ public final class HapiWorkerContext extends I18nBase implements IWorkerContext
ConceptDefinitionComponent definition = new ConceptDefinitionComponent();
definition.setCode(theCode);
definition.setDisplay(outcome.getDisplay());
return new ValidationResult(theSystem, definition);
return new ValidationResult(theSystem, definition, null);
}
return new ValidationResult(IssueSeverity.ERROR, "Unknown code[" + theCode + "] in system[" +
Constants.codeSystemWithDefaultDescription(theSystem) + "]");
Constants.codeSystemWithDefaultDescription(theSystem) + "]", null);
}
@Override

View File

@ -244,15 +244,16 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
if (isNotBlank(code)) {
retVal = new ValidationResult(theSystem, new org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent()
.setCode(code)
.setDisplay(display));
.setDisplay(display),
null);
} else if (isNotBlank(issueSeverity)) {
retVal = new ValidationResult(ValidationMessage.IssueSeverity.fromCode(issueSeverity), message, ValueSetExpander.TerminologyServiceErrorClass.UNKNOWN);
retVal = new ValidationResult(ValidationMessage.IssueSeverity.fromCode(issueSeverity), message, ValueSetExpander.TerminologyServiceErrorClass.UNKNOWN, null);
}
}
if (retVal == null) {
retVal = new ValidationResult(ValidationMessage.IssueSeverity.ERROR, "Validation failed");
retVal = new ValidationResult(ValidationMessage.IssueSeverity.ERROR, "Validation failed", null);
}
return retVal;
@ -329,6 +330,16 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
}
}
@Override
public CodeSystem fetchSupplementedCodeSystem(String system) {
return null;
}
@Override
public CodeSystem fetchSupplementedCodeSystem(String system, String version) {
return null;
}
@Override
public <T extends Resource> T fetchResource(Class<T> class_, String uri) {
@ -558,7 +569,7 @@ public class VersionSpecificWorkerContextWrapper extends I18nBase implements IWo
return validationResultsOk.get(0);
}
return new ValidationResult(ValidationMessage.IssueSeverity.ERROR, null);
return new ValidationResult(ValidationMessage.IssueSeverity.ERROR, null, null);
}
public void invalidateCaches() {

View File

@ -1098,7 +1098,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
assertEquals(2, errors.getMessages().size());
assertThat(errors.getMessages().get(0).getMessage(), containsString("A code with no system has no defined meaning. A system should be provided"));
assertThat(errors.getMessages().get(0).getLocationString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
assertThat(errors.getMessages().get(1).getMessage(), containsString("The value provided (null::code1) is not in the options value set in the questionnaire"));
assertThat(errors.getMessages().get(1).getMessage(), containsString("The code provided code1 in the system null) is not in the options value set (ValueSet[http://somevalueset]) in the questionnaire: Validation failed"));
assertThat(errors.getMessages().get(1).getLocationString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
qa = new QuestionnaireResponse();
@ -1111,7 +1111,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
assertEquals(2, errors.getMessages().size());
assertThat(errors.getMessages().get(0).getMessage(), containsString("A code with no system has no defined meaning. A system should be provided"));
assertThat(errors.getMessages().get(0).getLocationString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
assertThat(errors.getMessages().get(1).getMessage(), containsString("The value provided (null::code1) is not in the options value set in the questionnaire"));
assertThat(errors.getMessages().get(1).getMessage(), containsString("The code provided code1 in the system null) is not in the options value set (ValueSet[http://somevalueset]) in the questionnaire: Validation failed"));
assertThat(errors.getMessages().get(1).getLocationString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
qa = new QuestionnaireResponse();
@ -1120,7 +1120,7 @@ public class QuestionnaireResponseValidatorDstu3Test {
qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("http://system").setCode(null));
errors = myVal.validateWithResult(qa);
ourLog.info(errors.toString());
assertThat(errors.toString(), containsString("The value provided (http://system::null) is not in the options value set in the questionnaire"));
assertThat(errors.toString(), containsString("ValidationResult{messageCount=1, isSuccessful=false, description="));
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
// Wrong type

View File

@ -44,7 +44,7 @@ public class CustomResourceGenerationTest extends BaseTest {
assertEquals(3, result.getMessages().size());
assertEquals("Error parsing JSON: the primitive value must be a boolean", result.getMessages().get(0).getMessage());
assertEquals("This property must be an Array, not a Primitive property", result.getMessages().get(1).getMessage());
assertEquals("The property name must be a JSON Array, not a Primitive property (at CustomResource)", result.getMessages().get(1).getMessage());
assertEquals("Unrecognized property 'id1'", result.getMessages().get(2).getMessage());
}

View File

@ -432,7 +432,7 @@ public class FhirInstanceValidatorR4Test extends BaseTest {
ValidationResult result = val.validateWithResult(operationDefinition);
List<SingleValidationMessage> all = logResultsAndReturnAll(result);
assertFalse(result.isSuccessful());
assertEquals("This property must be an Array, not a Primitive property", all.get(0).getMessage());
assertEquals("The property resource must be a JSON Array, not a Primitive property (at OperationDefinition)", all.get(0).getMessage());
}
@Test

View File

@ -57,7 +57,7 @@ public class HapiWorkerContextTest extends BaseTest {
// Built-in Codes
vs.setUrl("http://hl7.org/fhir/ValueSet/fm-status");
ValidationOptions options = new ValidationOptions().guessSystem();
ValidationOptions options = new ValidationOptions().withGuessSystem();
outcome = workerCtx.validateCode(options, "active", vs);
assertEquals(true, outcome.isOk(), outcome.getMessage());

View File

@ -609,7 +609,7 @@ public class QuestionnaireResponseValidatorR4Test {
errors = myVal.validateWithResult(qa);
ourLog.info(errors.toString());
// This is set in InstanceValidator#validateAnswerCode
assertThat(errors.toString(), containsString(" The value provided (http://system::null) is not in the options value set"));
assertThat(errors.toString(), containsString("ValidationResult{messageCount=1, isSuccessful=false, description='"));
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
// Wrong type

View File

@ -377,7 +377,7 @@ public class FhirInstanceValidatorR4BTest extends BaseTest {
ValidationResult result = val.validateWithResult(operationDefinition);
List<SingleValidationMessage> all = logResultsAndReturnAll(result);
assertFalse(result.isSuccessful());
assertEquals("This property must be an Array, not a Primitive property", all.get(0).getMessage());
assertEquals("The property resource must be a JSON Array, not a Primitive property (at OperationDefinition)", all.get(0).getMessage());
}
@Test

View File

@ -581,7 +581,7 @@ public class QuestionnaireResponseValidatorR5Test {
errors = myVal.validateWithResult(qa);
ourLog.info(errors.toString());
// This is set in InstanceValidator#validateAnswerCode
assertThat(errors.toString(), containsString(" The value provided (http://system::null) is not in the options value set"));
assertThat(errors.toString(), containsString("ValidationResult{messageCount=1, isSuccessful=false, description="));
assertThat(errors.toString(), containsString("QuestionnaireResponse.item[0].answer[0]"));
// Wrong type

View File

@ -873,7 +873,7 @@
</licenses>
<properties>
<fhir_core_version>5.6.117</fhir_core_version>
<fhir_core_version>6.0.1</fhir_core_version>
<ucum_version>1.0.3</ucum_version>
<surefire_jvm_args>-Dfile.encoding=UTF-8 -Xmx2048m</surefire_jvm_args>