Skip to content

Commit

Permalink
Publish Release 2.4.0
Browse files Browse the repository at this point in the history
Automerge Pull-Request for Release 2.4.0
  • Loading branch information
gematik1 authored Jul 3, 2024
2 parents bf9c486 + 2953d8b commit ef1035b
Show file tree
Hide file tree
Showing 176 changed files with 58,067 additions and 67 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Siehe [Release Notes](ReleaseNotes.md)

| **Modul** | **Version** |
|------------------------------------------------|-------------|
| E-Rezept | 2.2 |
| E-Rezept | 2.3 |
| Elektronische Arbeitsunfähigkeitsbescheinigung | 0.9 |
| FHIR Core | 1.0 |
| E-Rezept Abrechnungsdaten (experimentell) | 0.2 |
Expand Down Expand Up @@ -222,6 +222,10 @@ Optionen:
- `--verbose` - Verbode-Modus mit Debug-Protokoll-Ausgaben und INFORMATION- bzw. WARNING-Validierungsnachrichten
- `--no-profile-validity-period-check` - Deaktivierung der Zeitraumgültigkeitsprüfung der Profilversionen
- `--profile` - Angabe einer Profil-Canonical-URL zur Validierung. Falls angegeben, wird die Angabe der Instanz unter meta.profile ignoriert
- `--profile-filter` - Angabe eines regulären Ausdrucks zur Prüfung der unter meta.profile referenzierten Profile. Falls kein referenzierstes Profil dem regulären Ausdruck entspricht, wird die Instanz als invalide betrachtet. Beispiele zur Nutzung:
- Prüfen, dass es sich bei einer Instanz grundsätzlich um eine E-Rezept-Quittung und nicht etwa um eine Verordnung handelt: `--profile-filter "ErxReceipt|GEM_ERP_PR_Bundle"`
- Prüfen, dass es sich bei einer Instanz um eine E-Rezept-Verordnung (in der Profilversion 1.1) handelt: `--profile-filter "KBV_PR_ERP_Bundle\|1\.1"`
- Prüfen, dass es sich bei einer Instanz um Abgabedaten (GKV oder PKV) handelt: `--profile-filter "DAV-(PKV-)?PR-ERP-AbgabedatenBundle"`
- `--module-info` - Ausgabe der unterstützten Profile, Profilversionen und FHIR-Packages zu einem Validierungsmodul
- `--accepted-encodings` - Komma-separierte Liste mit den zu akzeptierenden Serialisierungsformaten der FHIR-Ressourcen. Unterstützte Werte: `xml`,`json`. Überschreibt die Modul-eigene Konfiguration.
- `--output` - Angabe eines Dateipfades um das Validierungsergebnis in Form einer FHIR OperationOutcome-Ressource bzw. eines Bundles mit OperationOutcome-Ressourcen in eine Datei zu schreiben. Beispiel: `--output c:\temp\output.xml`. Als Ausgabeformate werden sowohl `xml` als auch `json` unterstützt. Die Struktur der Ausgabedateien ist in der [weiterführenden Dokumentation](docs/profiles/fsh/fsh-generated/resources) zu finden.
Expand Down Expand Up @@ -255,7 +259,7 @@ Validierung einer FHIR-Ressource als String:
System.out.println(result.getValidationMessages());
```

Die Validierungseinstellungen auch angepasst werden:
Die Validierungseinstellungen können auch angepasst werden:

``` Java
ValidationModule erpModule = new ValidationModuleFactory().createValidationModule(SupportedValidationModule.ERP);
Expand All @@ -264,6 +268,7 @@ Die Validierungseinstellungen auch angepasst werden:
options.setProfiles(List.of("https://fhir.kbv.de/StructureDefinition/KBV_PR_ERP_Bundle|1.0.1"));
options.setAcceptedEncodings(List.of("xml", "json"));
options.setValidationMessagesFilter(ValidationMessagesFilter.KEEP_ALL);
options.setProfileFilterRegex("KBV_PR_ERP_Bundle");
ValidationResult result = erpModule.validateFile("c:/temp/KBV_PR_ERP_Bundle.xml", options);
System.out.println(result.isValid());
System.out.println(result.getValidationMessages());
Expand Down
14 changes: 14 additions & 0 deletions ReleaseNotes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@

