From ea52c4206f01b57bc1057c771c20c354500bfc68 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 30 Jul 2020 11:57:11 +1000 Subject: [PATCH 01/11] Batch validation of codes in value sets --- .../txClient/TerminologyClientR2.java | 11 +- .../txClient/TerminologyClientR3.java | 10 +- .../txClient/TerminologyClientR4.java | 8 +- .../txClient/TerminologyClientR5.java | 6 + .../fhir/r5/context/BaseWorkerContext.java | 103 +++++++++++++++++- .../hl7/fhir/r5/context/IWorkerContext.java | 51 +++++++++ .../instance/type/ValueSetValidator.java | 50 ++++++++- 7 files changed, 230 insertions(+), 9 deletions(-) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR2.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR2.java index 4bd03e308..e626fda9e 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR2.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR2.java @@ -35,8 +35,10 @@ import java.net.URISyntaxException; import java.util.Map; import org.hl7.fhir.convertors.VersionConvertor_10_50; +import org.hl7.fhir.convertors.VersionConvertor_40_50; import org.hl7.fhir.dstu2.utils.client.FHIRToolingClient; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.Parameters; import org.hl7.fhir.r5.model.TerminologyCapabilities; @@ -116,6 +118,11 @@ public class TerminologyClientR2 implements TerminologyClient { public int getRetryCount() throws FHIRException { return client.getRetryCount(); } - - + + @Override + public Bundle validateBatch(Bundle batch) { + return (Bundle) VersionConvertor_10_50.convertResource(client.transaction((org.hl7.fhir.dstu2.model.Bundle) VersionConvertor_10_50.convertResource(batch))); + } + + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR3.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR3.java index 531f5bc6a..cd47d3bab 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR3.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR3.java @@ -35,8 +35,10 @@ import java.net.URISyntaxException; import java.util.Map; import org.hl7.fhir.convertors.VersionConvertor_30_50; +import org.hl7.fhir.convertors.VersionConvertor_40_50; import org.hl7.fhir.dstu3.utils.client.FHIRToolingClient; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.Parameters; import org.hl7.fhir.r5.model.TerminologyCapabilities; @@ -116,5 +118,11 @@ public class TerminologyClientR3 implements TerminologyClient { public int getRetryCount() throws FHIRException { return client.getRetryCount(); } - + + @Override + public Bundle validateBatch(Bundle batch) { + return (Bundle) VersionConvertor_30_50.convertResource(client.transaction((org.hl7.fhir.dstu3.model.Bundle) VersionConvertor_30_50.convertResource(batch, false)), false); + } + + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR4.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR4.java index 2b38fdfcd..8260a6dee 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR4.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR4.java @@ -38,6 +38,7 @@ import org.hl7.fhir.convertors.VersionConvertor_40_50; import org.hl7.fhir.convertors.conv40_50.TerminologyCapabilities40_50; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.r4.utils.client.FHIRToolingClient; +import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.Parameters; import org.hl7.fhir.r5.model.TerminologyCapabilities; @@ -117,5 +118,10 @@ public class TerminologyClientR4 implements TerminologyClient { public int getRetryCount() throws FHIRException { return client.getRetryCount(); } - + + @Override + public Bundle validateBatch(Bundle batch) { + return (Bundle) VersionConvertor_40_50.convertResource(client.transaction((org.hl7.fhir.r4.model.Bundle) VersionConvertor_40_50.convertResource(batch))); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR5.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR5.java index ce6ed5d3c..788339798 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR5.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/txClient/TerminologyClientR5.java @@ -35,6 +35,7 @@ import java.net.URISyntaxException; import java.util.Map; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.Parameters; @@ -110,4 +111,9 @@ public class TerminologyClientR5 implements TerminologyClient { return client.getRetryCount(); } + @Override + public Bundle validateBatch(Bundle batch) { + return client.transaction(batch); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index f72301f67..3e1a0e387 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -55,6 +55,7 @@ import org.hl7.fhir.r5.context.CanonicalResourceManager.CanonicalResourceProxy; import org.hl7.fhir.r5.context.IWorkerContext.ILoggingService.LogCategory; import org.hl7.fhir.r5.context.TerminologyCache.CacheToken; import org.hl7.fhir.r5.model.BooleanType; +import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.CanonicalResource; import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.CodeSystem; @@ -88,6 +89,8 @@ import org.hl7.fhir.r5.model.StructureMap; import org.hl7.fhir.r5.model.TerminologyCapabilities; import org.hl7.fhir.r5.model.TerminologyCapabilities.TerminologyCapabilitiesCodeSystemComponent; import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r5.model.Bundle.BundleType; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; @@ -111,6 +114,8 @@ import org.hl7.fhir.utilities.validation.ValidationOptions.ValueSetMode; import com.google.gson.JsonObject; +import ca.uhn.fhir.model.valueset.BundleEntrySearchModeEnum; + public abstract class BaseWorkerContext extends I18nBase implements IWorkerContext{ public class MetadataResourceVersionComparator implements Comparator { @@ -685,10 +690,99 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte return validateCode(options.guessSystem(), c, vs); } + + @Override + public void validateCodeBatch(ValidationOptions options, List codes, ValueSet vs) { + if (options == null) { + options = ValidationOptions.defaults(); + } + // 1st pass: what is in the cache? + // 2nd pass: What can we do internally + // 3rd pass: hit the server + for (CodingValidationRequest t : codes) { + t.setCacheToken(txCache != null ? txCache.generateValidationToken(options, t.getCoding(), vs) : null); + if (txCache != null) { + t.setResult(txCache.getValidation(t.getCacheToken())); + } + } + if (options.isUseClient()) { + for (CodingValidationRequest t : codes) { + if (!t.hasResult()) { + try { + ValueSetCheckerSimple vsc = new ValueSetCheckerSimple(options, vs, this); + ValidationResult res = vsc.validateCode(t.getCoding()); + if (txCache != null) + txCache.cacheValidation(t.getCacheToken(), res, TerminologyCache.TRANSIENT); + t.setResult(res); + } catch (Exception e) { + } + } + } + } + + for (CodingValidationRequest t : codes) { + if (!t.hasResult()) { + if (!options.isUseServer()) { + t.setResult(new ValidationResult(IssueSeverity.WARNING,formatMessage(I18nConstants.UNABLE_TO_VALIDATE_CODE_WITHOUT_USING_SERVER), TerminologyServiceErrorClass.BLOCKED_BY_OPTIONS)); + } else if (unsupportedCodeSystems.contains(t.getCoding().getSystem())) { + t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.TERMINOLOGY_TX_SYSTEM_NOTKNOWN, t.getCoding().getSystem()), TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED)); + } else if (noTerminologyServer) { + t.setResult(new ValidationResult(IssueSeverity.ERROR,formatMessage(I18nConstants.ERROR_VALIDATING_CODE_RUNNING_WITHOUT_TERMINOLOGY_SERVICES), TerminologyServiceErrorClass.NOSERVICE)); + } + } + } + + if (expParameters == null) + throw new Error(formatMessage(I18nConstants.NO_EXPANSIONPROFILE_PROVIDED)); + // for those that that failed, we try to validate on the server + Bundle batch = new Bundle(); + batch.setType(BundleType.BATCH); + Set systems = new HashSet<>(); + for (CodingValidationRequest t : codes) { + if (!t.hasResult()) { + Parameters pIn = new Parameters(); + pIn.addParameter().setName("coding").setValue(t.getCoding()); + if (options.isGuessSystem()) + pIn.addParameter().setName("implySystem").setValue(new BooleanType(true)); + if (vs != null) { + pIn.addParameter().setName("valueSet").setResource(vs); + } + pIn.addParameter().setName("profile").setResource(expParameters); + setTerminologyOptions(options, pIn); + BundleEntryComponent be = batch.addEntry(); + be.setResource(pIn); + be.getRequest().setUrl("ValueSet/$validate-code"); + be.setUserData("source", t); + systems.add(t.getCoding().getSystem()); + } + } + if (batch.getEntry().size() > 0) { + tlog("$batch validate for "+batch.getEntry().size()+" codes on systems "+systems.toString()); + if (txClient == null) { + throw new FHIRException(formatMessage(I18nConstants.ATTEMPT_TO_USE_TERMINOLOGY_SERVER_WHEN_NO_TERMINOLOGY_SERVER_IS_AVAILABLE)); + } + if (txLog != null) { + txLog.clearLastId(); + } + Bundle resp = txClient.validateBatch(batch); + for (int i = 0; i < batch.getEntry().size(); i++) { + CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source"); + BundleEntryComponent r = resp.getEntry().get(i); + if (r.getResponse().getStatus().startsWith("2")) { + t.setResult(new ValidationResult(IssueSeverity.ERROR, getResponseText(r.getResource())).setTxLink(txLog == null ? null : txLog.getLastId())); + } else { + t.setResult(processValidationResult((Parameters) r.getResource())); + } + } + } + } + + private String getResponseText(Resource resource) { + return "Todo"; + } + @Override public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs) { - assert options != null; - if (options == null) { options = ValidationOptions.defaults(); } @@ -813,6 +907,10 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte pOut = txClient.validateCS(pin); else pOut = txClient.validateVS(pin); + return processValidationResult(pOut); + } + + public ValidationResult processValidationResult(Parameters pOut) { boolean ok = false; String message = "No Message returned"; String display = null; @@ -1622,4 +1720,5 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte return txClient; } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java index 1e16c41ab..0d31f3d3f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/IWorkerContext.java @@ -44,6 +44,8 @@ import org.fhir.ucum.UcumService; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.TerminologyServiceException; +import org.hl7.fhir.r5.context.IWorkerContext.CodingValidationRequest; +import org.hl7.fhir.r5.context.TerminologyCache.CacheToken; import org.hl7.fhir.r5.formats.IParser; import org.hl7.fhir.r5.formats.ParserType; import org.hl7.fhir.r5.model.Bundle; @@ -93,6 +95,53 @@ import com.google.gson.JsonSyntaxException; */ public interface IWorkerContext { + public class CodingValidationRequest { + private Coding coding; + private ValidationResult result; + private CacheToken cacheToken; + + public CodingValidationRequest(Coding coding) { + super(); + this.coding = coding; + } + + public ValidationResult getResult() { + return result; + } + + public void setResult(ValidationResult result) { + this.result = result; + } + + public Coding getCoding() { + return coding; + } + + public boolean hasResult() { + return result != null; + } + + /** + * internal logic; external users of batch validation should ignore this property + * + * @return + */ + public CacheToken getCacheToken() { + return cacheToken; + } + + /** + * internal logic; external users of batch validation should ignore this property + * + * @param cacheToken + */ + public void setCacheToken(CacheToken cacheToken) { + this.cacheToken = cacheToken; + } + + + } + public class PackageVersion { private String id; private String version; @@ -619,6 +668,8 @@ public interface IWorkerContext { * @return */ public ValidationResult validateCode(ValidationOptions options, Coding code, ValueSet vs); + + public void validateCodeBatch(ValidationOptions options, List codes, ValueSet vs); /** * returns the recommended tla for the type (from the structure definitions) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java index 2d8631aa5..8e284a902 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java @@ -1,9 +1,11 @@ package org.hl7.fhir.validation.instance.type; +import java.util.ArrayList; import java.util.List; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.context.IWorkerContext.CodingValidationRequest; import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult; import org.hl7.fhir.r5.elementmodel.Element; import org.hl7.fhir.r5.model.CodeSystem; @@ -19,10 +21,26 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.Source; import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.validation.BaseValidator; import org.hl7.fhir.validation.TimeTracker; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.VSCodingValidationRequest; import org.hl7.fhir.validation.instance.utils.NodeStack; public class ValueSetValidator extends BaseValidator { + public class VSCodingValidationRequest extends CodingValidationRequest { + + private NodeStack stack; + + public VSCodingValidationRequest(NodeStack stack, Coding code) { + super(code); + this.stack = stack; + } + + public NodeStack getStack() { + return stack; + } + + } + public ValueSetValidator(IWorkerContext context, TimeTracker timeTracker) { super(context); source = Source.InstanceValidator; @@ -58,7 +76,6 @@ public class ValueSetValidator extends BaseValidator { private void validateValueSetInclude(List errors, Element include, NodeStack stack) { String system = include.getChildValue("system"); String version = include.getChildValue("version"); - boolean systemOk = true; List valuesets = include.getChildrenByName("valueSet"); int i = 0; for (Element ve : valuesets) { @@ -79,13 +96,31 @@ public class ValueSetValidator extends BaseValidator { List concepts = include.getChildrenByName("concept"); List filters = include.getChildrenByName("filter"); if (!Utilities.noString(system)) { + boolean systemOk = true; int cc = 0; + List batch = new ArrayList<>(); + boolean first = true; for (Element concept : concepts) { - if (systemOk && !validateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version)) { - systemOk = false; + // we treat the first differently because we want to know if tbe system is worth validating. if it is, then we batch the rest + if (first) { + systemOk = validateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version); + first = false; + } else if (systemOk) { + batch.add(prepareValidateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version)); } cc++; } + if (batch.size() > 0) { + context.validateCodeBatch(ValidationOptions.defaults(), batch, null); + for (VSCodingValidationRequest cv : batch) { + if (version == null) { + warning(errors, IssueType.BUSINESSRULE, cv.getStack().getLiteralPath(), cv.getResult().isOk(), I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE, system, cv.getCoding().getCode()); + } else { + warning(errors, IssueType.BUSINESSRULE, cv.getStack().getLiteralPath(), cv.getResult().isOk(), I18nConstants.VALUESET_INCLUDE_INVALID_CONCEPT_CODE_VER, system, version, cv.getCoding().getCode()); + } + } + } + int cf = 0; for (Element filter : filters) { if (systemOk && !validateValueSetIncludeFilter(errors, include, stack.push(filter, cf, null, null), system, version)) { @@ -121,6 +156,15 @@ public class ValueSetValidator extends BaseValidator { return true; } + private VSCodingValidationRequest prepareValidateValueSetIncludeConcept(List errors, Element concept, NodeStack stack, String system, String version) { + String code = concept.getChildValue("code"); + Coding c = new Coding(system, code, null); + if (version != null) { + c.setVersion(version); + } + return new VSCodingValidationRequest(stack, c); + } + private boolean validateValueSetIncludeFilter(List errors, Element include, NodeStack push, String system, String version) { return true; } From 1ef4e59113a9725d80137f3017b67019b8d2c34e Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 30 Jul 2020 11:58:55 +1000 Subject: [PATCH 02/11] fix path problem in comparison --- .../hl7/fhir/r5/comparison/ComparisonRenderer.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java index 4815b9967..075455058 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java @@ -146,8 +146,8 @@ public class ComparisonRenderer implements IEvaluationContext { vars.put("concepts", new StringType(new XhtmlComposer(true).compose(cs.renderConcepts(comp, "", "")))); String cnt = processTemplate(template, "CodeSystem", vars); TextFile.stringToFile(cnt, file(comp.getId()+".html")); - new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, "comparison", comp.getId() + "-union.json")), comp.getUnion()); - new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, "comparison", comp.getId() + "-intersection.json")), comp.getIntersection()); + new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-union.json")), comp.getUnion()); + new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-intersection.json")), comp.getIntersection()); } private String file(String name) throws IOException { @@ -170,8 +170,8 @@ public class ComparisonRenderer implements IEvaluationContext { vars.put("expansion", new StringType(new XhtmlComposer(true).compose(cs.renderExpansion(comp, "", "")))); String cnt = processTemplate(template, "ValueSet", vars); TextFile.stringToFile(cnt, file(comp.getId()+".html")); - new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, "comparison", comp.getId() + "-union.json")), comp.getUnion()); - new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, "comparison", comp.getId() + "-intersection.json")), comp.getIntersection()); + new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-union.json")), comp.getUnion()); + new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-intersection.json")), comp.getIntersection()); } private void renderProfile(String id, ProfileComparison comp) throws IOException { @@ -189,8 +189,8 @@ public class ComparisonRenderer implements IEvaluationContext { vars.put("structure", new StringType(new XhtmlComposer(true).compose(cs.renderStructure(comp, "", "", "http://hl7.org/fhir")))); String cnt = processTemplate(template, "CodeSystem", vars); TextFile.stringToFile(cnt, file(comp.getId()+".html")); - new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, "comparison", comp.getId() + "-union.json")), comp.getUnion()); - new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, "comparison", comp.getId() + "-intersection.json")), comp.getIntersection()); + new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-union.json")), comp.getUnion()); + new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-intersection.json")), comp.getIntersection()); } private String processTemplate(String template, String name, Map vars) { From bde55f18724a1b6679fcbf0e798e335bdd28ca0c Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 30 Jul 2020 11:59:37 +1000 Subject: [PATCH 03/11] Improve error message --- .../main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index 6a3f3f7f9..6b699f4a1 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -2596,7 +2596,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (derived.hasIsSummaryElement()) { if (!Base.compareDeep(derived.getIsSummaryElement(), base.getIsSummaryElement(), false)) { if (base.hasIsSummary() && !context.getVersion().equals("1.4.0")) // work around a known issue with some 1.4.0 cosntraints - throw new Error(context.formatMessage(I18nConstants.ERROR_IN_PROFILE__AT__BASE_ISSUMMARY___DERIVED_ISSUMMARY__, pn, derived.getPath(), base.getIsSummaryElement().asStringValue(), derived.getIsSummaryElement().asStringValue())); + throw new Error(context.formatMessage(I18nConstants.ERROR_IN_PROFILE__AT__BASE_ISSUMMARY___DERIVED_ISSUMMARY__, purl, derived.getPath(), base.getIsSummaryElement().asStringValue(), derived.getIsSummaryElement().asStringValue())); base.setIsSummaryElement(derived.getIsSummaryElement().copy()); } else if (trimDifferential) derived.setIsSummaryElement(null); From 4b5470f8257c292e109ec54e1c5ac84123ec3ae3 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 30 Jul 2020 12:00:30 +1000 Subject: [PATCH 04/11] Don't produce 2 columns for status for deprecated codes --- .../hl7/fhir/r5/renderers/CodeSystemRenderer.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java index 7214219cf..4abbe0c1b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java @@ -138,6 +138,7 @@ public class CodeSystemRenderer extends TerminologyRenderer { boolean display = false; boolean hierarchy = false; boolean version = false; + boolean ignoreStatus = false; List properties = new ArrayList<>(); for (PropertyComponent cp : cs.getProperty()) { if (showPropertyInTable(cp)) { @@ -147,12 +148,15 @@ public class CodeSystemRenderer extends TerminologyRenderer { } if (exists) { properties.add(cp); + if ("status".equals(cp.getCode())) { + ignoreStatus = true; + } } } } for (ConceptDefinitionComponent c : cs.getConcept()) { commentS = commentS || conceptsHaveComments(c); - deprecated = deprecated || conceptsHaveDeprecated(cs, c); + deprecated = deprecated || conceptsHaveDeprecated(cs, c, ignoreStatus); display = display || conceptsHaveDisplay(c); version = version || conceptsHaveVersion(c); hierarchy = hierarchy || c.hasConcept(); @@ -255,11 +259,11 @@ public class CodeSystemRenderer extends TerminologyRenderer { return false; } - private boolean conceptsHaveDeprecated(CodeSystem cs, ConceptDefinitionComponent c) { - if (CodeSystemUtilities.isDeprecated(cs, c)) + private boolean conceptsHaveDeprecated(CodeSystem cs, ConceptDefinitionComponent c, boolean ignoreStatus) { + if (CodeSystemUtilities.isDeprecated(cs, c, ignoreStatus)) return true; for (ConceptDefinitionComponent g : c.getConcept()) - if (conceptsHaveDeprecated(cs, g)) + if (conceptsHaveDeprecated(cs, g, ignoreStatus)) return true; return false; } @@ -325,7 +329,7 @@ public class CodeSystemRenderer extends TerminologyRenderer { } if (deprecated) { td = tr.td(); - Boolean b = CodeSystemUtilities.isDeprecated(cs, c); + Boolean b = CodeSystemUtilities.isDeprecated(cs, c, false); if (b != null && b) { smartAddText(td, getContext().getWorker().translator().translate("xhtml-gen-cs", "Deprecated", getContext().getLang())); hasExtensions = true; From a80d11334afc423afbcfb0fe8ebcf5f0a3598d97 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 30 Jul 2020 12:01:09 +1000 Subject: [PATCH 05/11] Fix bug producing invalid xhtml --- .../r5/renderers/ProfileDrivenRenderer.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java index 7f401337a..1ee3f4e45 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java @@ -270,7 +270,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer { return null; } - private void renderLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode x, boolean title, boolean showCodeDetails, Map displayHints, String path, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome { + private void renderLeaf(ResourceWrapper res, BaseWrapper ew, ElementDefinition defn, XhtmlNode parent, XhtmlNode x, boolean title, boolean showCodeDetails, Map displayHints, String path, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome { if (ew == null) return; @@ -361,7 +361,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer { } else { x.an(rw.getId()); ResourceRenderer rr = RendererFactory.factory(rw, context.copy().setAddGeneratedNarrativeHeader(false)); - rr.render(x.blockquote(), rw); + rr.render(parent.blockquote(), rw); } } } else { @@ -598,7 +598,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer { private void generateByProfile(ResourceWrapper res, StructureDefinition profile, BaseWrapper e, List allElements, ElementDefinition defn, List children, XhtmlNode x, String path, boolean showCodeDetails, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome { if (children.isEmpty()) { - renderLeaf(res, e, defn, x, false, showCodeDetails, readDisplayHints(defn), path, indent); + renderLeaf(res, e, defn, x, x, false, showCodeDetails, readDisplayHints(defn), path, indent); } else { for (PropertyWrapper p : splitExtensions(profile, e.children())) { if (p.hasValues()) { @@ -626,7 +626,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer { if (renderAsList(child) && p.getValues().size() > 1) { XhtmlNode list = x.ul(); for (BaseWrapper v : p.getValues()) - renderLeaf(res, v, child, list.li(), false, showCodeDetails, displayHints, path, indent); + renderLeaf(res, v, child, x, list.li(), false, showCodeDetails, displayHints, path, indent); } else { boolean first = true; for (BaseWrapper v : p.getValues()) { @@ -634,7 +634,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer { first = false; else para.tx(", "); - renderLeaf(res, v, child, para, false, showCodeDetails, displayHints, path, indent); + renderLeaf(res, v, child, x, para, false, showCodeDetails, displayHints, path, indent); } } } @@ -730,10 +730,12 @@ public class ProfileDrivenRenderer extends ResourceRenderer { private void addColumnValues(ResourceWrapper res, XhtmlNode tr, List grandChildren, BaseWrapper v, boolean showCodeDetails, Map displayHints, String path, int indent) throws FHIRException, UnsupportedEncodingException, IOException, EOperationOutcome { for (ElementDefinition e : grandChildren) { PropertyWrapper p = v.getChildByName(e.getPath().substring(e.getPath().lastIndexOf(".")+1)); + XhtmlNode td = tr.td(); if (p == null || p.getValues().size() == 0 || p.getValues().get(0) == null) - tr.td().tx(" "); - else - renderLeaf(res, p.getValues().get(0), e, tr.td(), false, showCodeDetails, displayHints, path, indent); + td.tx(" "); + else { + renderLeaf(res, p.getValues().get(0), e, td, td, false, showCodeDetails, displayHints, path, indent); + } } } From 2c1460a66387069a2acd567b6a97d88e90d12742 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 30 Jul 2020 12:01:35 +1000 Subject: [PATCH 06/11] ditto --- .../hl7/fhir/r5/terminologies/CodeSystemUtilities.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java index b7083dd89..9de226f72 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java @@ -208,11 +208,13 @@ public class CodeSystemUtilities { defineCodeSystemProperty(cs, "child", "The concept identified in this property is a child of the concept on which it is a property. The property type will be 'code'. The meaning of parent/child relationships is defined by the hierarchyMeaning attribute", PropertyType.CODE); } - public static boolean isDeprecated(CodeSystem cs, ConceptDefinitionComponent def) { + public static boolean isDeprecated(CodeSystem cs, ConceptDefinitionComponent def, boolean ignoreStatus) { try { for (ConceptPropertyComponent p : def.getProperty()) { - if (p.getCode().equals("status") && p.hasValue() && p.hasValueCodeType() && p.getValueCodeType().getCode().equals("deprecated")) - return true; + if (!ignoreStatus) { + if (p.getCode().equals("status") && p.hasValue() && p.hasValueCodeType() && p.getValueCodeType().getCode().equals("deprecated")) + return true; + } // this, though status should also be set if (p.getCode().equals("deprecationDate") && p.hasValue() && p.getValue() instanceof DateTimeType) return ((DateTimeType) p.getValue()).before(new DateTimeType(Calendar.getInstance())); From 883f4ee4cbf056eb93be16731064f5a51a67dfd2 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 30 Jul 2020 12:02:04 +1000 Subject: [PATCH 07/11] batch value set validation --- .../java/org/hl7/fhir/r5/terminologies/TerminologyClient.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyClient.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyClient.java index 1f9f47a98..a9cc3b539 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyClient.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/TerminologyClient.java @@ -34,6 +34,7 @@ package org.hl7.fhir.r5.terminologies; import java.util.Map; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r5.model.Bundle; import org.hl7.fhir.r5.model.CapabilityStatement; import org.hl7.fhir.r5.model.Parameters; import org.hl7.fhir.r5.model.TerminologyCapabilities; @@ -52,4 +53,5 @@ public interface TerminologyClient { public TerminologyClient setRetryCount(int retryCount) throws FHIRException; public CapabilityStatement getCapabilitiesStatementQuick() throws FHIRException; public Parameters lookupCode(Map params) throws FHIRException; + public Bundle validateBatch(Bundle batch); } \ No newline at end of file From 569718d829016d8b6de3720d917005a813039615 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 30 Jul 2020 12:03:03 +1000 Subject: [PATCH 08/11] Code formatting + fix code system rendering --- .../terminologies/ValueSetCheckerSimple.java | 22 ++++++++++++------- .../terminologies/ValueSetExpanderSimple.java | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java index a6b11323b..3bc0e2b24 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java @@ -402,18 +402,21 @@ public class ValueSetCheckerSimple implements ValueSetChecker { private boolean inComponent(ConceptSetComponent vsi, String system, String code, boolean only) throws FHIRException { for (UriType uri : vsi.getValueSet()) { - if (inImport(uri.getValue(), system, code)) + if (inImport(uri.getValue(), system, code)) { return true; + } } - if (!vsi.hasSystem()) + if (!vsi.hasSystem()) { return false; - + } if (only && system == null) { // whether we know the system or not, we'll accept the stated codes at face value - for (ConceptReferenceComponent cc : vsi.getConcept()) - if (cc.getCode().equals(code)) + for (ConceptReferenceComponent cc : vsi.getConcept()) { + if (cc.getCode().equals(code)) { return true; + } + } } if (!system.equals(vsi.getSystem())) @@ -443,12 +446,15 @@ public class ValueSetCheckerSimple implements ValueSetChecker { List list = cs.getConcept(); boolean ok = validateCodeInConceptList(code, cs, list); if (ok && vsi.hasConcept()) { - for (ConceptReferenceComponent cc : vsi.getConcept()) - if (cc.getCode().equals(code)) + for (ConceptReferenceComponent cc : vsi.getConcept()) { + if (cc.getCode().equals(code)) { return true; + } + } return false; - } else + } else { return ok; + } } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetExpanderSimple.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetExpanderSimple.java index c8dfad813..0ae49867d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetExpanderSimple.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetExpanderSimple.java @@ -293,7 +293,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander { if (exclusion.getCode().equals(def.getCode())) return; // excluded. } - if (!CodeSystemUtilities.isDeprecated(cs, def)) { + if (!CodeSystemUtilities.isDeprecated(cs, def, false)) { ValueSetExpansionContainsComponent np = null; boolean abs = CodeSystemUtilities.isNotSelectable(cs, def); boolean inc = CodeSystemUtilities.isInactive(cs, def); From eee552cdde15e4ec01331c0f6427495104ad277c Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 30 Jul 2020 12:03:34 +1000 Subject: [PATCH 09/11] Fix errors in StructureDefinition validation --- .../fhir/utilities/i18n/I18nConstants.java | 1 + .../src/main/resources/Messages.properties | 1 + .../type/StructureDefinitionValidator.java | 39 +++++++++++-------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index 24df4db80..34b8cc05c 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -333,6 +333,7 @@ public class I18nConstants { public static final String RESOURCE_RES_ID_PROHIBITED = "Resource_RES_ID_Prohibited"; public static final String RESOURCE_TYPE_MISMATCH_FOR___ = "Resource_type_mismatch_for___"; public static final String SAME_ID_ON_MULTIPLE_ELEMENTS__IN_ = "Same_id_on_multiple_elements__in_"; + public static final String SD_MUST_HAVE_DERIVATION = "SD_MUST_HAVE_DERIVATION"; public static final String SEARCHPARAMETER_BASE_WRONG = "SEARCHPARAMETER_BASE_WRONG"; public static final String SEARCHPARAMETER_EXP_WRONG = "SEARCHPARAMETER_EXP_WRONG"; public static final String SEARCHPARAMETER_NOTFOUND = "SEARCHPARAMETER_NOTFOUND"; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 1d471563e..d49720b3c 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -570,3 +570,4 @@ XHTML_XHTML_DOCTYPE_ILLEGAL = Malformed XHTML: Found a DocType declaration, and PACKAGE_VERSION_MISMATCH = FHIR Version mismatch in package {0}: version is {2} but must be {1} (path: {3}) VALUESET_REFERENCE_UNKNOWN = The value set import {0} could not be found so cannot be checked VALUESET_REFERENCE_INVALID_TYPE = The value set import {0} points to a resource of type {1} which is not valid +SD_MUST_HAVE_DERIVATION = StructureDefinition {0} must have a derivation, since it has a baseDefinition diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java index 42867c353..d7dec41cd 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java @@ -24,6 +24,7 @@ import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ExpressionNode; import org.hl7.fhir.r5.model.ExpressionNode.Kind; import org.hl7.fhir.r5.model.ExpressionNode.Operation; +import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule; import org.hl7.fhir.r5.model.SearchParameter; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.utils.FHIRPathEngine; @@ -66,27 +67,31 @@ public class StructureDefinitionValidator extends BaseValidator { sd.setSnapshot(null); StructureDefinition base = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition()); if (warning(errors, IssueType.NOTFOUND, stack.getLiteralPath(), base != null, I18nConstants.UNABLE_TO_FIND_BASE__FOR_, sd.getBaseDefinition(), "StructureDefinition, so can't check the differential")) { - List msgs = new ArrayList<>(); - ProfileUtilities pu = new ProfileUtilities(context, msgs, null); - pu.generateSnapshot(base, sd, sd.getUrl(), "http://hl7.org/fhir", sd.getName()); - if (msgs.size() > 0) { - for (ValidationMessage msg : msgs) { - // we need to set the location for the context - String loc = msg.getLocation(); - if (loc.contains("#")) { - msg.setLocation(stack.getLiteralPath()+".differential.element.where(path = '"+loc.substring(loc.indexOf("#")+1)+"')"); - } else { - msg.setLocation(stack.getLiteralPath()); + if (rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), sd.hasDerivation(), I18nConstants.SD_MUST_HAVE_DERIVATION, sd.getUrl())) { + if (sd.getDerivation() == TypeDerivationRule.CONSTRAINT) { + List msgs = new ArrayList<>(); + ProfileUtilities pu = new ProfileUtilities(context, msgs, null); + pu.generateSnapshot(base, sd, sd.getUrl(), "http://hl7.org/fhir", sd.getName()); + if (msgs.size() > 0) { + for (ValidationMessage msg : msgs) { + // we need to set the location for the context + String loc = msg.getLocation(); + if (loc.contains("#")) { + msg.setLocation(stack.getLiteralPath()+".differential.element.where(path = '"+loc.substring(loc.indexOf("#")+1)+"')"); + } else { + msg.setLocation(stack.getLiteralPath()); + } + errors.add(msg); + } + } + if (!snapshot.isEmpty()) { + int was = snapshot.size(); + int is = sd.getSnapshot().getElement().size(); + rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), was == is, I18nConstants.SNAPSHOT_EXISTING_PROBLEM, was, is); } - errors.add(msg); } } } - if (!snapshot.isEmpty()) { - int was = snapshot.size(); - int is = sd.getSnapshot().getElement().size(); - rule(errors, IssueType.NOTFOUND, stack.getLiteralPath(), was == is, I18nConstants.SNAPSHOT_EXISTING_PROBLEM, was, is); - } } catch (FHIRException | IOException e) { rule(errors, IssueType.EXCEPTION, stack.getLiteralPath(), false, I18nConstants.ERROR_GENERATING_SNAPSHOT, e.getMessage()); } From ef20988d704c1c18fe244f6da7ee5c99b9b4a02d Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 30 Jul 2020 12:04:03 +1000 Subject: [PATCH 10/11] release notes --- RELEASE_NOTES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e69de29bb..59f962f6f 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -0,0 +1,4 @@ +* Batch validation of codes in value sets +* Fix path problem doing comparisons +* Don't produce 2 columns for status for deprecated codes +* Fix errors in StructureDefinition validation \ No newline at end of file From 5aec9978bdfa8b4104e199dcf34fbfc7fcbc6d2f Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Thu, 30 Jul 2020 12:35:16 +1000 Subject: [PATCH 11/11] fix up tests to pass --- .../org/hl7/fhir/r5/context/BaseWorkerContext.java | 13 ++++++++++--- .../fhir/r5/renderers/OperationOutcomeRenderer.java | 8 ++++++++ .../hl7/fhir/r5/utils/client/FHIRToolingClient.java | 1 - .../fhir/validation/tests/ValidationTestSuite.java | 3 ++- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index 3e1a0e387..3062c0734 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -75,6 +75,7 @@ import org.hl7.fhir.r5.model.NamingSystem; import org.hl7.fhir.r5.model.NamingSystem.NamingSystemIdentifierType; import org.hl7.fhir.r5.model.NamingSystem.NamingSystemUniqueIdComponent; import org.hl7.fhir.r5.model.OperationDefinition; +import org.hl7.fhir.r5.model.OperationOutcome; import org.hl7.fhir.r5.model.Parameters; import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent; import org.hl7.fhir.r5.model.PlanDefinition; @@ -91,8 +92,10 @@ import org.hl7.fhir.r5.model.TerminologyCapabilities.TerminologyCapabilitiesCode import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.Bundle.BundleEntryComponent; import org.hl7.fhir.r5.model.Bundle.BundleType; +import org.hl7.fhir.r5.model.Bundle.HTTPVerb; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent; +import org.hl7.fhir.r5.renderers.OperationOutcomeRenderer; import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; import org.hl7.fhir.r5.terminologies.TerminologyClient; import org.hl7.fhir.r5.terminologies.ValueSetCheckerSimple; @@ -751,6 +754,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte setTerminologyOptions(options, pIn); BundleEntryComponent be = batch.addEntry(); be.setResource(pIn); + be.getRequest().setMethod(HTTPVerb.POST); be.getRequest().setUrl("ValueSet/$validate-code"); be.setUserData("source", t); systems.add(t.getCoding().getSystem()); @@ -768,16 +772,19 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte for (int i = 0; i < batch.getEntry().size(); i++) { CodingValidationRequest t = (CodingValidationRequest) batch.getEntry().get(i).getUserData("source"); BundleEntryComponent r = resp.getEntry().get(i); - if (r.getResponse().getStatus().startsWith("2")) { - t.setResult(new ValidationResult(IssueSeverity.ERROR, getResponseText(r.getResource())).setTxLink(txLog == null ? null : txLog.getLastId())); - } else { + if (r.getResource() instanceof Parameters) { t.setResult(processValidationResult((Parameters) r.getResource())); + } else { + t.setResult(new ValidationResult(IssueSeverity.ERROR, getResponseText(r.getResource())).setTxLink(txLog == null ? null : txLog.getLastId())); } } } } private String getResponseText(Resource resource) { + if (resource instanceof OperationOutcome) { + return OperationOutcomeRenderer.toString((OperationOutcome) resource); + } return "Todo"; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationOutcomeRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationOutcomeRenderer.java index d67c8f8f4..6139cbb97 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationOutcomeRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/OperationOutcomeRenderer.java @@ -16,6 +16,7 @@ import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.renderers.utils.RenderingContext; import org.hl7.fhir.r5.renderers.utils.Resolver.ResourceContext; import org.hl7.fhir.r5.utils.ToolingExtensions; +import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.xhtml.XhtmlNode; public class OperationOutcomeRenderer extends ResourceRenderer { @@ -89,4 +90,11 @@ public class OperationOutcomeRenderer extends ResourceRenderer { return display((OperationOutcome) r); } + public static String toString(OperationOutcome oo) { + CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(); + for (OperationOutcomeIssueComponent issue : oo.getIssue()) { + b.append(issue.getSeverity().toCode()+": "+issue.getDetails().getText()); + } + return b.toString(); + } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java index 0411ff817..56698605c 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/FHIRToolingClient.java @@ -513,7 +513,6 @@ public class FHIRToolingClient { } @SuppressWarnings("unchecked") - public OperationOutcome validate(Class resourceClass, T resource, String id) { ResourceRequest result = null; try { diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java index 4f143c22d..e1bd5dece 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/validation/tests/ValidationTestSuite.java @@ -98,7 +98,8 @@ public class ValidationTestSuite implements IEvaluationContext, IValidatorResour private String version; private String name; - private static final String DEF_TX = "http://tx.fhir.org"; + private static final String DEF_TX = "http://tx.fhir.org"; +// private static final String DEF_TX = "http://local.fhir.org:960"; private static Map ve = new HashMap<>(); private static ValidationEngine vCurr;