fixes to snapshot generation and validation for Bundle.tnry slicing by resource profile
This commit is contained in:
parent
9f1697dad1
commit
c13de56203
|
@ -248,6 +248,8 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
public static final String IS_DERIVED = "derived.fact";
|
public static final String IS_DERIVED = "derived.fact";
|
||||||
public static final String UD_ERROR_STATUS = "error-status";
|
public static final String UD_ERROR_STATUS = "error-status";
|
||||||
private static final String GENERATED_IN_SNAPSHOT = "profileutilities.snapshot.processed";
|
private static final String GENERATED_IN_SNAPSHOT = "profileutilities.snapshot.processed";
|
||||||
|
private static final boolean COPY_BINDING_EXTENSIONS = false;
|
||||||
|
private static final boolean DONT_DO_THIS = false;
|
||||||
private final boolean ADD_REFERENCE_TO_TABLE = true;
|
private final boolean ADD_REFERENCE_TO_TABLE = true;
|
||||||
|
|
||||||
private boolean useTableForFixedValues = true;
|
private boolean useTableForFixedValues = true;
|
||||||
|
@ -574,6 +576,34 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// last, check for wrong profiles or target profiles
|
||||||
|
for (ElementDefinition ed : derived.getSnapshot().getElement()) {
|
||||||
|
for (TypeRefComponent t : ed.getType()) {
|
||||||
|
for (UriType u : t.getProfile()) {
|
||||||
|
StructureDefinition sd = context.fetchResource(StructureDefinition.class, u.getValue());
|
||||||
|
if (sd == null) {
|
||||||
|
if (messages != null) {
|
||||||
|
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), "The type of profile "+u.getValue()+" cannot be checked as the profile is not known", IssueSeverity.WARNING));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
String wt = t.getWorkingCode();
|
||||||
|
if (ed.getPath().equals("Bundle.entry.response.outcome")) {
|
||||||
|
wt = "OperationOutcome";
|
||||||
|
}
|
||||||
|
if (!sd.getType().equals(wt)) {
|
||||||
|
boolean ok = isCompatibleType(wt, sd.getType());
|
||||||
|
if (!ok) {
|
||||||
|
String smsg = "The profile "+u.getValue()+" has type "+sd.getType()+" which is not consistent with the stated type "+wt;
|
||||||
|
if (exception)
|
||||||
|
throw new DefinitionException(smsg);
|
||||||
|
else
|
||||||
|
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.VALUE, url+"#"+ed.getId(), smsg, IssueSeverity.ERROR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// if we had an exception generating the snapshot, make sure we don't leave any half generated snapshot behind
|
// if we had an exception generating the snapshot, make sure we don't leave any half generated snapshot behind
|
||||||
derived.setSnapshot(null);
|
derived.setSnapshot(null);
|
||||||
|
@ -581,6 +611,18 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isCompatibleType(String base, String type) {
|
||||||
|
StructureDefinition sd = context.fetchTypeDefinition(type);
|
||||||
|
while (sd != null) {
|
||||||
|
if (sd.getType().equals(base)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
sd = context.fetchResource(StructureDefinition.class, sd.getBaseDefinition());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private StructureDefinitionDifferentialComponent cloneDiff(StructureDefinitionDifferentialComponent source) {
|
private StructureDefinitionDifferentialComponent cloneDiff(StructureDefinitionDifferentialComponent source) {
|
||||||
StructureDefinitionDifferentialComponent diff = new StructureDefinitionDifferentialComponent();
|
StructureDefinitionDifferentialComponent diff = new StructureDefinitionDifferentialComponent();
|
||||||
for (ElementDefinition sed : source.getElement()) {
|
for (ElementDefinition sed : source.getElement()) {
|
||||||
|
@ -2293,10 +2335,25 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Unable to check if "+derived.getBinding().getValueSet()+" is a proper subset of " +base.getBinding().getValueSet()+" - base value set is too large to check", ValidationMessage.IssueSeverity.WARNING));
|
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Unable to check if "+derived.getBinding().getValueSet()+" is a proper subset of " +base.getBinding().getValueSet()+" - base value set is too large to check", ValidationMessage.IssueSeverity.WARNING));
|
||||||
else if (!isSubset(expBase.getValueset(), expDerived.getValueset()))
|
else if (!isSubset(expBase.getValueset(), expDerived.getValueset()))
|
||||||
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" is not a subset of binding "+base.getBinding().getValueSet(), ValidationMessage.IssueSeverity.ERROR));
|
messages.add(new ValidationMessage(Source.ProfileValidator, ValidationMessage.IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "Binding "+derived.getBinding().getValueSet()+" is not a subset of binding "+base.getBinding().getValueSet(), ValidationMessage.IssueSeverity.ERROR));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
base.setBinding(derived.getBinding().copy());
|
ElementDefinitionBindingComponent d = derived.getBinding();
|
||||||
|
ElementDefinitionBindingComponent nb = base.getBinding().copy();
|
||||||
|
if (!COPY_BINDING_EXTENSIONS) {
|
||||||
|
nb.getExtension().clear();
|
||||||
|
}
|
||||||
|
nb.setDescription(null);
|
||||||
|
nb.getExtension().addAll(d.getExtension());
|
||||||
|
if (d.hasStrength()) {
|
||||||
|
nb.setStrength(d.getStrength());
|
||||||
|
}
|
||||||
|
if (d.hasDescription()) {
|
||||||
|
nb.setDescription(d.getDescription());
|
||||||
|
}
|
||||||
|
if (d.hasValueSet()) {
|
||||||
|
nb.setValueSet(d.getValueSet());
|
||||||
|
}
|
||||||
|
base.setBinding(nb);
|
||||||
} else if (trimDifferential)
|
} else if (trimDifferential)
|
||||||
derived.setBinding(null);
|
derived.setBinding(null);
|
||||||
else
|
else
|
||||||
|
@ -2316,31 +2373,61 @@ public class ProfileUtilities extends TranslatingUtilities {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (derived.hasType()) {
|
if (derived.hasType()) {
|
||||||
// if (derived.getPath().endsWith("[x]") && derived.hasSlicing() && derived.getSlicing().getRules() != SlicingRules.CLOSED) {
|
if (!Base.compareDeep(derived.getType(), base.getType(), false)) {
|
||||||
// // if we're slicing, and not closed, then it's the same list
|
|
||||||
//
|
|
||||||
// derived.getType().clear();
|
|
||||||
// derived.getType().addAll(base.getType());
|
|
||||||
// } else
|
|
||||||
if (!Base.compareDeep(derived.getType(), base.getType(), false)) {
|
|
||||||
if (base.hasType()) {
|
if (base.hasType()) {
|
||||||
for (TypeRefComponent ts : derived.getType()) {
|
for (TypeRefComponent ts : derived.getType()) {
|
||||||
// if (!ts.hasCode()) { // ommitted in the differential; copy it over....
|
|
||||||
// if (base.getType().size() > 1)
|
|
||||||
// throw new DefinitionException("StructureDefinition "+pn+" at "+derived.getPath()+": constrained type code must be present if there are multiple types ("+base.typeSummary()+")");
|
|
||||||
// if (base.getType().get(0).getCode() != null)
|
|
||||||
// ts.setCode(base.getType().get(0).getCode());
|
|
||||||
// }
|
|
||||||
boolean ok = false;
|
boolean ok = false;
|
||||||
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
|
||||||
String t = ts.getWorkingCode();
|
String t = ts.getWorkingCode();
|
||||||
for (TypeRefComponent td : base.getType()) {;
|
for (TypeRefComponent td : base.getType()) {;
|
||||||
String tt = td.getWorkingCode();
|
String tt = td.getWorkingCode();
|
||||||
b.append(tt);
|
b.append(tt);
|
||||||
if (td.hasCode() && (tt.equals(t) || "Extension".equals(tt) || (t.equals("uri") && tt.equals("string")) || // work around for old badly generated SDs
|
if (td.hasCode() && (tt.equals(t))) {
|
||||||
"Element".equals(tt) || "*".equals(tt) ||
|
|
||||||
(("Resource".equals(tt) || ("DomainResource".equals(tt)) && pkp.isResource(t)))))
|
|
||||||
ok = true;
|
ok = true;
|
||||||
|
}
|
||||||
|
if (!ok) {
|
||||||
|
StructureDefinition sdt = context.fetchTypeDefinition(tt);
|
||||||
|
if (sdt != null && sdt.getAbstract()) {
|
||||||
|
StructureDefinition sdb = context.fetchTypeDefinition(t);
|
||||||
|
while (sdb != null && !ok) {
|
||||||
|
ok = sdb.getType().equals(sdt.getUrl());
|
||||||
|
sdb = context.fetchResource(StructureDefinition.class, sdb.getBaseDefinition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// work around for old badly generated SDs
|
||||||
|
if (DONT_DO_THIS && Utilities.existsInList(tt, "Extension", "uri", "string", "Element")) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
if (DONT_DO_THIS && Utilities.existsInList(tt, "Resource","DomainResource") && pkp.isResource(t)) {
|
||||||
|
ok = true;
|
||||||
|
}
|
||||||
|
if (ok && ts.hasTargetProfile()) {
|
||||||
|
// check that any derived target has a reference chain back to one of the base target profiles
|
||||||
|
for (UriType u : ts.getTargetProfile()) {
|
||||||
|
boolean tgtOk = false;
|
||||||
|
String url = u.getValue();
|
||||||
|
while (url != null && !td.hasTargetProfile(url)) {
|
||||||
|
StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
|
||||||
|
if (sd == null) {
|
||||||
|
if (messages != null) {
|
||||||
|
messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.BUSINESSRULE, purl+"#"+derived.getPath(), "Connect check whether the target profile "+url+" is valid constraint on the base because it is not known", IssueSeverity.WARNING));
|
||||||
|
}
|
||||||
|
url = null;
|
||||||
|
tgtOk = true; // suppress error message
|
||||||
|
} else {
|
||||||
|
url = sd.getBaseDefinition();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!tgtOk) {
|
||||||
|
if (messages == null) {
|
||||||
|
throw new FHIRException("Error at "+purl+"#"+derived.getPath()+": The target profile "+url+" is not valid constraint on the base ("+td.getTargetProfile()+")");
|
||||||
|
} else {
|
||||||
|
messages.add(new ValidationMessage(Source.InstanceValidator, IssueType.BUSINESSRULE, pn+"."+derived.getPath(), "The target profile "+url+" is not valid constraint on the base ("+td.getTargetProfile()+")", IssueSeverity.ERROR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!ok)
|
if (!ok)
|
||||||
throw new DefinitionException("StructureDefinition "+purl+" at "+derived.getPath()+": illegal constrained type "+t+" from "+b.toString()+" in "+srcSD.getUrl());
|
throw new DefinitionException("StructureDefinition "+purl+" at "+derived.getPath()+": illegal constrained type "+t+" from "+b.toString()+" in "+srcSD.getUrl());
|
||||||
|
|
|
@ -4287,19 +4287,73 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
|
||||||
|
|
||||||
private void validateContains(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, ElementDefinition child, ElementDefinition context, Element resource, Element element, NodeStack stack, IdStatus idstatus) throws FHIRException, FHIRException, IOException {
|
private void validateContains(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, ElementDefinition child, ElementDefinition context, Element resource, Element element, NodeStack stack, IdStatus idstatus) throws FHIRException, FHIRException, IOException {
|
||||||
String resourceName = element.getType();
|
String resourceName = element.getType();
|
||||||
long t = System.nanoTime();
|
TypeRefComponent trr = null;
|
||||||
StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName);
|
for (TypeRefComponent tr : child.getType()) {
|
||||||
sdTime = sdTime + (System.nanoTime() - t);
|
if (tr.getCode().equals("Resource")) {
|
||||||
// special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise
|
trr = tr;
|
||||||
ValidatorHostContext hc = null;
|
break;
|
||||||
if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY || element.getSpecial() == SpecialElement.BUNDLE_OUTCOME || element.getSpecial() == SpecialElement.PARAMETER ) {
|
}
|
||||||
resource = element;
|
|
||||||
hc = hostContext.forEntry(element);
|
|
||||||
} else {
|
|
||||||
hc = hostContext.forContained(element);
|
|
||||||
}
|
}
|
||||||
if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, "No profile found for contained resource of type '" + resourceName + "'"))
|
if (trr == null) {
|
||||||
validateResource(hc, errors, resource, element, profile, null, idstatus, stack, false);
|
rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '"+resourceName+" is not valid - no resources allowed here");
|
||||||
|
} else if (isValidResourceType(resourceName, trr)) {
|
||||||
|
long t = System.nanoTime();
|
||||||
|
StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, "http://hl7.org/fhir/StructureDefinition/" + resourceName);
|
||||||
|
sdTime = sdTime + (System.nanoTime() - t);
|
||||||
|
// special case: resource wrapper is reset if we're crossing a bundle boundary, but not otherwise
|
||||||
|
ValidatorHostContext hc = null;
|
||||||
|
if (element.getSpecial() == SpecialElement.BUNDLE_ENTRY || element.getSpecial() == SpecialElement.BUNDLE_OUTCOME || element.getSpecial() == SpecialElement.PARAMETER ) {
|
||||||
|
resource = element;
|
||||||
|
hc = hostContext.forEntry(element);
|
||||||
|
} else {
|
||||||
|
hc = hostContext.forContained(element);
|
||||||
|
}
|
||||||
|
if (rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), profile != null, "No profile found for contained resource of type '" + resourceName + "'")) {
|
||||||
|
validateResource(hc, errors, resource, element, profile, null, idstatus, stack, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
List<String> types = new ArrayList<>();
|
||||||
|
for (UriType u : trr.getProfile()) {
|
||||||
|
StructureDefinition sd = this.context.fetchResource(StructureDefinition.class, u.getValue());
|
||||||
|
if (sd != null && !types.contains(sd.getType())) {
|
||||||
|
types.add(sd.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (types.size() == 1) {
|
||||||
|
rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '"+resourceName+"' is not valid - must be "+types.get(0));
|
||||||
|
} else {
|
||||||
|
rule(errors, IssueType.INFORMATIONAL, element.line(), element.col(), stack.getLiteralPath(), false, "The type '"+resourceName+"' is not valid - must be one of "+types);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidResourceType(String type, TypeRefComponent def) {
|
||||||
|
if (!def.hasProfile()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
List<StructureDefinition> list = new ArrayList<>();
|
||||||
|
for (UriType u : def.getProfile()) {
|
||||||
|
StructureDefinition sdt = context.fetchResource(StructureDefinition.class, u.getValue());
|
||||||
|
if (sdt != null) {
|
||||||
|
list.add(sdt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StructureDefinition sdt = context.fetchTypeDefinition(type);
|
||||||
|
while (sdt != null) {
|
||||||
|
if (def.getWorkingCode().equals("Resource")) {
|
||||||
|
for (StructureDefinition sd : list) {
|
||||||
|
if (sd.getUrl().equals(sdt.getUrl())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (sd.getType().equals(sdt.getType())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sdt = context.fetchResource(StructureDefinition.class, sdt.getBaseDefinition());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateDocument(List<ValidationMessage> errors, List<Element> entries, Element composition, NodeStack stack, String fullUrl, String id) {
|
private void validateDocument(List<ValidationMessage> errors, List<Element> entries, Element composition, NodeStack stack, String fullUrl, String id) {
|
||||||
|
@ -4480,6 +4534,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
|
||||||
checkInvariants(hostContext, errors, profile, ei.definition, resource, ei.element, localStack, true);
|
checkInvariants(hostContext, errors, profile, ei.definition, resource, ei.element, localStack, true);
|
||||||
|
|
||||||
ei.element.markValidation(profile, ei.definition);
|
ei.element.markValidation(profile, ei.definition);
|
||||||
|
boolean elementValidated = false;
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
if (isPrimitiveType(type)) {
|
if (isPrimitiveType(type)) {
|
||||||
checkPrimitive(hostContext, errors, ei.path, type, ei.definition, ei.element, profile, stack);
|
checkPrimitive(hostContext, errors, ei.path, type, ei.definition, ei.element, profile, stack);
|
||||||
|
@ -4514,6 +4569,7 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
|
||||||
}
|
}
|
||||||
} else if (type.equals("Resource")) {
|
} else if (type.equals("Resource")) {
|
||||||
validateContains(hostContext, errors, ei.path, ei.definition, definition, resource, ei.element, localStack, idStatusForEntry(element, ei)); // if
|
validateContains(hostContext, errors, ei.path, ei.definition, definition, resource, ei.element, localStack, idStatusForEntry(element, ei)); // if
|
||||||
|
elementValidated = true;
|
||||||
// (str.matches(".*([.,/])work\\1$"))
|
// (str.matches(".*([.,/])work\\1$"))
|
||||||
} else if (Utilities.isAbsoluteUrl(type)) {
|
} else if (Utilities.isAbsoluteUrl(type)) {
|
||||||
StructureDefinition defn = context.fetchTypeDefinition(type);
|
StructureDefinition defn = context.fetchTypeDefinition(type);
|
||||||
|
@ -4532,7 +4588,6 @@ private boolean isAnswerRequirementFulfilled(QuestionnaireItemComponent qItem, L
|
||||||
validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.element, type, localStack, false, true, null);
|
validateElement(hostContext, errors, profile, ei.definition, null, null, resource, ei.element, type, localStack, false, true, null);
|
||||||
}
|
}
|
||||||
StructureDefinition p = null;
|
StructureDefinition p = null;
|
||||||
boolean elementValidated = false;
|
|
||||||
String tail = null;
|
String tail = null;
|
||||||
if (profiles.isEmpty()) {
|
if (profiles.isEmpty()) {
|
||||||
if (type != null) {
|
if (type != null) {
|
||||||
|
|
Loading…
Reference in New Issue