# Release Notes Gematik Referenzvalidator

## Release 2.4.0

### added

- validation option `profile-filter` to validate meta.profile references against a specified regular expression (cf. [README.md](README.md))
- ERP module (version update to 2.3):
- integrated [KBV Schlüsseltabelle S_KBV_DMP v1.06](https://applications.kbv.de/S_KBV_DMP.xhtml) (valid from 1.10.2024)

### changed:

- ERP module (version update to 2.3):
- validity of 1.3 profiles from de.abda.erezeptabgabedaten package extended till 15.4.2025
- integrated [de.gkvsv.erezeptabgabedaten#1.4.1](https://simplifier.net/packages/de.abda.erezeptabgabedaten/1.4.1) and [de.abda.erezeptabgabedatenbasis#1.4.1](https://simplifier.net/packages/de.abda.erezeptabgabedatenbasis/1.4.1), which replace the corresponding 1.4.0 packages

## Release 2.3.0

### added:
Expand Down
2 changes: 1 addition & 1 deletion cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>referencevalidator</artifactId>
<groupId>de.gematik.refv</groupId>
<version>2.3.0</version>
<version>2.4.0</version>
</parent>
<properties>
<integrationtest.folder>${basedir}/target/test-classes/pluginloader-integration-test</integrationtest.folder>
Expand Down
11 changes: 8 additions & 3 deletions cli/src/main/java/de/gematik/refv/cli/ReferenceValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -63,6 +62,7 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

Expand Down Expand Up @@ -90,6 +90,9 @@ public class ReferenceValidator implements Runnable {
@CommandLine.Option(names = {"-p", "--profile"}, description = "Canonical url of a profile to validate against", required = false)
private String profile;

@CommandLine.Option(names = {"-pf", "--profile-filter"}, description = "Regular expression to proceed only with those resources, where meta.profile matches the expression", required = false)
private Pattern profileFilter;

@CommandLine.Option(names = {"-mi", "--module-info"}, description = "Print profiles supported by a module", required = false)
private boolean showModuleConfiguration;

Expand Down Expand Up @@ -148,10 +151,10 @@ public void run() {

validateAndPrintResult(validator, getValidationOptions());
} catch (Exception e){
log.error("Exception", e);
log.debug("Exception", e);
log.error("An error occurred during validation: " + e.getMessage());
if(outputFilePath != null)
writeExceptionAsOperationOutcome(outputFilePath, e);

}
}

Expand All @@ -165,6 +168,8 @@ private ValidationOptions getValidationOptions() {
validationOptions.setProfileValidityPeriodCheckStrategy(ProfileValidityPeriodCheckStrategy.IGNORE);
if(isVerbose)
validationOptions.setValidationMessagesFilter(ValidationMessagesFilter.KEEP_ALL);
if(profileFilter != null)
validationOptions.setProfileFilterRegex(profileFilter.pattern());
return validationOptions;
}

Expand Down
14 changes: 14 additions & 0 deletions cli/src/test/java/de/gematik/refv/cli/ReferenceValidatorIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.parallel.Execution;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
Expand All @@ -40,6 +42,7 @@

import static org.assertj.core.api.Assertions.assertThat;

@Execution(ExecutionMode.SAME_THREAD)
@Slf4j
class ReferenceValidatorIT {

Expand Down Expand Up @@ -253,4 +256,15 @@ void testValidationOfDirectoryFileAndFileWithException() {

assertThat(actualBundle.equalsShallow(expectedBundle)).isTrue();
}

@Test
@SneakyThrows
void testProfileFilterWhichDoesntMatchAnyMetaProfileReference() {
String input = "erp src/test/resources/erp-test.xml --profile-filter NON-MATCHING-PATTERN";
String [] args = input.split(" ");

ReferenceValidator.main(args);

assertThat(appender.getLogs().stream()).as("No errors found while profile filter doesn't match").anyMatch(e -> e.getMessage().toString().contains("REFV_PROFILE_FILTER_MISMATCH"));
}
}
2 changes: 1 addition & 1 deletion commons/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>referencevalidator</artifactId>
<groupId>de.gematik.refv</groupId>
<version>2.3.0</version>
<version>2.4.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
Expand All @@ -43,8 +44,9 @@ public class GenericValidator {
public static final String ERR_REFV_NO_CREATION_DATE_IN_RESOURCE = "REFV_NO_CREATION_DATE_IN_RESOURCE";
public static final String ERR_REFV_PARSE_ERROR = "REFV_PARSE_ERROR";
public static final String WARN_REFV_VALIDITY_PERIOD_CHECK_DISABLED = "REFV_VALIDITY_PERIOD_CHECK_DISABLED";
private static final String WARN_REFV_PASSED_PROFILE_DIFFERS_FROM_META_PROFILE = "REFV_WARN_PASSED_PROFILE_DIFFERS_FROM_META_PROFILE";
public static final String WARN_REFV_PASSED_PROFILE_DIFFERS_FROM_META_PROFILE = "REFV_WARN_PASSED_PROFILE_DIFFERS_FROM_META_PROFILE";
public static final String ERR_REFV_WRONG_ENCODING = "REFV_WRONG_ENCODING";
public static final String ERR_REFV_PROFILE_FILTER_MISMATCH = "REFV_PROFILE_FILTER_MISMATCH";
private final FhirContext fhirContext;

private final ReferencedProfileLocator referencedProfileLocator = new ReferencedProfileLocator();
Expand Down Expand Up @@ -87,28 +89,37 @@ public ValidationResult validate(
if(!validateEncoding(resourceBody, configuration, validationOptions))
result = ValidationResult.createInstance(ResultSeverityEnum.ERROR, ERR_REFV_WRONG_ENCODING, String.format("Wrong instance encoding. Allowed encodings: %s", String.join(",", getAcceptedEncodings(configuration, validationOptions))));
else {
Profile profileForValidation;

List<String> allReferencedProfilesInResource = referencedProfileLocator.getAllReferencedProfilesInResource(resourceBody);

if (!validationOptions.getProfiles().isEmpty()) {
var userDefinedProfile = validationOptions.getProfiles().get(0); // Only one user defined profile is supported at the moment
log.warn("Profile for validation has been passed by user: " + userDefinedProfile);
profileForValidation = configuration.findFirstSupportedProfileWithExistingConfiguration(List.of(userDefinedProfile));
} else if (!allReferencedProfilesInResource.isEmpty())
profileForValidation = configuration.findFirstSupportedProfileWithExistingConfiguration(allReferencedProfilesInResource);
else
throw new IllegalArgumentException("FHIR resources without a referenced profile are currently unsupported. Please provide a profile parameter or a profile in the resource meta.profile element.");

if(profileForValidation == null)
throw new UnsupportedProfileException(allReferencedProfilesInResource);

result = validateResource(resourceBody, resourceProvider, validationOptions, profileForValidation, configuration, allReferencedProfilesInResource);
if(validationOptions.getProfileFilterRegex() != null && noneReferencedProfileMatches(allReferencedProfilesInResource, validationOptions.getProfileFilterRegex())) {
result = ValidationResult.createInstance(ResultSeverityEnum.ERROR, ERR_REFV_PROFILE_FILTER_MISMATCH, String.format("None of the referenced profiles in the resource matches the profile filter: %s. Referenced profiles: %s", validationOptions.getProfileFilterRegex(), allReferencedProfilesInResource));
}
else {
Profile profileForValidation;
if (!validationOptions.getProfiles().isEmpty()) {
var userDefinedProfile = validationOptions.getProfiles().get(0); // Only one user defined profile is supported at the moment
log.warn("Profile for validation has been passed by user: " + userDefinedProfile);
profileForValidation = configuration.findFirstSupportedProfileWithExistingConfiguration(List.of(userDefinedProfile));
} else if (!allReferencedProfilesInResource.isEmpty())
profileForValidation = configuration.findFirstSupportedProfileWithExistingConfiguration(allReferencedProfilesInResource);
else
throw new IllegalArgumentException("FHIR resources without a referenced profile are currently unsupported. Please provide a profile parameter or a profile in the resource meta.profile element.");

if (profileForValidation == null)
throw new UnsupportedProfileException(allReferencedProfilesInResource);

result = validateResource(resourceBody, resourceProvider, validationOptions, profileForValidation, configuration, allReferencedProfilesInResource);
}
}

return outputFilter.apply(result, validationOptions.getValidationMessagesFilter());
}

private static boolean noneReferencedProfileMatches(List<String> allReferencedProfilesInResource, Pattern profileFilterRegex) {
log.debug("Checking if any of the referenced profiles in the resource matches the profile filter: {}...", profileFilterRegex);
return allReferencedProfilesInResource.stream().noneMatch(p -> profileFilterRegex.matcher(p).find());
}

private ValidationResult validateResource(String resourceBody, ValidationModuleResourceProvider resourceProvider, ValidationOptions validationOptions, Profile profileForValidation, ValidationModuleConfiguration configuration, List<String> allReferencedProfilesInResource) {
log.info("Validating against {}...", profileForValidation);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@
package de.gematik.refv.commons.validation;

import lombok.Data;
import lombok.Getter;

import java.util.LinkedList;
import java.util.List;
import java.util.regex.Pattern;

@Data
public class ValidationOptions {
Expand All @@ -28,6 +30,9 @@ public class ValidationOptions {
private ProfileValidityPeriodCheckStrategy profileValidityPeriodCheckStrategy;
private ValidationMessagesFilter validationMessagesFilter;

@Getter
private Pattern profileFilterRegex;

private ValidationOptions() {
}

Expand All @@ -37,4 +42,8 @@ public static ValidationOptions getDefaults() {
options.setValidationMessagesFilter(ValidationMessagesFilter.KEEP_ERRORS_ONLY);
return options;
}

public void setProfileFilterRegex(String profileFilterRegex) {
this.profileFilterRegex = Pattern.compile(profileFilterRegex);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ class IntegratedValidationModuleIT {
public static final String VALID_FILE = "simplevalidationmodule.test.patient.valid.xml";
public static final String VALID_FILE_JSON = "simplevalidationmodule.test.patient.valid.json";
public static final String INVALID_FILE = "simplevalidationmodule.test.patient.invalid.xml";
public static final String INVALID_FILE_FOR_PATCH = "simplevalidationmodule.test.patch.patient.invalid.xml";
private static final ValidationModule module = ValidationModuleFactory.createInstance("simple");

@Test
Expand Down Expand Up @@ -111,4 +110,26 @@ void testXmlCommentsDoNotProduceMisleadingValidationIssues() {
Assertions.assertFalse(result.getValidationMessages().stream().anyMatch(m -> m.getMessageId().equals("Terminology_TX_NoValid_3_CC")), "Misleading validation messages detected while there should be none");
}

@Test
@SneakyThrows
void testProfileFilterWhichDoesntMatchAnyMetaProfileReference() {
var input = ClasspathUtil.loadResourceAsStream("classpath:" + VALID_FILE);

ValidationOptions options = ValidationOptions.getDefaults();
options.setProfileFilterRegex("non-matching-pattern");
var result = module.validateString(IOUtils.toString(input, StandardCharsets.UTF_8), options);
Assertions.assertFalse(result.isValid(),"Resource is valid while the profile filter doesn't match any meta.profile reference");
}

@Test
@SneakyThrows
void testProfileFilterWhichMatcheAtLeastOneMetaProfileReference() {
var input = ClasspathUtil.loadResourceAsStream("classpath:" + VALID_FILE);

ValidationOptions options = ValidationOptions.getDefaults();
options.setProfileFilterRegex("patient|birthdate\\|1\\.0\\.\\d");
var result = module.validateString(IOUtils.toString(input, StandardCharsets.UTF_8), options);
Assertions.assertTrue(result.isValid(),"Resource is invalid while the profile filter matches one meta.profile reference");
}

}
2 changes: 1 addition & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<artifactId>referencevalidator</artifactId>
<groupId>de.gematik.refv</groupId>
<version>2.3.0</version>
<version>2.4.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>

Expand Down
Loading

0 comments on commit ef1035b

Please sign in to comment.