Merge pull request #482 from hapifhir/gg-202104-bugfixes-2
Ensure that CVX uses tx.fhir.org, not UTG definitions which are wrong…
This commit is contained in:
commit
8550fc37b8
|
@ -0,0 +1,3 @@
|
|||
Terminology: Ensure that CVX uses tx.fhir.org, not UTG definitions which are wrong
|
||||
Validator: Fix problems with Bundle validation for ids in collections and add additional search related validation
|
||||
Validator: Remove check on ElementDefinition.id for R2B
|
|
@ -1,6 +1,6 @@
|
|||
The Java Core Code Generator
|
||||
|
||||
Note: This code only generates tje R5 java code. Older generated models are now maintained by hand.
|
||||
Note: This code only generates the R5 java code. Older generated models are now maintained by hand.
|
||||
|
||||
To run this code, run the class JavaCoreGenerator with 3 parameters:
|
||||
* 1: fhir version to generate from (e.g. 4.1.0 or 'current'
|
||||
|
|
|
@ -63,7 +63,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
|
|||
import org.hl7.fhir.utilities.validation.ValidationOptions;
|
||||
import org.hl7.fhir.utilities.validation.ValidationOptions.ValueSetMode;
|
||||
|
||||
public class ValueSetCheckerSimple implements ValueSetChecker {
|
||||
public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChecker {
|
||||
|
||||
private ValueSet valueset;
|
||||
private IWorkerContext context;
|
||||
|
|
|
@ -108,7 +108,7 @@ import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionParameterComponent;
|
|||
import org.hl7.fhir.r5.utils.ToolingExtensions;
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
|
||||
public class ValueSetExpanderSimple implements ValueSetExpander {
|
||||
public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetExpander {
|
||||
|
||||
public class PropertyFilter implements IConceptFilter {
|
||||
|
||||
|
@ -567,7 +567,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
|
|||
copyImportContains(base.getExpansion().getContains(), null, expParams, imports);
|
||||
} else {
|
||||
CodeSystem cs = context.fetchCodeSystem(inc.getSystem());
|
||||
if ((cs == null || (cs.getContent() != CodeSystemContentMode.COMPLETE && cs.getContent() != CodeSystemContentMode.FRAGMENT))) {
|
||||
if (isServerSide(inc.getSystem()) || (cs == null || (cs.getContent() != CodeSystemContentMode.COMPLETE && cs.getContent() != CodeSystemContentMode.FRAGMENT))) {
|
||||
doServerIncludeCodes(inc, heirarchical, exp, imports, expParams, extensions);
|
||||
} else {
|
||||
doInternalIncludeCodes(inc, exp, expParams, imports, cs);
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
package org.hl7.fhir.r5.terminologies;
|
||||
|
||||
import org.hl7.fhir.utilities.Utilities;
|
||||
|
||||
public class ValueSetWorker {
|
||||
|
||||
protected boolean isServerSide(String url) {
|
||||
return Utilities.existsInList(url, "http://hl7.org/fhir/sid/cvx");
|
||||
}
|
||||
}
|
|
@ -631,6 +631,15 @@ public class I18nConstants {
|
|||
public static final String BUNDLE_RULE_UNKNOWN = "BUNDLE_RULE_UNKNOWN";
|
||||
public static final String BUNDLE_RULE_INVALID_INDEX = "BUNDLE_RULE_INVALID_INDEX";
|
||||
public static final String BUNDLE_RULE_PROFILE_UNKNOWN = "BUNDLE_RULE_PROFILE_UNKNOWN";
|
||||
public static final String BUNDLE_SEARCH_NOSELF = "BUNDLE_SEARCH_NOSELF";
|
||||
public static final String BUNDLE_SEARCH_SELF_NOT_UNDERSTOOD = "BUNDLE_SEARCH_SELF_NOT_UNDERSTOOD";
|
||||
public static final String BUNDLE_SEARCH_ENTRY_NO_RESOURCE = "BUNDLE_SEARCH_ENTRY_NO_RESOURCE";
|
||||
public static final String BUNDLE_SEARCH_ENTRY_TYPE_NOT_SURE = "BUNDLE_SEARCH_ENTRY_TYPE_NOT_SURE";
|
||||
public static final String BUNDLE_SEARCH_ENTRY_NO_RESOURCE_ID = "BUNDLE_SEARCH_ENTRY_NO_RESOURCE_ID";
|
||||
public static final String BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_MODE = "BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_MODE";
|
||||
public static final String BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_NO_MODE = "BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_NO_MODE";
|
||||
public static final String BUNDLE_SEARCH_NO_MODE = "BUNDLE_SEARCH_NO_MODE";
|
||||
public static final String BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_OUTCOME = "BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_OUTCOME";
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -643,3 +643,12 @@ CODESYSTEM_CS_SUPP_INVALID_CODE = The code ''{1}'' is not declared in the base C
|
|||
SD_VALUE_TYPE_IILEGAL = The element {0} has a {1} of type {2}, which is not in the list of allowed types ({3})
|
||||
SD_NO_TYPES_OR_CONTENTREF = The element {0} has no assigned types, and no content reference
|
||||
CODESYSTEM_CS_UNK_EXPANSION = The code provided ({2}) is not in the value set {0}, and a code is required from this value set. The system {1} is unknown.
|
||||
BUNDLE_SEARCH_NOSELF = SearchSet Bundles should have a self link that specifies what the search was
|
||||
BUNDLE_SEARCH_SELF_NOT_UNDERSTOOD = No types could be determined from the search string, so the types can't be checked
|
||||
BUNDLE_SEARCH_ENTRY_NO_RESOURCE = SearchSet Bundle Entries must have resources
|
||||
BUNDLE_SEARCH_ENTRY_TYPE_NOT_SURE = Unable to determine if this resource is a valid resource type for this search
|
||||
BUNDLE_SEARCH_ENTRY_NO_RESOURCE_ID = Search results must have ids
|
||||
BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_MODE = This is not a matching resource type for the specified search ({0} expecting {1})
|
||||
BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_OUTCOME = This is not an OperationOutcome ({0})
|
||||
BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_NO_MODE = This is not a matching resource type for the specified search (is a search mode needed?) ({0} expecting {1})
|
||||
BUNDLE_SEARCH_NO_MODE = SearchSet bundles should have search modes on the entries
|
||||
|
|
|
@ -125,9 +125,11 @@ public class BaseValidator {
|
|||
|
||||
protected final String META = "meta";
|
||||
protected final String ENTRY = "entry";
|
||||
protected final String LINK = "link";
|
||||
protected final String DOCUMENT = "document";
|
||||
protected final String RESOURCE = "resource";
|
||||
protected final String MESSAGE = "message";
|
||||
protected final String SEARCHSET = "searchset";
|
||||
protected final String ID = "id";
|
||||
protected final String FULL_URL = "fullUrl";
|
||||
protected final String PATH_ARG = ":0";
|
||||
|
|
|
@ -2005,7 +2005,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
}
|
||||
if (type.equals(ID)) {
|
||||
// work around an old issue with ElementDefinition.id
|
||||
if (!context.getPath().equals("ElementDefinition.id") || VersionUtilities.versionsCompatible("1.4", this.context.getVersion())) {
|
||||
if (!context.getPath().equals("ElementDefinition.id")) {
|
||||
rule(errors, IssueType.INVALID, e.line(), e.col(), path, FormatUtilities.isValidId(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_ID_VALID, e.primitiveValue());
|
||||
}
|
||||
}
|
||||
|
@ -4166,6 +4166,24 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
|
|||
hc = hostContext.forContained(element);
|
||||
}
|
||||
stack.resetIds();
|
||||
if (element.getSpecial() != null) {
|
||||
switch (element.getSpecial()) {
|
||||
case BUNDLE_ENTRY:
|
||||
idstatus = IdStatus.OPTIONAL;
|
||||
break;
|
||||
case BUNDLE_OUTCOME:
|
||||
idstatus = IdStatus.OPTIONAL;
|
||||
break;
|
||||
case CONTAINED:
|
||||
idstatus = IdStatus.REQUIRED;
|
||||
break;
|
||||
case PARAMETER:
|
||||
idstatus = IdStatus.OPTIONAL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (trr.getProfile().size() == 1) {
|
||||
long t = System.nanoTime();
|
||||
StructureDefinition profile = this.context.fetchResource(StructureDefinition.class, trr.getProfile().get(0).asStringValue());
|
||||
|
|
|
@ -73,6 +73,9 @@ public class BundleValidator extends BaseValidator{
|
|||
}
|
||||
checkAllInterlinked(errors, entries, stack, bundle, VersionUtilities.isR5Ver(context.getVersion()));
|
||||
}
|
||||
if (type.equals(SEARCHSET)) {
|
||||
checkSearchSet(errors, bundle, entries, stack);
|
||||
}
|
||||
// We do not yet have rules requiring that the id and fullUrl match when dealing with messaging Bundles
|
||||
// validateResourceIds(errors, entries, stack);
|
||||
}
|
||||
|
@ -122,6 +125,159 @@ public class BundleValidator extends BaseValidator{
|
|||
}
|
||||
}
|
||||
|
||||
private void checkSearchSet(List<ValidationMessage> errors, Element bundle, List<Element> entries, NodeStack stack) {
|
||||
// warning: should have self link
|
||||
List<Element> links = new ArrayList<Element>();
|
||||
bundle.getNamedChildren(LINK, links);
|
||||
Element selfLink = getSelfLink(links);
|
||||
List<String> types = new ArrayList<>();
|
||||
if (selfLink == null) {
|
||||
warning(errors, IssueType.INVALID, bundle.line(), bundle.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_SEARCH_NOSELF);
|
||||
} else {
|
||||
readSearchResourceTypes(selfLink.getNamedChildValue("url"), types);
|
||||
if (types.size() == 0) {
|
||||
hint(errors, IssueType.INVALID, bundle.line(), bundle.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_SEARCH_SELF_NOT_UNDERSTOOD);
|
||||
}
|
||||
}
|
||||
|
||||
Boolean searchMode = readHasSearchMode(entries);
|
||||
if (searchMode == false) { // if no resources have search mode
|
||||
boolean typeProblem = false;
|
||||
String rtype = null;
|
||||
int count = 0;
|
||||
for (Element entry : entries) {
|
||||
NodeStack estack = stack.push(entry, count, null, null);
|
||||
count++;
|
||||
Element res = entry.getNamedChild("resource");
|
||||
if (rule(errors, IssueType.INVALID, bundle.line(), bundle.col(), estack.getLiteralPath(), res != null, I18nConstants.BUNDLE_SEARCH_ENTRY_NO_RESOURCE)) {
|
||||
NodeStack rstack = estack.push(res, -1, null, null);
|
||||
String rt = res.fhirType();
|
||||
Boolean ok = checkSearchType(types, rt);
|
||||
if (ok == null) {
|
||||
typeProblem = true;
|
||||
hint(errors, IssueType.INVALID, bundle.line(), bundle.col(), rstack.getLiteralPath(), selfLink == null, I18nConstants.BUNDLE_SEARCH_ENTRY_TYPE_NOT_SURE);
|
||||
String id = res.getNamedChildValue("id");
|
||||
warning(errors, IssueType.INVALID, bundle.line(), bundle.col(), rstack.getLiteralPath(), id != null || "OperationOutcome".equals(rt), I18nConstants.BUNDLE_SEARCH_ENTRY_NO_RESOURCE_ID);
|
||||
} else if (ok) {
|
||||
if (!"OperationOutcome".equals(rt)) {
|
||||
String id = res.getNamedChildValue("id");
|
||||
warning(errors, IssueType.INVALID, bundle.line(), bundle.col(), rstack.getLiteralPath(), id != null, I18nConstants.BUNDLE_SEARCH_ENTRY_NO_RESOURCE_ID);
|
||||
if (rtype != null && !rt.equals(rtype)) {
|
||||
typeProblem = true;
|
||||
} else if (rtype == null) {
|
||||
rtype = rt;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
typeProblem = true;
|
||||
warning(errors, IssueType.INVALID, bundle.line(), bundle.col(), estack.getLiteralPath(), false, I18nConstants.BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_NO_MODE, rt, types);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeProblem) {
|
||||
warning(errors, IssueType.INVALID, bundle.line(), bundle.col(), stack.getLiteralPath(), !typeProblem, I18nConstants.BUNDLE_SEARCH_NO_MODE);
|
||||
} else {
|
||||
hint(errors, IssueType.INVALID, bundle.line(), bundle.col(), stack.getLiteralPath(), !typeProblem, I18nConstants.BUNDLE_SEARCH_NO_MODE);
|
||||
}
|
||||
} else {
|
||||
int count = 0;
|
||||
for (Element entry : entries) {
|
||||
NodeStack estack = stack.push(entry, count, null, null);
|
||||
count++;
|
||||
Element res = entry.getNamedChild("resource");
|
||||
String sm = null;
|
||||
Element s = entry.getNamedChild("search");
|
||||
if (s != null) {
|
||||
sm = s.getNamedChildValue("mode");
|
||||
}
|
||||
warning(errors, IssueType.INVALID, bundle.line(), bundle.col(), estack.getLiteralPath(), sm != null, I18nConstants.BUNDLE_SEARCH_NO_MODE);
|
||||
if (rule(errors, IssueType.INVALID, bundle.line(), bundle.col(), estack.getLiteralPath(), res != null, I18nConstants.BUNDLE_SEARCH_ENTRY_NO_RESOURCE)) {
|
||||
NodeStack rstack = estack.push(res, -1, null, null);
|
||||
String rt = res.fhirType();
|
||||
String id = res.getNamedChildValue("id");
|
||||
if (sm != null) {
|
||||
if ("match".equals(sm)) {
|
||||
rule(errors, IssueType.INVALID, bundle.line(), bundle.col(), rstack.getLiteralPath(), id != null, I18nConstants.BUNDLE_SEARCH_ENTRY_NO_RESOURCE_ID);
|
||||
rule(errors, IssueType.INVALID, bundle.line(), bundle.col(), rstack.getLiteralPath(), types.size() == 0 || checkSearchType(types, rt), I18nConstants.BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_MODE, rt, types);
|
||||
} else if ("include".equals(sm)) {
|
||||
rule(errors, IssueType.INVALID, bundle.line(), bundle.col(), rstack.getLiteralPath(), id != null, I18nConstants.BUNDLE_SEARCH_ENTRY_NO_RESOURCE_ID);
|
||||
} else { // outcome
|
||||
rule(errors, IssueType.INVALID, bundle.line(), bundle.col(), rstack.getLiteralPath(), "OperationOutcome".equals(rt), I18nConstants.BUNDLE_SEARCH_ENTRY_WRONG_RESOURCE_TYPE_OUTCOME, rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean checkSearchType(List<String> types, String rt) {
|
||||
if (types.size() == 0) {
|
||||
return null;
|
||||
} else {
|
||||
return Utilities.existsInList(rt, types);
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean readHasSearchMode(List<Element> entries) {
|
||||
boolean all = true;
|
||||
boolean any = false;
|
||||
for (Element entry : entries) {
|
||||
String sm = null;
|
||||
Element s = entry.getNamedChild("search");
|
||||
if (s != null) {
|
||||
sm = s.getNamedChildValue("mode");
|
||||
}
|
||||
if (sm != null) {
|
||||
any = true;
|
||||
} else {
|
||||
all = false;
|
||||
}
|
||||
}
|
||||
if (all) {
|
||||
return true;
|
||||
} else if (any) {
|
||||
return null;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void readSearchResourceTypes(String ref, List<String> types) {
|
||||
if (ref == null) {
|
||||
return;
|
||||
}
|
||||
String[] head = null;
|
||||
String[] tail = null;
|
||||
if (ref.contains("?")) {
|
||||
head = ref.substring(0, ref.indexOf("?")).split("\\/");
|
||||
tail = ref.substring(ref.indexOf("?")+1).split("\\&");
|
||||
} else {
|
||||
head = ref.split("\\/");
|
||||
}
|
||||
if (head == null || head.length == 0) {
|
||||
return;
|
||||
} else if (context.getResourceNames().contains(head[head.length-1])) {
|
||||
types.add(head[head.length-1]);
|
||||
} else if (tail != null) {
|
||||
for (String s : tail) {
|
||||
if (s.startsWith("_type=")) {
|
||||
for (String t : s.substring(6).split("\\,")) {
|
||||
types.add(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Element getSelfLink(List<Element> links) {
|
||||
for (Element link : links) {
|
||||
if ("self".equals(link.getNamedChildValue("relation"))) {
|
||||
return link;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void validateDocument(List<ValidationMessage> errors, List<Element> entries, Element composition, NodeStack stack, String fullUrl, String id) {
|
||||
// first entry must be a composition
|
||||
if (rule(errors, IssueType.INVALID, composition.line(), composition.col(), stack.getLiteralPath(), composition.getType().equals("Composition"), I18nConstants.BUNDLE_BUNDLE_ENTRY_DOCUMENT)) {
|
||||
|
|
Loading…
Reference in New Issue