This commit is contained in:
Grahame Grieve 2021-08-27 08:52:26 +10:00
commit b3b43c17d1
26 changed files with 120 additions and 44 deletions

View File

@ -1,8 +0,0 @@
* Differential element fields minValue/maxValue are now correctly treated in the snapshot generation process
* Conversion context added to conversions process
* Users can now define custom behavior for CodeSystems, Extensions, BundleEntries, and Types by extending BaseAdvisor.
* Resource Conversions are now thread-safe, each using their own instance of the conversion context that is unique
* ConversionFactory classes are statically accessed, to minimize changes downstream
* I need to add more tests, there were very few to begin with, and it's my next task
* All conversion libraries and no play makes Mark a dull boy
* Exposed showMessagesFromReferences on the command line interface to support reporting validation errors on referenced types (particularly useful when validating messages & documents)

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.4.11-SNAPSHOT</version>
<version>5.4.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -41,7 +41,7 @@ import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.model.FhirPublication;
import org.hl7.fhir.utilities.Utilities;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.io.File;
import java.io.FileInputStream;
@ -84,18 +84,18 @@ public class IGPackConverter102 extends BaseAdvisor_10_30 {
}
@Override
public boolean ignoreEntry(@NotNull BundleEntryComponent src, @NotNull FhirPublication publication) {
public boolean ignoreEntry(@Nonnull BundleEntryComponent src, @Nonnull FhirPublication publication) {
return false;
}
@Override
public void handleCodeSystem(@NotNull CodeSystem tgtcs, @NotNull ValueSet vs) {
public void handleCodeSystem(@Nonnull CodeSystem tgtcs, @Nonnull ValueSet vs) {
cslist.addEntry().setFullUrl(tgtcs.getUrl()).setResource(tgtcs);
}
@Override
public CodeSystem getCodeSystem(@NotNull ValueSet src) {
public CodeSystem getCodeSystem(@Nonnull ValueSet src) {
return null;
}

View File

@ -32,11 +32,11 @@ package org.hl7.fhir.convertors.misc;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_10_40;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.ValueSet;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
public class IGR2ConvertorAdvisor extends BaseAdvisor_10_40 {
@Override
public void handleCodeSystem(@NotNull CodeSystem cs, @NotNull ValueSet vs) {
public void handleCodeSystem(@Nonnull CodeSystem cs, @Nonnull ValueSet vs) {
cs.setId(vs.getId());
}
}

View File

@ -33,12 +33,12 @@ package org.hl7.fhir.convertors.misc;
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_10_50;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.ValueSet;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
public class IGR2ConvertorAdvisor5 extends BaseAdvisor_10_50 {
@Override
public void handleCodeSystem(@NotNull CodeSystem cs, @NotNull ValueSet vs) {
public void handleCodeSystem(@Nonnull CodeSystem cs, @Nonnull ValueSet vs) {
cs.setId(vs.getId());
}
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.4.11-SNAPSHOT</version>
<version>5.4.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.4.11-SNAPSHOT</version>
<version>5.4.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.4.11-SNAPSHOT</version>
<version>5.4.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.4.11-SNAPSHOT</version>
<version>5.4.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.4.11-SNAPSHOT</version>
<version>5.4.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -3,7 +3,7 @@ package org.hl7.fhir.r5.utils.client.network;
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
import java.io.IOException;
@ -27,7 +27,7 @@ public class RetryInterceptor implements Interceptor {
}
@Override
public @NotNull Response intercept(Interceptor.Chain chain) throws IOException {
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
Response response = null;

View File

@ -0,0 +1,18 @@
package org.hl7.fhir.r5.utils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class FHIRLexerTest {
@Test
@DisplayName("Test that a 'null' current value returns 'false' when FHIRLexer.isConstant() is called, and not NPE.")
void getCurrent() {
FHIRLexer lexer = new FHIRLexer(null, null);
String lexerCurrent = lexer.getCurrent();
Assertions.assertNull(lexerCurrent);
Assertions.assertFalse(lexer.isConstant());
}
}

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.4.11-SNAPSHOT</version>
<version>5.4.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.4.11-SNAPSHOT</version>
<version>5.4.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -347,6 +347,7 @@ public class I18nConstants {
public static final String REFERENCE_TO__CANNOT_BE_RESOLVED = "reference_to__cannot_be_resolved";
public static final String REFERENCE__REFERS_TO_A__NOT_A_VALUESET = "Reference__refers_to_a__not_a_ValueSet";
public static final String RESOURCE_RESOLUTION_SERVICES_NOT_PROVIDED = "Resource_resolution_services_not_provided";
public static final String RESOURCE_RES_ID_MALFORMED = "Resource_RES_ID_Malformed";
public static final String RESOURCE_RES_ID_MISSING = "Resource_RES_ID_Missing";
public static final String RESOURCE_RES_ID_PROHIBITED = "Resource_RES_ID_Prohibited";
public static final String RESOURCE_TYPE_MISMATCH_FOR___ = "Resource_type_mismatch_for___";

View File

@ -61,14 +61,7 @@ import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
/**
@ -143,6 +136,11 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
return names;
}
private List<String> reverseSorted(String[] keys) {
Arrays.sort(keys, Collections.reverseOrder());
return Arrays.asList(keys);
}
private NpmPackage loadPackageInfo(String path) throws IOException {
NpmPackage pi = NpmPackage.fromFolder(path);
return pi;
@ -316,7 +314,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
}
String foundPackage = null;
String foundVersion = null;
for (String f : sorted(new File(cacheFolder).list())) {
for (String f : reverseSorted(new File(cacheFolder).list())) {
File cf = new File(Utilities.path(cacheFolder, f));
if (cf.isDirectory()) {
if (f.equals(id + "#" + version) || (Utilities.noString(version) && f.startsWith(id + "#"))) {
@ -475,6 +473,7 @@ public class FilesystemPackageCacheManager extends BasePackageCacheManager imple
version = id.substring(id.indexOf("#")+1);
id = id.substring(0, id.indexOf("#"));
}
NpmPackage p = loadPackageFromCacheOnly(id, version);
if (p != null) {
if ("current".equals(version)) {

View File

@ -118,6 +118,7 @@ Reference_REF_NoType = Unable to determine type of target resource
Reference_REF_NotFound_Bundle = Bundled or contained reference not found within the bundle/resource {0}
Reference_REF_ResourceType = Matching reference for reference {0} has resourceType {1}
Reference_REF_WrongTarget = The type ''{0}'' is not a valid Target for this element (must be one of {1})
Resource_RES_ID_Malformed = Resource id not formatted correctly
Resource_RES_ID_Missing = Resource requires an id, but none is present
Resource_RES_ID_Prohibited = Resource has an id, but none is allowed
Terminology_PassThrough_TX_Message = {0} for ''{1}#{2}''

View File

@ -118,6 +118,7 @@ Reference_REF_NoType = Kan type van de doel-resource niet vaststellen
Reference_REF_NotFound_Bundle = Gebundelde of contained reference niet gevonden binnen de bundle/resource {0}
Reference_REF_ResourceType = Overeenkomende reference voor reference {0} heeft resourceType {1}
Reference_REF_WrongTarget = Het type ''{0}'' is geen geldig doel voor dit element (moet een zijn van {1})
Resource_RES_ID_Malformed = Resource id nicht richtig formatiert
Resource_RES_ID_Missing = Resource vereist een id, maar deze ontbreekt
Resource_RES_ID_Prohibited = Resource heeft een id, maar dat is niet toegestaan
Terminology_PassThrough_TX_Message = {0} voor ''{1}#{2}''

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.4.11-SNAPSHOT</version>
<version>5.4.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -5,7 +5,7 @@
<parent>
<groupId>ca.uhn.hapi.fhir</groupId>
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.4.11-SNAPSHOT</version>
<version>5.4.12-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

View File

@ -78,7 +78,7 @@ import org.hl7.fhir.utilities.VersionUtilities;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull;
/**
* This class allows you to host the java validator in another service, and use the services it has in a wider context. The way it works is
@ -106,10 +106,10 @@ public class NativeHostServices {
private class NH_10_50_Advisor extends BaseAdvisor_10_50 {
@Override
public void handleCodeSystem(@NotNull CodeSystem tgtcs, @NotNull ValueSet source) throws FHIRException {}
public void handleCodeSystem(@Nonnull CodeSystem tgtcs, @Nonnull ValueSet source) throws FHIRException {}
@Override
public CodeSystem getCodeSystem(@NotNull ValueSet src) throws FHIRException {
public CodeSystem getCodeSystem(@Nonnull ValueSet src) throws FHIRException {
throw new FHIRException("Code systems cannot be handled at this time"); // what to do? need thread local storage?
}
}

View File

@ -47,6 +47,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.codec.binary.Base64InputStream;
import org.apache.commons.lang3.NotImplementedException;
@ -168,6 +170,8 @@ import org.w3c.dom.Document;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import javax.annotation.Nonnull;
/**
* Thinking of using this in a java program? Don't!
@ -5165,14 +5169,37 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
ok = rule(errors, IssueType.INVALID, -1, -1, stack.getLiteralPath(), type.equals(resourceName), I18nConstants.VALIDATION_VAL_PROFILE_WRONGTYPE, type, resourceName);
if (ok) {
if (idstatus == IdStatus.REQUIRED && (element.getNamedChild(ID) == null))
if (idstatus == IdStatus.REQUIRED && (element.getNamedChild(ID) == null)) {
rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.RESOURCE_RES_ID_MISSING);
else if (idstatus == IdStatus.PROHIBITED && (element.getNamedChild(ID) != null))
} else if (idstatus == IdStatus.PROHIBITED && (element.getNamedChild(ID) != null)) {
rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.RESOURCE_RES_ID_PROHIBITED);
} else if ((idstatus == IdStatus.OPTIONAL || idstatus == IdStatus.REQUIRED)
&& (element.getNamedChild(ID) != null)
&& (!idFormattedCorrectly(element.getNamedChild(ID).getValue()))) {
rule(errors, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.RESOURCE_RES_ID_MALFORMED);
}
start(hostContext, errors, element, element, defn, stack); // root is both definition and type
}
}
/**
* FHIR IDs are constrained to any combination of upper- or lower-case ASCII letters ('A'..'Z', and 'a'..'z',
* numerals ('0'..'9'), '-' and '.', with a length limit of 64 characters. (This might be an integer, an un-prefixed
* OID, UUID or any other identifier pattern that meets these constraints.)
*
* As a heads up, a null String will pass this check.
*
* @param id The id to verify conformance to specification requirements.
* @return {@link Boolean#TRUE} if the passed in ID is a valid resource ID, {@link Boolean#FALSE} otherwise.
*/
protected static boolean idFormattedCorrectly(@Nonnull String id) {
String idRegex = "[A-Za-z0-9\\-\\.]{1,64}";
Pattern pattern = Pattern.compile(idRegex);
Matcher matcher = pattern.matcher(id);
return matcher.matches();
}
private NodeStack getFirstEntry(NodeStack bundle) {
List<Element> list = new ArrayList<Element>();
bundle.getElement().getNamedChildren(ENTRY, list);

View File

@ -28,6 +28,10 @@ The following parameters are supported:
the URL of an implementation guide or a package ([id]-[ver]) for
a built implementation guide or a local folder that contains a
set of conformance resources.
If you would like to load the latest unreleased version of the implementation guide or package,
please define the version as '#current'. If no version is provided, the latest version
in the package cache will be used, or if no such cached package is available, the
PackageCacheManager will load the latest from the the online package repo.
No default value. This parameter can appear any number of times
-tx [url]: the [base] url of a FHIR terminology service
Default value is http://tx.fhir.org. This parameter can appear once

View File

@ -0,0 +1,33 @@
package org.hl7.fhir.validation.instance;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.*;
class InstanceValidatorTest {
private static Stream<Arguments> provideIdsWithOutcomes() {
return Stream.of(
Arguments.of("1234", true),
Arguments.of("12-34", true),
Arguments.of("12_34", false),
Arguments.of("12.34", true),
Arguments.of("12/34", false),
Arguments.of("1234#", false),
Arguments.of("31415926535897932384626433832795028841971693993751058209749445923", false) // 65 digits
);
}
@ParameterizedTest
@MethodSource("provideIdsWithOutcomes")
void idFormattedCorrectly(String id, boolean expected) {
Assertions.assertEquals(InstanceValidator.idFormattedCorrectly(id), expected);
}
}

View File

@ -97,7 +97,7 @@ public class ValidationTests implements IEvaluationContext, IValidatorResourceFe
List<Object[]> objects = new ArrayList<Object[]>(examples.size());
for (String id : names) {
//if (id.equals("patient-translated-codes"))
//if (id.equals("bundle-documentation-miss-last-updated"))
objects.add(new Object[]{id, examples.get(id)});
}
return objects;

View File

@ -14,7 +14,7 @@
HAPI FHIR
-->
<artifactId>org.hl7.fhir.core</artifactId>
<version>5.4.11-SNAPSHOT</version>
<version>5.4.12-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>