mirror of
https://github.com/hapifhir/hapi-fhir.git
synced 2025-03-09 14:33:32 +00:00
SMILE-3459 Implemented -l parameter in validator. (#3983)
* SMILE-3459 Implemented -l parameter in validator. Added SnapshotGeneratingValidationSupport to R4 support chain. * SMILE-3459 Removed failing test that I added. * Revert "SMILE-3459 Removed failing test that I added." This reverts commit d11abc7ad5bc0bc5fa149e83670621959b03a7b9. * SMILE-3459 Added test cases for loading local R4 profile. * SMILE-3459 Added when clause to test names. * SMILE-3459 WIP Logging assert isn't working. * - changes to test for logging * - change logging level to error * - capture all logging events in tests * SMILE-3459 Ensure validation error is logged when resource doesn't comply with profile. * SMILE-3459 Added changelog. * SMILE-3459 Made ValidateCommandTest less brittle. * SMILE-3459 Changed LocalFileValidationSupport to support all resource types. * SMILE-3459 Added LocalFileValidationSupportTest * SMILE-3459 Created ValidationSupportChainCreator. * SMILE-3459 Added new Msg.code for local profile load failure. Co-authored-by: kylejule <kyle.jule@smilecdr.com> Co-authored-by: nathaniel.doef <nathaniel.doef@smilecdr.com>
This commit is contained in:
parent
d1f988f98a
commit
4d16a109de
@ -25,7 +25,7 @@ public final class Msg {
|
||||
|
||||
/**
|
||||
* IMPORTANT: Please update the following comment after you add a new code
|
||||
* Last used code value: 2140
|
||||
* Last used code value: 2141
|
||||
*/
|
||||
|
||||
private Msg() {}
|
||||
|
@ -27,6 +27,7 @@ import ca.uhn.fhir.context.support.IValidationSupport;
|
||||
import ca.uhn.fhir.parser.DataFormatException;
|
||||
import ca.uhn.fhir.validation.FhirValidator;
|
||||
import ca.uhn.fhir.validation.SingleValidationMessage;
|
||||
import ca.uhn.fhir.validation.ValidationOptions;
|
||||
import ca.uhn.fhir.validation.ValidationResult;
|
||||
import com.google.common.base.Charsets;
|
||||
import com.helger.commons.io.file.FileHelper;
|
||||
@ -40,7 +41,6 @@ import org.fusesource.jansi.Ansi.Color;
|
||||
import org.hl7.fhir.common.hapi.validation.validator.FhirInstanceValidator;
|
||||
import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@ -136,40 +136,27 @@ public class ValidateCommand extends BaseCommand {
|
||||
FhirContext ctx = getFhirContext();
|
||||
FhirValidator val = ctx.newValidator();
|
||||
|
||||
IBaseResource localProfileResource = null;
|
||||
ValidationOptions options = new ValidationOptions();
|
||||
if (theCommandLine.hasOption("l")) {
|
||||
String localProfile = theCommandLine.getOptionValue("l");
|
||||
ourLog.info("Loading profile: {}", localProfile);
|
||||
String input = loadFile(localProfile);
|
||||
|
||||
localProfileResource = ca.uhn.fhir.rest.api.EncodingEnum.detectEncodingNoDefault(input).newParser(ctx).parseResource(input);
|
||||
options.addProfile(localProfile);
|
||||
}
|
||||
|
||||
if (theCommandLine.hasOption("p")) {
|
||||
switch (ctx.getVersion().getVersion()) {
|
||||
case DSTU2: {
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(
|
||||
new DefaultProfileValidationSupport(ctx), new InMemoryTerminologyServerValidationSupport(ctx));
|
||||
|
||||
if (theCommandLine.hasOption("r")) {
|
||||
validationSupport.addValidationSupport((IValidationSupport) new LoadingValidationSupportDstu2());
|
||||
}
|
||||
FhirInstanceValidator instanceValidator;
|
||||
instanceValidator = new FhirInstanceValidator(validationSupport);
|
||||
ValidationSupportChain validationSupportChain = ValidationSupportChainCreator.getValidationSupportChainDstu2(ctx, theCommandLine);
|
||||
instanceValidator = new FhirInstanceValidator(validationSupportChain);
|
||||
val.registerValidatorModule(instanceValidator);
|
||||
|
||||
break;
|
||||
}
|
||||
case DSTU3:
|
||||
case R4: {
|
||||
FhirInstanceValidator instanceValidator = new FhirInstanceValidator(ctx);
|
||||
val.registerValidatorModule(instanceValidator);
|
||||
ValidationSupportChain validationSupport = new ValidationSupportChain(new DefaultProfileValidationSupport(ctx), new InMemoryTerminologyServerValidationSupport(ctx));
|
||||
|
||||
if (theCommandLine.hasOption("r")) {
|
||||
validationSupport.addValidationSupport((IValidationSupport) new LoadingValidationSupportDstu3());
|
||||
}
|
||||
instanceValidator.setValidationSupport(validationSupport);
|
||||
ValidationSupportChain validationSupportChain = ValidationSupportChainCreator.getValidationSupportChainR4(ctx, theCommandLine);
|
||||
instanceValidator.setValidationSupport(validationSupportChain);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -182,7 +169,7 @@ public class ValidateCommand extends BaseCommand {
|
||||
|
||||
ValidationResult results;
|
||||
try {
|
||||
results = val.validateWithResult(contents);
|
||||
results = val.validateWithResult(contents, options);
|
||||
} catch (DataFormatException e) {
|
||||
throw new CommandFailureException(Msg.code(1621) + e.getMessage());
|
||||
}
|
||||
|
@ -0,0 +1,51 @@
|
||||
package ca.uhn.fhir.cli;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||
import ca.uhn.fhir.i18n.Msg;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.hl7.fhir.common.hapi.validation.support.InMemoryTerminologyServerValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.LocalFileValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class ValidationSupportChainCreator {
|
||||
|
||||
public static ValidationSupportChain getValidationSupportChainR4(FhirContext ctx, CommandLine commandLine) {
|
||||
ValidationSupportChain chain = new ValidationSupportChain(
|
||||
new DefaultProfileValidationSupport(ctx),
|
||||
new InMemoryTerminologyServerValidationSupport(ctx));
|
||||
|
||||
if (commandLine.hasOption("l")) {
|
||||
try {
|
||||
String localProfile = commandLine.getOptionValue("l");
|
||||
LocalFileValidationSupport localFileValidationSupport = new LocalFileValidationSupport(ctx);
|
||||
|
||||
localFileValidationSupport.loadFile(localProfile);
|
||||
|
||||
chain.addValidationSupport(localFileValidationSupport);
|
||||
chain.addValidationSupport(new SnapshotGeneratingValidationSupport(ctx));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(Msg.code(2141) + "Failed to load local profile.", e);
|
||||
}
|
||||
}
|
||||
if (commandLine.hasOption("r")) {
|
||||
chain.addValidationSupport(new LoadingValidationSupportDstu3());
|
||||
}
|
||||
|
||||
return chain;
|
||||
}
|
||||
|
||||
public static ValidationSupportChain getValidationSupportChainDstu2(FhirContext ctx, CommandLine commandLine) {
|
||||
ValidationSupportChain chain = new ValidationSupportChain(
|
||||
new DefaultProfileValidationSupport(ctx), new InMemoryTerminologyServerValidationSupport(ctx));
|
||||
|
||||
if (commandLine.hasOption("r")) {
|
||||
chain.addValidationSupport(new LoadingValidationSupportDstu2());
|
||||
}
|
||||
|
||||
return chain;
|
||||
}
|
||||
}
|
@ -1,22 +1,57 @@
|
||||
package ca.uhn.fhir.cli;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import ch.qos.logback.classic.joran.JoranConfigurator;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.joran.spi.JoranException;
|
||||
import ch.qos.logback.core.read.ListAppender;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.DefaultParser;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class ValidateCommandTest {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ValidateCommandTest.class);
|
||||
|
||||
private final Logger ourLog = (Logger) LoggerFactory.getLogger(ValidateCommand.class);
|
||||
private ValidateCommand myValidateCommand = new ValidateCommand();
|
||||
private ListAppender<ILoggingEvent> myListAppender = new ListAppender<>();
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void beforeEach() {
|
||||
ourLog.setLevel(Level.ALL);
|
||||
myListAppender = new ListAppender<>();
|
||||
myListAppender.start();
|
||||
ourLog.addAppender(myListAppender);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void afterEach() {
|
||||
myListAppender.stop();
|
||||
}
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
System.setProperty("test", "true");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testValidateLocalProfileDstu3() {
|
||||
String resourcePath = ValidateCommandTest.class.getResource("/patient-uslab-example1.xml").getFile();
|
||||
ourLog.info(resourcePath);
|
||||
|
||||
App.main(new String[] {
|
||||
|
||||
App.main(new String[]{
|
||||
"validate",
|
||||
"-v", "dstu3",
|
||||
"-p",
|
||||
@ -28,10 +63,23 @@ public class ValidateCommandTest {
|
||||
String resourcePath = ValidateCommandTest.class.getResource("/patient-uslab-example1.xml").getFile();
|
||||
ourLog.info(resourcePath);
|
||||
|
||||
App.main(new String[] {
|
||||
App.main(new String[]{
|
||||
"validate",
|
||||
"-v", "R4",
|
||||
"-p",
|
||||
"-n", resourcePath});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validate_withLocalProfileR4_shouldPass_whenResourceCompliesWithProfile() {
|
||||
String patientJson = ValidateCommandTest.class.getResource("/validate/Patient.json").getFile();
|
||||
String patientProfile = ValidateCommandTest.class.getResource("/validate/PatientIn-Profile.json").getFile();
|
||||
|
||||
App.main(new String[]{
|
||||
"validate",
|
||||
"--fhir-version", "r4",
|
||||
"--profile",
|
||||
"--file", patientJson,
|
||||
"-l", patientProfile});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
package ca.uhn.fhir.cli;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.DefaultParser;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ValidationSupportChainCreatorTest {
|
||||
|
||||
private static FhirContext myContextDstu2 = FhirContext.forDstu2();
|
||||
private static FhirContext myContextR4 = FhirContext.forR4();
|
||||
|
||||
private ValidateCommand myValidateCommand = new ValidateCommand();
|
||||
|
||||
@Test
|
||||
public void getR4Chain_shouldProvideExpectedValidationSupport() throws ParseException {
|
||||
String patientJson = ValidateCommandTest.class.getResource("/validate/Patient-no-identifier.json").getFile();
|
||||
String patientProfile = ValidateCommandTest.class.getResource("/validate/PatientIn-Profile.json").getFile();
|
||||
|
||||
String[] args = new String[]{
|
||||
"validate",
|
||||
"--fhir-version", "r4",
|
||||
"--profile",
|
||||
"--file", patientJson,
|
||||
"-l", patientProfile
|
||||
};
|
||||
|
||||
CommandLine commandLine = new DefaultParser().parse(myValidateCommand.getOptions(), args);
|
||||
ValidationSupportChain chain = ValidationSupportChainCreator.getValidationSupportChainR4(myContextR4, commandLine);
|
||||
StructureDefinition structureDefinition = (StructureDefinition) chain.fetchStructureDefinition("https://www.medizininformatik-initiative.de/fhir/core/modul-person/StructureDefinition/Patient");
|
||||
|
||||
assertEquals(structureDefinition.getName(), "Profile_MII_Patient_PatientIn");
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"resourceType": "Patient",
|
||||
"id": "ExamplePatientPatientFull",
|
||||
"meta": {
|
||||
"profile": [
|
||||
"https://www.medizininformatik-initiative.de/fhir/core/modul-person/StructureDefinition/Patient"
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
{
|
||||
"resourceType": "Patient",
|
||||
"id": "ExamplePatientPatientFull",
|
||||
"meta": {
|
||||
"profile": [
|
||||
"https://www.medizininformatik-initiative.de/fhir/core/modul-person/StructureDefinition/Patient"
|
||||
]
|
||||
},
|
||||
"identifier": [
|
||||
{
|
||||
"use": "usual",
|
||||
"type": {
|
||||
"coding": [
|
||||
{
|
||||
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
|
||||
"code": "MR"
|
||||
}
|
||||
]
|
||||
},
|
||||
"system": "https://www.example.org/fhir/sid/patienten",
|
||||
"value": "42285243"
|
||||
}
|
||||
]
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
{
|
||||
"resourceType": "StructureDefinition",
|
||||
"id": "Profile-MII-Patient-PatientIn",
|
||||
"url": "https://www.medizininformatik-initiative.de/fhir/core/modul-person/StructureDefinition/Patient",
|
||||
"version": "2.0.0",
|
||||
"name": "Profile_MII_Patient_PatientIn",
|
||||
"title": "Profile - MI-I - Patient - PatientIn",
|
||||
"status": "active",
|
||||
"fhirVersion": "4.0.1",
|
||||
"kind": "resource",
|
||||
"abstract": false,
|
||||
"type": "Patient",
|
||||
"baseDefinition": "http://hl7.org/fhir/StructureDefinition/Patient",
|
||||
"derivation": "constraint",
|
||||
"differential": {
|
||||
"element": [
|
||||
{
|
||||
"id": "Patient.identifier",
|
||||
"path": "Patient.identifier",
|
||||
"slicing": {
|
||||
"discriminator": [
|
||||
{
|
||||
"type": "pattern",
|
||||
"path": "$this"
|
||||
}
|
||||
],
|
||||
"rules": "open"
|
||||
},
|
||||
"min": 1,
|
||||
"mustSupport": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
type: fix
|
||||
issue: 4003
|
||||
jira: SMILE-3459
|
||||
title: "Added support for -l parameter for providing a local validation profile in the HAPI FHIR CLI."
|
@ -0,0 +1,48 @@
|
||||
package org.hl7.fhir.common.hapi.validation.support;
|
||||
|
||||
/*-
|
||||
* #%L
|
||||
* HAPI FHIR - Command Line Client - API
|
||||
* %%
|
||||
* Copyright (C) 2014 - 2022 Smile CDR, Inc.
|
||||
* %%
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
public class LocalFileValidationSupport extends PrePopulatedValidationSupport {
|
||||
|
||||
public LocalFileValidationSupport(FhirContext ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FhirContext getFhirContext() {
|
||||
return myCtx;
|
||||
}
|
||||
|
||||
public void loadFile(String theFileName) throws IOException {
|
||||
String contents = IOUtils.toString(new InputStreamReader(new FileInputStream(theFileName), "UTF-8"));
|
||||
IBaseResource resource = myCtx.newJsonParser().parseResource(contents);
|
||||
this.addResource(resource);
|
||||
}
|
||||
|
||||
}
|
@ -229,7 +229,6 @@ public class FhirInstanceValidator extends BaseValidatorBridge implements IInsta
|
||||
.setBestPracticeWarningLevel(getBestPracticeWarningLevel())
|
||||
.setErrorForUnknownProfiles(isErrorForUnknownProfiles())
|
||||
.setExtensionDomains(getExtensionDomains())
|
||||
.setValidatorResourceFetcher(validatorResourceFetcher)
|
||||
.setValidationPolicyAdvisor(validatorPolicyAdvisor)
|
||||
.setNoTerminologyChecks(isNoTerminologyChecks())
|
||||
.setNoExtensibleWarnings(isNoExtensibleWarnings())
|
||||
|
@ -0,0 +1,42 @@
|
||||
package org.hl7.fhir.r4.validation;
|
||||
|
||||
import ca.uhn.fhir.context.FhirContext;
|
||||
import ca.uhn.fhir.context.support.DefaultProfileValidationSupport;
|
||||
import ca.uhn.fhir.context.support.ValidationSupportContext;
|
||||
import ca.uhn.fhir.parser.IParser;
|
||||
import ca.uhn.fhir.rest.api.EncodingEnum;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.hl7.fhir.common.hapi.validation.support.LocalFileValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.SnapshotGeneratingValidationSupport;
|
||||
import org.hl7.fhir.common.hapi.validation.support.ValidationSupportChain;
|
||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||
import org.hl7.fhir.r4.model.StructureDefinition;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class LocalFileValidationSupportTest {
|
||||
|
||||
private FhirContext myFhirCtx = FhirContext.forR4();
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(LocalFileValidationSupportTest.class);
|
||||
|
||||
@Test
|
||||
public void getStructureDefinition_shouldReturnLocalFile_whenLocalFileProvided() throws IOException {
|
||||
String patientProfile = LocalFileValidationSupportTest.class.getResource("/PatientIn-Profile.json").getFile();
|
||||
|
||||
LocalFileValidationSupport localFileValidationSupport = new LocalFileValidationSupport(myFhirCtx);
|
||||
localFileValidationSupport.loadFile(patientProfile);
|
||||
StructureDefinition structureDefinition = (StructureDefinition) localFileValidationSupport.fetchStructureDefinition("https://www.medizininformatik-initiative.de/fhir/core/modul-person/StructureDefinition/Patient");
|
||||
|
||||
assertEquals(structureDefinition.getName(), "Profile_MII_Patient_PatientIn");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
{
|
||||
"resourceType": "StructureDefinition",
|
||||
"id": "Profile-MII-Patient-PatientIn",
|
||||
"url": "https://www.medizininformatik-initiative.de/fhir/core/modul-person/StructureDefinition/Patient",
|
||||
"version": "2.0.0",
|
||||
"name": "Profile_MII_Patient_PatientIn",
|
||||
"title": "Profile - MI-I - Patient - PatientIn",
|
||||
"status": "active",
|
||||
"fhirVersion": "4.0.1",
|
||||
"kind": "resource",
|
||||
"abstract": false,
|
||||
"type": "Patient",
|
||||
"baseDefinition": "http://hl7.org/fhir/StructureDefinition/Patient",
|
||||
"derivation": "constraint",
|
||||
"differential": {
|
||||
"element": [
|
||||
{
|
||||
"id": "Patient.identifier",
|
||||
"path": "Patient.identifier",
|
||||
"slicing": {
|
||||
"discriminator": [
|
||||
{
|
||||
"type": "pattern",
|
||||
"path": "$this"
|
||||
}
|
||||
],
|
||||
"rules": "open"
|
||||
},
|
||||
"min": 1,
|
||||
"mustSupport": true
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user