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 8ea73872735..5d3fbd43144 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 @@ -1538,9 +1538,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, codeSystem = new TermCodeSystem(); } codeSystem.setResource(theCodeSystemVersion.getResource()); - codeSystem.setCodeSystemUri(theSystemUri); - codeSystem.setName(theSystemName); - myCodeSystemDao.save(codeSystem); } else { if (!ObjectUtil.equals(codeSystem.getResource().getId(), theCodeSystemVersion.getResource().getId())) { String msg = myContext.getLocalizer().getMessage(BaseHapiTerminologySvcImpl.class, "cannotCreateDuplicateCodeSystemUrl", theSystemUri, @@ -1548,6 +1545,11 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, throw new UnprocessableEntityException(msg); } } + + codeSystem.setCodeSystemUri(theSystemUri); + codeSystem.setName(theSystemName); + codeSystem = myCodeSystemDao.save(codeSystem); + theCodeSystemVersion.setCodeSystem(codeSystem); theCodeSystemVersion.setCodeSystemDisplayName(theSystemName); @@ -1626,6 +1628,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, } @Override + @Transactional(propagation = Propagation.MANDATORY) public void storeNewCodeSystemVersionIfNeeded(CodeSystem theCodeSystem, ResourceTable theResourceEntity) { if (theCodeSystem != null && isNotBlank(theCodeSystem.getUrl())) { String codeSystemUrl = theCodeSystem.getUrl(); @@ -1633,20 +1636,13 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, ourLog.info("CodeSystem {} has a status of {}, going to store concepts in terminology tables", theResourceEntity.getIdDt().getValue(), theCodeSystem.getContentElement().getValueAsString()); Long codeSystemResourcePid = getCodeSystemResourcePid(theCodeSystem.getIdElement()); - TermCodeSystemVersion persCs = myCodeSystemVersionDao.findCurrentVersionForCodeSystemResourcePid(codeSystemResourcePid); - if (persCs != null) { - ourLog.info("Code system version already exists in database"); - } else { + TermCodeSystemVersion persCs = new TermCodeSystemVersion(); - persCs = new TermCodeSystemVersion(); - populateCodeSystemVersionProperties(persCs, theCodeSystem, theResourceEntity); - - persCs.getConcepts().addAll(toPersistedConcepts(theCodeSystem.getConcept(), persCs)); - ourLog.info("Code system has {} concepts", persCs.getConcepts().size()); - storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, theCodeSystem.getName(), theCodeSystem.getVersion(), persCs); - - } + populateCodeSystemVersionProperties(persCs, theCodeSystem, theResourceEntity); + persCs.getConcepts().addAll(toPersistedConcepts(theCodeSystem.getConcept(), persCs)); + ourLog.info("Code system has {} concepts", persCs.getConcepts().size()); + storeNewCodeSystemVersion(codeSystemResourcePid, codeSystemUrl, theCodeSystem.getName(), theCodeSystem.getVersion(), persCs); } } } diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java index bceb5f6c365..467893c6964 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/ResourceProviderR4ValueSetTest.java @@ -612,6 +612,59 @@ public class ResourceProviderR4ValueSetTest extends BaseResourceProviderR4Test { } } + @Test + public void testExpandValueSetBasedOnCodeSystemWithChangedUrl() throws IOException { + + CodeSystem cs = new CodeSystem(); + cs.setId("CodeSystem/CS"); + cs.setContent(CodeSystemContentMode.COMPLETE); + cs.setUrl("http://foo1"); + cs.addConcept().setCode("foo1").setDisplay("foo1"); + ourClient.update().resource(cs).execute(); + + ValueSet vs = new ValueSet(); + vs.setId("ValueSet/VS179789"); + vs.setUrl("http://bar"); + vs.getCompose().addInclude().setSystem("http://foo1").addConcept().setCode("foo1"); + ourClient.update().resource(vs).execute(); + + ValueSet expanded = ourClient + .operation() + .onInstance(new IdType("ValueSet/VS179789")) + .named("$expand") + .withNoParameters(Parameters.class) + .returnResourceType(ValueSet.class) + .execute(); + ourLog.info("Expanded: {}",myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expanded)); + assertEquals(1, expanded.getExpansion().getContains().size()); + + // Update the CodeSystem URL and Codes + cs = new CodeSystem(); + cs.setId("CodeSystem/CS"); + cs.setContent(CodeSystemContentMode.COMPLETE); + cs.setUrl("http://foo2"); + cs.addConcept().setCode("foo2").setDisplay("foo2"); + ourClient.update().resource(cs).execute(); + + vs = new ValueSet(); + vs.setId("ValueSet/VS179789"); + vs.setUrl("http://bar"); + vs.getCompose().addInclude().setSystem("http://foo2").addConcept().setCode("foo2"); + ourClient.update().resource(vs).execute(); + + expanded = ourClient + .operation() + .onInstance(new IdType("ValueSet/VS179789")) + .named("$expand") + .withNoParameters(Parameters.class) + .returnResourceType(ValueSet.class) + .execute(); + ourLog.info("Expanded: {}",myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(expanded)); + assertEquals(1, expanded.getExpansion().getContains().size()); + } + + + /** * #516 */ diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/ValidationSupportChain.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/ValidationSupportChain.java index eb01365d1a7..1f80bcfb382 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/ValidationSupportChain.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/r4/hapi/validation/ValidationSupportChain.java @@ -49,7 +49,9 @@ public class ValidationSupportChain implements IValidationSupport { @Override public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theCtx, ConceptSetComponent theInclude) { for (IValidationSupport next : myChain) { - if (next.isCodeSystemSupported(theCtx, theInclude.getSystem())) { + boolean codeSystemSupported = next.isCodeSystemSupported(theCtx, theInclude.getSystem()); + ourLog.trace("Support {} supports: {}", next, codeSystemSupported); + if (codeSystemSupported) { ValueSetExpander.ValueSetExpansionOutcome expansion = next.expandValueSet(theCtx, theInclude); if (expansion != null) { return expansion; @@ -57,7 +59,7 @@ public class ValidationSupportChain implements IValidationSupport { } } - throw new InvalidRequestException("unable to find code system " + theInclude.getSystem()); + throw new InvalidRequestException("Unable to find code system " + theInclude.getSystem()); } @Override diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireResponseValidatorR4Test.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireResponseValidatorR4Test.java index f2ceee7e2e5..10d36d46a35 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireResponseValidatorR4Test.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/r4/validation/QuestionnaireResponseValidatorR4Test.java @@ -633,6 +633,57 @@ public class QuestionnaireResponseValidatorR4Test { } + + @Test + public void testOpenchoiceAnswerWithOptions() { + String questionnaireRef = "http://example.com/Questionnaire/q1"; + + List options = new ArrayList<>(); + options.add(new Questionnaire.QuestionnaireItemAnswerOptionComponent().setValue(new Coding("http://foo", "foo", "FOOOO"))); + options.add(new Questionnaire.QuestionnaireItemAnswerOptionComponent().setValue(new Coding("http://bar", "bar", "FOOOO"))); + + Questionnaire q = new Questionnaire(); + QuestionnaireItemComponent item = q.addItem(); + item.setLinkId("link0") + .setRequired(true) + .setType(QuestionnaireItemType.OPENCHOICE) + .setAnswerOption(options); + when(myValSupport.fetchResource(any(FhirContext.class), eq(Questionnaire.class), eq(questionnaireRef))).thenReturn(q); + + QuestionnaireResponse qa; + ValidationResult errors; + + qa = new QuestionnaireResponse(); + qa.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); + qa.getQuestionnaireElement().setValue(questionnaireRef); + qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("http://foo").setCode("foo")); + errors = myVal.validateWithResult(qa); + ourLog.info(errors.toString()); + assertEquals(true, errors.isSuccessful()); + + qa = new QuestionnaireResponse(); + qa.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); + qa.getQuestionnaireElement().setValue(questionnaireRef); + qa.addItem().setLinkId("link0").addAnswer().setValue(new StringType("Hello")); + errors = myVal.validateWithResult(qa); + ourLog.info(errors.toString()); + assertEquals(true, errors.isSuccessful()); + + qa = new QuestionnaireResponse(); + qa.getText().setDiv(new XhtmlNode().setValue("
AA
")).setStatus(Narrative.NarrativeStatus.GENERATED); + qa.setStatus(QuestionnaireResponseStatus.COMPLETED); + qa.getQuestionnaireElement().setValue(questionnaireRef); + qa.addItem().setLinkId("link0").addAnswer().setValue(new Coding().setSystem("http://foo").setCode("hello")); + errors = myVal.validateWithResult(qa); + ourLog.info(errors.toString()); + // This is set in InstanceValidator#validateAnswerCode + assertEquals(false, errors.isSuccessful()); + + } + + @Test public void testUnexpectedAnswer() { Questionnaire q = new Questionnaire(); diff --git a/src/changes/changes.xml b/src/changes/changes.xml index d154f2f96e6..37cb46292ba 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -137,6 +137,11 @@ Fix a failure in FhirTerser#visit when fields in model classes being visited contain custom subclasses of the expected type. + + Updating an existing CodeSystem resource with a content mode of COMPLETE did not cause the + terminology service to accurately reflect the new CodeSystem URL and/or concepts. This is now + corrected. +