Skip to content

Commit

Permalink
Validate earliestAllowed and latestAllowed FHIRVersion for extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
Grahame Grieve committed Jan 7, 2025
1 parent 4ccaae1 commit 63e2ca3
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ public class ToolingExtensions {
public static final String EXT_SUPPRESS_RESOURCE_TYPE = "http://hl7.org/fhir/tools/StructureDefinition/json-suppress-resourcetype";
public static final String EXT_PROFILE_VIEW_HINT = "http://hl7.org/fhir/tools/StructureDefinition/view-hint";
public static final String EXT_SNAPSHOT_BEHAVIOR = "http://hl7.org/fhir/tools/StructureDefinition/snapshot-behavior";
public static final String EXT_EARLIEST_FHIR_VERSION = "http://hl7.org/fhir/StructureDefinition/earliestAllowedFHIRVersion";
public static final String EXT_LATEST_FHIR_VERSION = "http://hl7.org/fhir/StructureDefinition/latestAllowedFHIRVersion";

// specific extension helpers

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ private int compareString(String s1, String s2) {
private int compareInteger(String s1, String s2) {
if (s1 == null) {
return s2 == null ? 0 : 1;
} else if (s2 == null) {
return -1;
} else {
return Integer.compare(Integer.parseInt(s1), Integer.parseInt(s2));
}
Expand Down Expand Up @@ -329,7 +331,7 @@ public static boolean isSemVer(String version) {
if (Utilities.noString(version)) {
return false;
}
return version.matches("^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-\\+]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-\\+][0-9a-zA-Z-\\+]*))*))?$");
return version.matches("^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-\\+]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-\\+][0-9a-zA-Z-\\+]*))*))?)?$");
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1165,4 +1165,6 @@ public class I18nConstants {
public static final String NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG_ERR = "NO_VALID_DISPLAY_FOUND_NONE_FOR_LANG_ERR";
public static final String CONCEPTMAP_VS_NOT_A_VS = "CONCEPTMAP_VS_NOT_A_VS";
public static final String SD_DERIVATION_NO_CONCRETE = "SD_DERIVATION_NO_CONCRETE";
public static final String EXTENSION_FHIR_VERSION_EARLIEST = "EXTENSION_FHIR_VERSION_EARLIEST";
public static final String EXTENSION_FHIR_VERSION_LATEST = "EXTENSION_FHIR_VERSION_LATEST";
}
5 changes: 3 additions & 2 deletions org.hl7.fhir.utilities/src/main/resources/Messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -1197,5 +1197,6 @@ VS_EXP_IMPORT_ERROR_X = Unable to expand excluded value set ''{0}'', but no erro
VS_EXP_IMPORT_ERROR_TOO_COSTLY = Unable to expand excluded value set ''{0}'': too costly
VS_EXP_FILTER_UNK = ValueSet ''{0}'' Filter by property ''{1}'' and op ''{2}'' is not supported yet
CONCEPTMAP_VS_NOT_A_VS = Reference must be to a ValueSet, but found a {0} instead
SD_DERIVATION_NO_CONCRETE = {0} is labeled as an abstract type, but no concrete descendents were found (check definitions - this is usually an error unless concrete definitions are in some other package)

SD_DERIVATION_NO_CONCRETE = {0} is labeled as an abstract type, but no concrete descendants were found (check definitions - this is usually an error unless concrete definitions are in some other package)
EXTENSION_FHIR_VERSION_EARLIEST = The definition of the extension ''{0}'' specifies that the earliest version it can be used with is {1} (v{2}), but this context of use is version {3} (v{4})
EXTENSION_FHIR_VERSION_LATEST = The definition of the extension ''{0}'' specifies that the latest version it can be used with is {1} (v{2}), but this context of use is version {3} (v{4})
Original file line number Diff line number Diff line change
Expand Up @@ -2536,6 +2536,20 @@ private boolean checkExtensionContext(Object appContext, List<ValidationMessage>
}
Collections.sort(plist); // logical paths are a set, but we want a predictable order for error messages

if (definition.hasExtension(ToolingExtensions.EXT_EARLIEST_FHIR_VERSION) || definition.hasExtension(ToolingExtensions.EXT_LATEST_FHIR_VERSION)) {
if (definition.hasExtension(ToolingExtensions.EXT_EARLIEST_FHIR_VERSION)) {
String v = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_EARLIEST_FHIR_VERSION);
ok = rule(errors, "2025-01-07", IssueType.BUSINESSRULE, container.line(), container.col(), stack.getLiteralPath(),
VersionUtilities.compareVersions(VersionUtilities.getMajMin(context.getVersion()), v) >= 0,
I18nConstants.EXTENSION_FHIR_VERSION_EARLIEST, extUrl, VersionUtilities.getNameForVersion(v), v, VersionUtilities.getNameForVersion(context.getVersion()), context.getVersion()) && ok;
}
if (definition.hasExtension(ToolingExtensions.EXT_LATEST_FHIR_VERSION)) {
String v = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_LATEST_FHIR_VERSION);
ok = rule(errors, "2025-01-07", IssueType.BUSINESSRULE, container.line(), container.col(), stack.getLiteralPath(),
VersionUtilities.compareVersions(VersionUtilities.getMajMin(context.getVersion()), v) <= 0,
I18nConstants.EXTENSION_FHIR_VERSION_LATEST, extUrl, VersionUtilities.getNameForVersion(v), v, VersionUtilities.getNameForVersion(context.getVersion()), context.getVersion()) && ok;
}
}
for (StructureDefinitionContextComponent ctxt : fixContexts(extUrl, definition.getContext())) {
if (ok) {
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,18 @@ public void test() throws Exception {
}
}
}
if (content.has("supporting5")) {
for (JsonElement e : content.getAsJsonArray("supporting5")) {
String filename = e.getAsString();
String contents = TestingUtilities.loadTestResource("validator", filename);
CanonicalResource mr = (CanonicalResource) loadResource(filename, contents, "5.0.0");
logOutput("load resource "+mr.getUrl());
val.getContext().cacheResource(mr);
if (mr instanceof ImplementationGuide) {
val.getImplementationGuides().add((ImplementationGuide) mr);
}
}
}
val.getBundleValidationRules().clear();
if (content.has("bundle-param")) {
val.getBundleValidationRules().add(new BundleValidationRule().setRule(content.getAsJsonObject("bundle-param").get("rule").getAsString()).setProfile( content.getAsJsonObject("bundle-param").get("profile").getAsString()));
Expand Down Expand Up @@ -520,6 +532,10 @@ public StructureDefinition loadProfile(String filename, String contents, List<Va
}

public Resource loadResource(String filename, String contents) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException {
return loadResource(filename, contents, version);
}

public Resource loadResource(String filename, String contents, String version) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException {
try (InputStream inputStream = IOUtils.toInputStream(contents, Charsets.UTF_8)) {
if (filename.contains(".json")) {
if (Constants.VERSION.equals(version) || "5.0".equals(version))
Expand Down

0 comments on commit 63e2ca3

Please sign in to comment.