Merge pull request #1029 from hapifhir/gg-202212-json-header
Gg 202212 json header
This commit is contained in:
commit
89486c17fc
|
@ -186,6 +186,7 @@ public class ToolingExtensions {
|
||||||
public static final String EXT_RESOURCE_INTERFACE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-interface";
|
public static final String EXT_RESOURCE_INTERFACE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-interface";
|
||||||
public static final String EXT_SEC_CAT = "http://hl7.org/fhir/StructureDefinition/structuredefinition-security-category";
|
public static final String EXT_SEC_CAT = "http://hl7.org/fhir/StructureDefinition/structuredefinition-security-category";
|
||||||
public static final String EXT_STANDARDS_STATUS = "http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status";
|
public static final String EXT_STANDARDS_STATUS = "http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status";
|
||||||
|
public static final String EXT_STANDARDS_STATUS_REASON = "http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status-reason";
|
||||||
public static final String EXT_TABLE_NAME = "http://hl7.org/fhir/StructureDefinition/structuredefinition-table-name";
|
public static final String EXT_TABLE_NAME = "http://hl7.org/fhir/StructureDefinition/structuredefinition-table-name";
|
||||||
public static final String EXT_TARGET_ID = "http://hl7.org/fhir/StructureDefinition/targetElement";
|
public static final String EXT_TARGET_ID = "http://hl7.org/fhir/StructureDefinition/targetElement";
|
||||||
public static final String EXT_TARGET_PATH = "http://hl7.org/fhir/StructureDefinition/targetPath";
|
public static final String EXT_TARGET_PATH = "http://hl7.org/fhir/StructureDefinition/targetPath";
|
||||||
|
@ -837,6 +838,10 @@ public class ToolingExtensions {
|
||||||
return StandardsStatus.fromCode(ToolingExtensions.readStringExtension(dr, ToolingExtensions.EXT_STANDARDS_STATUS));
|
return StandardsStatus.fromCode(ToolingExtensions.readStringExtension(dr, ToolingExtensions.EXT_STANDARDS_STATUS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static StandardsStatus getStandardsStatus(Element e) throws FHIRException {
|
||||||
|
return StandardsStatus.fromCode(ToolingExtensions.readStringExtension(e, ToolingExtensions.EXT_STANDARDS_STATUS));
|
||||||
|
}
|
||||||
|
|
||||||
public static void setStandardsStatus(DomainResource dr, StandardsStatus status, String normativeVersion) {
|
public static void setStandardsStatus(DomainResource dr, StandardsStatus status, String normativeVersion) {
|
||||||
if (status == null)
|
if (status == null)
|
||||||
ToolingExtensions.removeExtension(dr, ToolingExtensions.EXT_STANDARDS_STATUS);
|
ToolingExtensions.removeExtension(dr, ToolingExtensions.EXT_STANDARDS_STATUS);
|
||||||
|
|
|
@ -311,9 +311,14 @@ public class VersionUtilities {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (pc!=null) {
|
if (pc!=null) {
|
||||||
|
if (pt.contains("-") && !pc.contains("-")) {
|
||||||
|
pt = pt.substring(0, pt.indexOf("-"));
|
||||||
|
return pt.compareTo(pc) >= 0;
|
||||||
|
} else {
|
||||||
return compareVersionPart(pt, pc);
|
return compareVersionPart(pt, pc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,11 @@ public class I18nConstants {
|
||||||
public static final String BUNDLE_BUNDLE_ENTRY_NOPROFILE_TYPE = "Bundle_BUNDLE_Entry_NoProfile_TYPE";
|
public static final String BUNDLE_BUNDLE_ENTRY_NOPROFILE_TYPE = "Bundle_BUNDLE_Entry_NoProfile_TYPE";
|
||||||
public static final String BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES = "BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES";
|
public static final String BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES = "BUNDLE_BUNDLE_ENTRY_MULTIPLE_PROFILES";
|
||||||
public static final String BUNDLE_BUNDLE_ENTRY_NOTFOUND = "Bundle_BUNDLE_Entry_NotFound";
|
public static final String BUNDLE_BUNDLE_ENTRY_NOTFOUND = "Bundle_BUNDLE_Entry_NotFound";
|
||||||
public static final String BUNDLE_BUNDLE_ENTRY_ORPHAN = "Bundle_BUNDLE_Entry_Orphan";
|
public static final String BUNDLE_BUNDLE_ENTRY_ORPHAN_DOCUMENT = "Bundle_BUNDLE_Entry_Orphan_DOCUMENT";
|
||||||
public static final String BUNDLE_BUNDLE_ENTRY_REVERSE = "BUNDLE_BUNDLE_ENTRY_REVERSE";
|
public static final String BUNDLE_BUNDLE_ENTRY_ORPHAN_MESSAGE = "Bundle_BUNDLE_Entry_Orphan_MESSAGE";
|
||||||
|
public static final String BUNDLE_BUNDLE_ENTRY_REVERSE_R4 = "BUNDLE_BUNDLE_ENTRY_REVERSE_R4";
|
||||||
|
public static final String BUNDLE_BUNDLE_ENTRY_REVERSE_R5 = "BUNDLE_BUNDLE_ENTRY_REVERSE_R5";
|
||||||
|
public static final String BUNDLE_BUNDLE_ENTRY_REVERSE_MSG = "BUNDLE_BUNDLE_ENTRY_REVERSE_MSG";
|
||||||
public static final String BUNDLE_BUNDLE_ENTRY_TYPE = "Bundle_BUNDLE_Entry_Type";
|
public static final String BUNDLE_BUNDLE_ENTRY_TYPE = "Bundle_BUNDLE_Entry_Type";
|
||||||
public static final String BUNDLE_BUNDLE_ENTRY_TYPE2 = "Bundle_BUNDLE_Entry_Type2";
|
public static final String BUNDLE_BUNDLE_ENTRY_TYPE2 = "Bundle_BUNDLE_Entry_Type2";
|
||||||
public static final String BUNDLE_BUNDLE_ENTRY_TYPE3 = "Bundle_BUNDLE_Entry_Type3";
|
public static final String BUNDLE_BUNDLE_ENTRY_TYPE3 = "Bundle_BUNDLE_Entry_Type3";
|
||||||
|
@ -38,6 +41,13 @@ public class I18nConstants {
|
||||||
public static final String BUNDLE_BUNDLE_FULLURL_NEEDVERSION = "Bundle_BUNDLE_FullUrl_NeedVersion";
|
public static final String BUNDLE_BUNDLE_FULLURL_NEEDVERSION = "Bundle_BUNDLE_FullUrl_NeedVersion";
|
||||||
public static final String BUNDLE_BUNDLE_MULTIPLEMATCHES = "Bundle_BUNDLE_MultipleMatches";
|
public static final String BUNDLE_BUNDLE_MULTIPLEMATCHES = "Bundle_BUNDLE_MultipleMatches";
|
||||||
public static final String BUNDLE_BUNDLE_NOT_LOCAL = "Bundle_BUNDLE_Not_Local";
|
public static final String BUNDLE_BUNDLE_NOT_LOCAL = "Bundle_BUNDLE_Not_Local";
|
||||||
|
public static final String BUNDLE_LINK_UNKNOWN = "BUNDLE_LINK_UNKNOWN";
|
||||||
|
public static final String BUNDLE_LINK_SEARCH_PROHIBITED = "BUNDLE_LINK_SEARCH_PROHIBITED";
|
||||||
|
public static final String BUNDLE_LINK_SEARCH_NO_DUPLICATES = "BUNDLE_LINK_SEARCH_NO_DUPLICATES";
|
||||||
|
public static final String BUNDLE_LINK_STYELSHEET_EXTERNAL = "BUNDLE_LINK_STYELSHEET_EXTERNAL";
|
||||||
|
public static final String BUNDLE_LINK_STYELSHEET_INSECURE = "BUNDLE_LINK_STYELSHEET_INSECURE";
|
||||||
|
public static final String BUNDLE_LINK_STYELSHEET_LINKABLE = "BUNDLE_LINK_STYELSHEET_LINKABLE";
|
||||||
|
public static final String BUNDLE_LINK_STYELSHEET_NOT_FOUND = "BUNDLE_LINK_STYELSHEET_NOT_FOUND";
|
||||||
public static final String BUNDLE_MSG_EVENT_COUNT = "Bundle_MSG_Event_Count";
|
public static final String BUNDLE_MSG_EVENT_COUNT = "Bundle_MSG_Event_Count";
|
||||||
public static final String CANT_HAVE_CHILDREN_ON_AN_ELEMENT_WITH_A_POLYMORPHIC_TYPE__YOU_MUST_SLICE_AND_CONSTRAIN_THE_TYPES_FIRST_SORTELEMENTS_ = "Cant_have_children_on_an_element_with_a_polymorphic_type__you_must_slice_and_constrain_the_types_first_sortElements_";
|
public static final String CANT_HAVE_CHILDREN_ON_AN_ELEMENT_WITH_A_POLYMORPHIC_TYPE__YOU_MUST_SLICE_AND_CONSTRAIN_THE_TYPES_FIRST_SORTELEMENTS_ = "Cant_have_children_on_an_element_with_a_polymorphic_type__you_must_slice_and_constrain_the_types_first_sortElements_";
|
||||||
public static final String CAN_ONLY_SPECIFY_PROFILE_IN_THE_CONTEXT = "Can_only_specify_profile_in_the_context";
|
public static final String CAN_ONLY_SPECIFY_PROFILE_IN_THE_CONTEXT = "Can_only_specify_profile_in_the_context";
|
||||||
|
|
|
@ -653,6 +653,7 @@ public class JsonParser {
|
||||||
|
|
||||||
private static byte[] fetch(String source) throws IOException {
|
private static byte[] fetch(String source) throws IOException {
|
||||||
SimpleHTTPClient fetcher = new SimpleHTTPClient();
|
SimpleHTTPClient fetcher = new SimpleHTTPClient();
|
||||||
|
fetcher.addHeader("Accept", "application/json, application/fhir+json");
|
||||||
HTTPResult res = fetcher.get(source+"?nocache=" + System.currentTimeMillis());
|
HTTPResult res = fetcher.get(source+"?nocache=" + System.currentTimeMillis());
|
||||||
res.checkThrowException();
|
res.checkThrowException();
|
||||||
return res.getContent();
|
return res.getContent();
|
||||||
|
|
|
@ -12,8 +12,11 @@ Bundle_BUNDLE_Entry_NoProfile_TYPE = No profile found for {0} resource of type '
|
||||||
Bundle_BUNDLE_Entry_NoProfile_EXPL = Specified profile {2} not found for {0} resource of type ''{0}''
|
Bundle_BUNDLE_Entry_NoProfile_EXPL = Specified profile {2} not found for {0} resource of type ''{0}''
|
||||||
Bundle_BUNDLE_Entry_NO_LOGICAL_EXPL = Specified logical model {1} not found for resource ''Binary/{0}''
|
Bundle_BUNDLE_Entry_NO_LOGICAL_EXPL = Specified logical model {1} not found for resource ''Binary/{0}''
|
||||||
Bundle_BUNDLE_Entry_NotFound = Can''t find ''{0}'' in the bundle ({1})
|
Bundle_BUNDLE_Entry_NotFound = Can''t find ''{0}'' in the bundle ({1})
|
||||||
Bundle_BUNDLE_Entry_Orphan = Entry {0} isn''t reachable by traversing from first Bundle entry
|
Bundle_BUNDLE_Entry_Orphan_MESSAGE = Entry {0} isn''t reachable by traversing links (forward or backward) from the MessageHeader, so its presence should be reviewed (is it needed to process the message?)
|
||||||
BUNDLE_BUNDLE_ENTRY_REVERSE = Entry {0} isn''t reachable by traversing forwards from first Bundle entry, and isn''t a resource type that is typically used that way - check this is not missed somewhere
|
Bundle_BUNDLE_Entry_Orphan_DOCUMENT = Entry {0} isn''t reachable by traversing links (forward or backward) from the Composition
|
||||||
|
BUNDLE_BUNDLE_ENTRY_REVERSE_R4 = Entry {0} isn''t reachable by traversing forwards from the Composition. Only Provenance is approved to be used this way (R4 section 3.3.1)
|
||||||
|
BUNDLE_BUNDLE_ENTRY_REVERSE_R5 = Entry {0} isn''t reachable by traversing forwards from the Composition. Check whether this should be linked directly from the composition if it's a source of narrative content
|
||||||
|
BUNDLE_BUNDLE_ENTRY_REVERSE_MSG = Entry {0} isn''t reachable by traversing forwards from the MessageHeader. Check that this is meant to be included (needed to process the message)
|
||||||
Bundle_BUNDLE_Entry_Type = The type ''{0}'' is not valid - no resources allowed here (allowed = {1})
|
Bundle_BUNDLE_Entry_Type = The type ''{0}'' is not valid - no resources allowed here (allowed = {1})
|
||||||
Bundle_BUNDLE_Entry_Type2 = The type ''{0}'' is not valid - must be {1} (allowed = {2})
|
Bundle_BUNDLE_Entry_Type2 = The type ''{0}'' is not valid - must be {1} (allowed = {2})
|
||||||
Bundle_BUNDLE_Entry_Type3_one = The type ''{1}'' is not valid - must be of type {2}
|
Bundle_BUNDLE_Entry_Type3_one = The type ''{1}'' is not valid - must be of type {2}
|
||||||
|
@ -23,7 +26,7 @@ Bundle_BUNDLE_FullUrl_NeedVersion = Entries matching fullURL {0} should declare
|
||||||
Bundle_BUNDLE_MultipleMatches = Multiple matches in bundle for reference {0}
|
Bundle_BUNDLE_MultipleMatches = Multiple matches in bundle for reference {0}
|
||||||
Bundle_BUNDLE_Not_Local = URN reference is not locally contained within the bundle {0}
|
Bundle_BUNDLE_Not_Local = URN reference is not locally contained within the bundle {0}
|
||||||
Bundle_MSG_Event_Count = Expected {0} but found {1} event elements
|
Bundle_MSG_Event_Count = Expected {0} but found {1} event elements
|
||||||
Bundle_Document_Date_Missing = A document must have a date
|
Bundle_Document_Date_Missing = A document must have a date (Bundle.timestamp)
|
||||||
Bundle_Document_Date_Missing_html = [(type = ''document'') implies (meta.lastUpdated.hasValue())]
|
Bundle_Document_Date_Missing_html = [(type = ''document'') implies (meta.lastUpdated.hasValue())]
|
||||||
CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter ''{0}'' type is {1}, but type here is {2}
|
CapabalityStatement_CS_SP_WrongType = Type mismatch - SearchParameter ''{0}'' type is {1}, but type here is {2}
|
||||||
CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has an ''all system'' value set of {1}, but the include has extra details
|
CodeSystem_CS_VS_IncludeDetails = CodeSystem {0} has an ''all system'' value set of {1}, but the include has extra details
|
||||||
|
@ -807,3 +810,10 @@ JSON_PROPERTY_VALUE_NO_QUOTES = The JSON property ''{0}'' has no quotes around t
|
||||||
JSON_COMMA_MISSING = A Comma is missing in the JSON
|
JSON_COMMA_MISSING = A Comma is missing in the JSON
|
||||||
JSON_COMMA_EXTRA = There is an extra comma at the end of the {0} in the JSON
|
JSON_COMMA_EXTRA = There is an extra comma at the end of the {0} in the JSON
|
||||||
JSON_COMMENTS_NOT_ALLOWED = Comments are not allowed in JSON
|
JSON_COMMENTS_NOT_ALLOWED = Comments are not allowed in JSON
|
||||||
|
BUNDLE_LINK_UNKNOWN = The link relationship type ''{0}'' is unknown and not allowed in this context
|
||||||
|
BUNDLE_LINK_SEARCH_PROHIBITED = The link relationship type ''{0}'' used in search sets is prohibited in this context
|
||||||
|
BUNDLE_LINK_SEARCH_NO_DUPLICATES = The link relationship type ''{0}'' can only occur once
|
||||||
|
BUNDLE_LINK_STYELSHEET_EXTERNAL = External Stylesheets other than https://hl7.org/fhir/fhir.css SHOULD not be used
|
||||||
|
BUNDLE_LINK_STYELSHEET_INSECURE = The stylesheet reference is not secure
|
||||||
|
BUNDLE_LINK_STYELSHEET_LINKABLE = The stylesheet reference is not a resolvable link
|
||||||
|
BUNDLE_LINK_STYELSHEET_NOT_FOUND = The stylesheet reference could not be resolved in this bundle
|
||||||
|
|
|
@ -44,10 +44,19 @@ public class BundleValidator extends BaseValidator {
|
||||||
|
|
||||||
public boolean validateBundle(List<ValidationMessage> errors, Element bundle, NodeStack stack, boolean checkSpecials, ValidatorHostContext hostContext, PercentageTracker pct, ValidationMode mode) {
|
public boolean validateBundle(List<ValidationMessage> errors, Element bundle, NodeStack stack, boolean checkSpecials, ValidatorHostContext hostContext, PercentageTracker pct, ValidationMode mode) {
|
||||||
boolean ok = true;
|
boolean ok = true;
|
||||||
List<Element> entries = new ArrayList<Element>();
|
|
||||||
bundle.getNamedChildren(ENTRY, entries);
|
|
||||||
String type = bundle.getNamedChildValue(TYPE);
|
String type = bundle.getNamedChildValue(TYPE);
|
||||||
type = StringUtils.defaultString(type);
|
type = StringUtils.defaultString(type);
|
||||||
|
List<Element> entries = new ArrayList<Element>();
|
||||||
|
bundle.getNamedChildren(ENTRY, entries);
|
||||||
|
|
||||||
|
List<Element> links = new ArrayList<Element>();
|
||||||
|
bundle.getNamedChildren(LINK, links);
|
||||||
|
if (links.size() > 0) {
|
||||||
|
int i = 0;
|
||||||
|
for (Element l : links) {
|
||||||
|
ok = validateLink(errors, bundle, links, l, stack.push(l, i++, null, null), type, entries) && ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (entries.size() == 0) {
|
if (entries.size() == 0) {
|
||||||
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, stack.getLiteralPath(), !(type.equals(DOCUMENT) || type.equals(MESSAGE)), I18nConstants.BUNDLE_BUNDLE_ENTRY_NOFIRST) && ok;
|
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, stack.getLiteralPath(), !(type.equals(DOCUMENT) || type.equals(MESSAGE)), I18nConstants.BUNDLE_BUNDLE_ENTRY_NOFIRST) && ok;
|
||||||
|
@ -68,7 +77,7 @@ public class BundleValidator extends BaseValidator {
|
||||||
if (!VersionUtilities.isThisOrLater(FHIRVersion._4_0_1.getDisplay(), bundle.getProperty().getStructure().getFhirVersion().getDisplay())) {
|
if (!VersionUtilities.isThisOrLater(FHIRVersion._4_0_1.getDisplay(), bundle.getProperty().getStructure().getFhirVersion().getDisplay())) {
|
||||||
ok = handleSpecialCaseForLastUpdated(bundle, errors, stack) && ok;
|
ok = handleSpecialCaseForLastUpdated(bundle, errors, stack) && ok;
|
||||||
}
|
}
|
||||||
ok = checkAllInterlinked(errors, entries, stack, bundle, true) && ok;
|
ok = checkAllInterlinked(errors, entries, stack, bundle, false) && ok;
|
||||||
}
|
}
|
||||||
if (type.equals(MESSAGE)) {
|
if (type.equals(MESSAGE)) {
|
||||||
Element resource = firstEntry.getNamedChild(RESOURCE);
|
Element resource = firstEntry.getNamedChild(RESOURCE);
|
||||||
|
@ -76,7 +85,7 @@ public class BundleValidator extends BaseValidator {
|
||||||
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath(ENTRY, PATH_ARG), resource != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOFIRSTRESOURCE)) {
|
if (rule(errors, NO_RULE_DATE, IssueType.INVALID, firstEntry.line(), firstEntry.col(), stack.addToLiteralPath(ENTRY, PATH_ARG), resource != null, I18nConstants.BUNDLE_BUNDLE_ENTRY_NOFIRSTRESOURCE)) {
|
||||||
ok = validateMessage(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id) && ok;
|
ok = validateMessage(errors, entries, resource, firstStack.push(resource, -1, null, null), fullUrl, id) && ok;
|
||||||
}
|
}
|
||||||
ok = checkAllInterlinked(errors, entries, stack, bundle, VersionUtilities.isR5Ver(context.getVersion())) && ok;
|
ok = checkAllInterlinked(errors, entries, stack, bundle, true) && ok;
|
||||||
}
|
}
|
||||||
if (type.equals(SEARCHSET)) {
|
if (type.equals(SEARCHSET)) {
|
||||||
checkSearchSet(errors, bundle, entries, stack);
|
checkSearchSet(errors, bundle, entries, stack);
|
||||||
|
@ -133,6 +142,151 @@ public class BundleValidator extends BaseValidator {
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean validateLink(List<ValidationMessage> errors, Element bundle, List<Element> links, Element link, NodeStack stack, String type, List<Element> entries) {
|
||||||
|
switch (type) {
|
||||||
|
case "document": return validateDocumentLink(errors, bundle, links, link, stack, entries);
|
||||||
|
case "message": return validateMessageLink(errors, bundle, links, link, stack, entries);
|
||||||
|
case "history":
|
||||||
|
case "searchset": return validateSearchLink(errors, bundle, links, link, stack);
|
||||||
|
case "collection": return validateCollectionLink(errors, bundle, links, link, stack);
|
||||||
|
case "subscription-notification": return validateSubscriptionLink(errors, bundle, links, link, stack);
|
||||||
|
case "transaction":
|
||||||
|
case "transaction-response":
|
||||||
|
case "batch":
|
||||||
|
case "batch-response":
|
||||||
|
return validateTransactionOrBatchLink(errors, bundle, links, link, stack);
|
||||||
|
default:
|
||||||
|
return true; // unknown document type, deal with that elsewhere
|
||||||
|
}
|
||||||
|
// rule(errors, "2022-12-09", IssueType.INVALID, l.line(), l.col(), stack.getLiteralPath(), false, I18nConstants.BUNDLE_LINK_UNKNOWN, );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateDocumentLink(List<ValidationMessage> errors, Element bundle, List<Element> links, Element link, NodeStack stack, List<Element> entries) {
|
||||||
|
boolean ok = true;
|
||||||
|
Element relE = link.getNamedChild("relation");
|
||||||
|
if (relE != null) {
|
||||||
|
NodeStack relStack = stack.push(relE, -1, null, null);
|
||||||
|
String rel = relE.getValue();
|
||||||
|
ok = rule(errors, "2022-12-09", IssueType.INVALID, relE.line(), relE.col(), relStack.getLiteralPath(), !Utilities.existsInList(rel, "first", "previous", "next", "last"), I18nConstants.BUNDLE_LINK_SEARCH_PROHIBITED, rel);
|
||||||
|
if ("self".equals(rel)) {
|
||||||
|
ok = rule(errors, "2022-12-09", IssueType.INVALID, relE.line(), relE.col(), relStack.getLiteralPath(), relationshipUnique(rel, link, links), I18nConstants.BUNDLE_LINK_SEARCH_NO_DUPLICATES, rel) && ok;
|
||||||
|
}
|
||||||
|
if ("stylesheet".equals(rel)) {
|
||||||
|
Element urlE = link.getNamedChild("url");
|
||||||
|
if (urlE != null) {
|
||||||
|
NodeStack urlStack = stack.push(urlE, -1, null, null);
|
||||||
|
String url = urlE.getValue();
|
||||||
|
if (url != null) {
|
||||||
|
if (Utilities.isAbsoluteUrl(url)) {
|
||||||
|
// todo: do we need to consider rel = base?
|
||||||
|
if (url.equals("https://hl7.org/fhir/fhir.css")) {
|
||||||
|
// well, this is ok!
|
||||||
|
} else {
|
||||||
|
warning(errors, "2022-12-09", IssueType.BUSINESSRULE, urlE.line(), urlE.col(), urlStack.getLiteralPath(), false, I18nConstants.BUNDLE_LINK_STYELSHEET_EXTERNAL);
|
||||||
|
if (url.startsWith("http://")) {
|
||||||
|
warning(errors, "2022-12-09", IssueType.BUSINESSRULE, urlE.line(), urlE.col(), urlStack.getLiteralPath(), false, I18nConstants.BUNDLE_LINK_STYELSHEET_INSECURE);
|
||||||
|
}
|
||||||
|
if (!Utilities.isAbsoluteUrlLinkable(url)) {
|
||||||
|
warning(errors, "2022-12-09", IssueType.BUSINESSRULE, urlE.line(), urlE.col(), urlStack.getLiteralPath(), false, I18nConstants.BUNDLE_LINK_STYELSHEET_LINKABLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// has to resolve in the bundle
|
||||||
|
boolean found = false;
|
||||||
|
for (Element e : entries) {
|
||||||
|
Element res = e.getNamedChild("resource");
|
||||||
|
if (res != null && (""+res.fhirType()+"/"+res.getIdBase()).equals(url)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ok = rule(errors, "2022-12-09", IssueType.NOTFOUND, urlE.line(), urlE.col(), urlStack.getLiteralPath(), found, I18nConstants.BUNDLE_LINK_STYELSHEET_NOT_FOUND) && ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateMessageLink(List<ValidationMessage> errors, Element bundle, List<Element> links, Element link, NodeStack stack, List<Element> entries) {
|
||||||
|
boolean ok = true;
|
||||||
|
Element relE = link.getNamedChild("relation");
|
||||||
|
if (relE != null) {
|
||||||
|
NodeStack relStack = stack.push(relE, -1, null, null);
|
||||||
|
String rel = relE.getValue();
|
||||||
|
ok = rule(errors, "2022-12-09", IssueType.INVALID, relE.line(), relE.col(), relStack.getLiteralPath(), !Utilities.existsInList(rel, "first", "previous", "next", "last"), I18nConstants.BUNDLE_LINK_SEARCH_PROHIBITED, rel);
|
||||||
|
if ("self".equals(rel)) {
|
||||||
|
ok = rule(errors, "2022-12-09", IssueType.INVALID, relE.line(), relE.col(), relStack.getLiteralPath(), relationshipUnique(rel, link, links), I18nConstants.BUNDLE_LINK_SEARCH_NO_DUPLICATES, rel) && ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateSearchLink(List<ValidationMessage> errors, Element bundle, List<Element> links, Element link, NodeStack stack) {
|
||||||
|
String rel = StringUtils.defaultString(link.getNamedChildValue("relation"));
|
||||||
|
if (Utilities.existsInList(rel, "first", "previous", "next", "last", "self")) {
|
||||||
|
return rule(errors, "2022-12-09", IssueType.INVALID, link.line(), link.col(), stack.getLiteralPath(), relationshipUnique(rel, link, links), I18nConstants.BUNDLE_LINK_SEARCH_NO_DUPLICATES, rel);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean relationshipUnique(String rel, Element link, List<Element> links) {
|
||||||
|
for (Element l : links) {
|
||||||
|
if (l != link && rel.equals(l.getNamedChildValue("relation"))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (l == link) {
|
||||||
|
// we only want to complain once, so we only look above this one
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateCollectionLink(List<ValidationMessage> errors, Element bundle, List<Element> links, Element link, NodeStack stack) {
|
||||||
|
boolean ok = true;
|
||||||
|
Element relE = link.getNamedChild("relation");
|
||||||
|
if (relE != null) {
|
||||||
|
NodeStack relStack = stack.push(relE, -1, null, null);
|
||||||
|
String rel = relE.getValue();
|
||||||
|
ok = rule(errors, "2022-12-09", IssueType.INVALID, relE.line(), relE.col(), relStack.getLiteralPath(), !Utilities.existsInList(rel, "first", "previous", "next", "last"), I18nConstants.BUNDLE_LINK_SEARCH_PROHIBITED, rel);
|
||||||
|
if ("self".equals(rel)) {
|
||||||
|
ok = rule(errors, "2022-12-09", IssueType.INVALID, relE.line(), relE.col(), relStack.getLiteralPath(), relationshipUnique(rel, link, links), I18nConstants.BUNDLE_LINK_SEARCH_NO_DUPLICATES, rel) && ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateSubscriptionLink(List<ValidationMessage> errors, Element bundle, List<Element> links, Element link, NodeStack stack) {
|
||||||
|
boolean ok = true;
|
||||||
|
Element relE = link.getNamedChild("relation");
|
||||||
|
if (relE != null) {
|
||||||
|
NodeStack relStack = stack.push(relE, -1, null, null);
|
||||||
|
String rel = relE.getValue();
|
||||||
|
ok = rule(errors, "2022-12-09", IssueType.INVALID, relE.line(), relE.col(), relStack.getLiteralPath(), !Utilities.existsInList(rel, "first", "previous", "next", "last"), I18nConstants.BUNDLE_LINK_SEARCH_PROHIBITED, rel);
|
||||||
|
if ("self".equals(rel)) {
|
||||||
|
ok = rule(errors, "2022-12-09", IssueType.INVALID, relE.line(), relE.col(), relStack.getLiteralPath(), relationshipUnique(rel, link, links), I18nConstants.BUNDLE_LINK_SEARCH_NO_DUPLICATES, rel) && ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateTransactionOrBatchLink(List<ValidationMessage> errors, Element bundle, List<Element> links, Element link, NodeStack stack) {
|
||||||
|
boolean ok = true;
|
||||||
|
Element relE = link.getNamedChild("relation");
|
||||||
|
if (relE != null) {
|
||||||
|
NodeStack relStack = stack.push(relE, -1, null, null);
|
||||||
|
String rel = relE.getValue();
|
||||||
|
ok = rule(errors, "2022-12-09", IssueType.INVALID, relE.line(), relE.col(), relStack.getLiteralPath(), !Utilities.existsInList(rel, "first", "previous", "next", "last"), I18nConstants.BUNDLE_LINK_SEARCH_PROHIBITED, rel);
|
||||||
|
if ("self".equals(rel)) {
|
||||||
|
ok = rule(errors, "2022-12-09", IssueType.INVALID, relE.line(), relE.col(), relStack.getLiteralPath(), relationshipUnique(rel, link, links), I18nConstants.BUNDLE_LINK_SEARCH_NO_DUPLICATES, rel) && ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
private void checkSearchSet(List<ValidationMessage> errors, Element bundle, List<Element> entries, NodeStack stack) {
|
private void checkSearchSet(List<ValidationMessage> errors, Element bundle, List<Element> entries, NodeStack stack) {
|
||||||
// warning: should have self link
|
// warning: should have self link
|
||||||
List<Element> links = new ArrayList<Element>();
|
List<Element> links = new ArrayList<Element>();
|
||||||
|
@ -413,7 +567,7 @@ public class BundleValidator extends BaseValidator {
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkAllInterlinked(List<ValidationMessage> errors, List<Element> entries, NodeStack stack, Element bundle, boolean isError) {
|
private boolean checkAllInterlinked(List<ValidationMessage> errors, List<Element> entries, NodeStack stack, Element bundle, boolean isMessage) {
|
||||||
boolean ok = true;
|
boolean ok = true;
|
||||||
List<EntrySummary> entryList = new ArrayList<>();
|
List<EntrySummary> entryList = new ArrayList<>();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -447,6 +601,7 @@ public class BundleValidator extends BaseValidator {
|
||||||
|
|
||||||
Set<EntrySummary> visited = new HashSet<>();
|
Set<EntrySummary> visited = new HashSet<>();
|
||||||
visitLinked(visited, entryList.get(0));
|
visitLinked(visited, entryList.get(0));
|
||||||
|
visitBundleLinks(visited, entryList, bundle);
|
||||||
boolean foundRevLinks;
|
boolean foundRevLinks;
|
||||||
do {
|
do {
|
||||||
foundRevLinks = false;
|
foundRevLinks = false;
|
||||||
|
@ -460,10 +615,22 @@ public class BundleValidator extends BaseValidator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (add) {
|
if (add) {
|
||||||
warning(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, e.getEntry().line(), e.getEntry().col(),
|
if (isMessage) {
|
||||||
|
hint(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, e.getEntry().line(), e.getEntry().col(),
|
||||||
stack.addToLiteralPath(ENTRY + '[' + (i + 1) + ']'), isExpectedToBeReverse(e.getResource().fhirType()),
|
stack.addToLiteralPath(ENTRY + '[' + (i + 1) + ']'), isExpectedToBeReverse(e.getResource().fhirType()),
|
||||||
I18nConstants.BUNDLE_BUNDLE_ENTRY_REVERSE, (e.getEntry().getChildValue(FULL_URL) != null ? "'" + e.getEntry().getChildValue(FULL_URL) + "'" : ""));
|
I18nConstants.BUNDLE_BUNDLE_ENTRY_REVERSE_MSG, (e.getEntry().getChildValue(FULL_URL) != null ? "'" + e.getEntry().getChildValue(FULL_URL) + "'" : ""));
|
||||||
// System.out.println("Found reverse links for "+e.getIndex());
|
} else {
|
||||||
|
// this was illegal up to R4B, but changed to be legal in R5
|
||||||
|
if (VersionUtilities.isR5VerOrLater(context.getVersion())) {
|
||||||
|
hint(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, e.getEntry().line(), e.getEntry().col(),
|
||||||
|
stack.addToLiteralPath(ENTRY + '[' + (i + 1) + ']'), isExpectedToBeReverse(e.getResource().fhirType()),
|
||||||
|
I18nConstants.BUNDLE_BUNDLE_ENTRY_REVERSE_R4, (e.getEntry().getChildValue(FULL_URL) != null ? "'" + e.getEntry().getChildValue(FULL_URL) + "'" : ""));
|
||||||
|
} else {
|
||||||
|
warning(errors, NO_RULE_DATE, IssueType.INVALID, e.getEntry().line(), e.getEntry().col(),
|
||||||
|
stack.addToLiteralPath(ENTRY + '[' + (i + 1) + ']'), isExpectedToBeReverse(e.getResource().fhirType()),
|
||||||
|
I18nConstants.BUNDLE_BUNDLE_ENTRY_REVERSE_R4, (e.getEntry().getChildValue(FULL_URL) != null ? "'" + e.getEntry().getChildValue(FULL_URL) + "'" : ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
foundRevLinks = true;
|
foundRevLinks = true;
|
||||||
visitLinked(visited, e);
|
visitLinked(visited, e);
|
||||||
}
|
}
|
||||||
|
@ -474,10 +641,10 @@ public class BundleValidator extends BaseValidator {
|
||||||
i = 0;
|
i = 0;
|
||||||
for (EntrySummary e : entryList) {
|
for (EntrySummary e : entryList) {
|
||||||
Element entry = e.getEntry();
|
Element entry = e.getEntry();
|
||||||
if (isError) {
|
if (isMessage) {
|
||||||
ok = rule(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath(ENTRY + '[' + (i + 1) + ']'), visited.contains(e), I18nConstants.BUNDLE_BUNDLE_ENTRY_ORPHAN, (entry.getChildValue(FULL_URL) != null ? "'" + entry.getChildValue(FULL_URL) + "'" : "")) && ok;
|
warning(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath(ENTRY + '[' + (i + 1) + ']'), visited.contains(e), I18nConstants.BUNDLE_BUNDLE_ENTRY_ORPHAN_MESSAGE, (entry.getChildValue(FULL_URL) != null ? "'" + entry.getChildValue(FULL_URL) + "'" : ""));
|
||||||
} else {
|
} else {
|
||||||
warning(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath(ENTRY + '[' + (i + 1) + ']'), visited.contains(e), I18nConstants.BUNDLE_BUNDLE_ENTRY_ORPHAN, (entry.getChildValue(FULL_URL) != null ? "'" + entry.getChildValue(FULL_URL) + "'" : ""));
|
ok = rule(errors, NO_RULE_DATE, IssueType.INFORMATIONAL, entry.line(), entry.col(), stack.addToLiteralPath(ENTRY + '[' + (i + 1) + ']'), visited.contains(e), I18nConstants.BUNDLE_BUNDLE_ENTRY_ORPHAN_DOCUMENT, (entry.getChildValue(FULL_URL) != null ? "'" + entry.getChildValue(FULL_URL) + "'" : "")) && ok;
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
@ -486,6 +653,26 @@ public class BundleValidator extends BaseValidator {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void visitBundleLinks(Set<EntrySummary> visited, List<EntrySummary> entryList, Element bundle) {
|
||||||
|
List<Element> links = bundle.getChildrenByName("link");
|
||||||
|
for (Element link : links) {
|
||||||
|
String rel = link.getNamedChildValue("relation");
|
||||||
|
String url = link.getNamedChildValue("url");
|
||||||
|
if (rel != null && url != null) {
|
||||||
|
if (Utilities.existsInList(rel, "stylesheet")) {
|
||||||
|
for (EntrySummary e : entryList) {
|
||||||
|
if (e.getResource() != null) {
|
||||||
|
if (url.equals(e.getResource().fhirType()+"/"+e.getResource().getIdBase())) {
|
||||||
|
visited.add(e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private boolean isExpectedToBeReverse(String fhirType) {
|
private boolean isExpectedToBeReverse(String fhirType) {
|
||||||
return Utilities.existsInList(fhirType, "Provenance");
|
return Utilities.existsInList(fhirType, "Provenance");
|
||||||
}
|
}
|
||||||
|
|
|
@ -2068,3 +2068,12 @@ v: {
|
||||||
"system" : "http://snomed.info/sct"
|
"system" : "http://snomed.info/sct"
|
||||||
}
|
}
|
||||||
-------------------------------------------------------------------------------------
|
-------------------------------------------------------------------------------------
|
||||||
|
{"code" : {
|
||||||
|
"code" : "text/css"
|
||||||
|
}, "url": "http://hl7.org/fhir/ValueSet/mimetypes", "version": "4.0.1", "lang":"null", "useServer":"true", "useClient":"true", "guessSystem":"true", "valueSetMode":"ALL_CHECKS", "versionFlexible":"false"}####
|
||||||
|
v: {
|
||||||
|
"display" : "text/css",
|
||||||
|
"code" : "text/css",
|
||||||
|
"system" : "urn:ietf:bcp:13"
|
||||||
|
}
|
||||||
|
-------------------------------------------------------------------------------------
|
||||||
|
|
Loading…
Reference in New Issue