Merge pull request #176 from hapifhir/validator_gui

Validator gui
This commit is contained in:
Grahame Grieve 2020-04-24 07:19:00 +10:00 committed by GitHub
commit 5d0fd426be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 1680 additions and 86 deletions

View File

@ -88,13 +88,10 @@ public class TestingUtilities {
if (!Utilities.noString(s)) if (!Utilities.noString(s))
return s; return s;
s = "C:\\work\\org.hl7.fhir\\build"; s = "C:\\work\\org.hl7.fhir\\build";
// FIXME: change this back
s = "/Users/jamesagnew/git/fhir";
if (new File(s).exists()) if (new File(s).exists())
return s; return s;
throw new Error("FHIR Home directory not configured"); throw new Error("FHIR Home directory not configured");
} }
public static String content() throws IOException { public static String content() throws IOException {
if (contentpath != null) if (contentpath != null)

View File

@ -24,6 +24,9 @@ package org.hl7.fhir.r5.elementmodel;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRException;
@ -34,23 +37,41 @@ import org.hl7.fhir.r5.model.StructureDefinition;
public class Manager { public class Manager {
//TODO use EnumMap
public enum FhirFormat { XML, JSON, TURTLE, TEXT, VBAR; public enum FhirFormat { XML, JSON, TURTLE, TEXT, VBAR;
public String getExtension() { public String getExtension() {
switch (this) { switch (this) {
case JSON: case JSON:
return "json"; return "json";
case TURTLE: case TURTLE:
return "ttl"; return "ttl";
case XML: case XML:
return "xml"; return "xml";
case TEXT: case TEXT:
return "txt"; return "txt";
case VBAR: case VBAR:
return "hl7"; return "hl7";
} }
return null; return null;
} }
public static FhirFormat getFhirFormat(String code) {
switch (code) {
case "json":
return JSON;
case "ttl":
return TURTLE;
case "xml":
return XML;
case "txt":
return TEXT;
case "hl7":
return VBAR;
}
return null;
}
} }
public static Element parse(IWorkerContext context, InputStream source, FhirFormat inputFormat) throws FHIRFormatError, DefinitionException, IOException, FHIRException { public static Element parse(IWorkerContext context, InputStream source, FhirFormat inputFormat) throws FHIRFormatError, DefinitionException, IOException, FHIRException {

View File

@ -51,6 +51,7 @@ package org.hl7.fhir.utilities.validation;
*/ */
import java.util.Comparator; import java.util.Comparator;
import java.util.EnumMap;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;

View File

@ -12,6 +12,10 @@
<artifactId>org.hl7.fhir.validation</artifactId> <artifactId>org.hl7.fhir.validation</artifactId>
<packaging>bundle</packaging> <packaging>bundle</packaging>
<properties>
<micronaut.version>1.3.4</micronaut.version>
</properties>
<dependencies> <dependencies>
<!-- HAPI Dependencies --> <!-- HAPI Dependencies -->
@ -138,6 +142,48 @@
<version>${validator_test_case_version}</version> <version>${validator_test_case_version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>io.javalin</groupId>
<artifactId>javalin</artifactId>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.28</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.10.3</version>
</dependency>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>RELEASE</version>
<scope>test</scope>
</dependency>
<!-- End to end -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-chrome-driver</artifactId>
<version>3.141.59</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>3.6.2</version>
<scope>test</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>
@ -151,5 +197,4 @@
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
</project> </project>

View File

@ -1068,6 +1068,13 @@ public class ValidationEngine implements IValidatorResourceFetcher {
return isBundle; return isBundle;
} }
public OperationOutcome validate(byte[] source, FhirFormat cntType, List<String> profiles) throws Exception {
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
InstanceValidator validator = getValidator();
validator.validate(null, messages, new ByteArrayInputStream(source), cntType, asSdList(profiles));
return messagesToOutcome(messages);
}
public OperationOutcome validate(String location, byte[] source, FhirFormat cntType, List<String> profiles) throws Exception { public OperationOutcome validate(String location, byte[] source, FhirFormat cntType, List<String> profiles) throws Exception {
List<ValidationMessage> messages = new ArrayList<ValidationMessage>(); List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
if (doNative) { if (doNative) {

View File

@ -49,12 +49,17 @@ POSSIBILITY OF SUCH DAMAGE.
*/ */
import java.io.File; import java.io.File;
import java.net.URI;
import org.hl7.fhir.r5.model.ImplementationGuide; import org.hl7.fhir.r5.model.ImplementationGuide;
import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.validation.cli.*; import org.hl7.fhir.validation.cli.ValidatorGui;
import org.hl7.fhir.validation.cli.services.ComparisonService;
import org.hl7.fhir.validation.cli.services.ValidationService;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.utils.*;
/** /**
* A executable class that will validate one or more FHIR resources against * A executable class that will validate one or more FHIR resources against
@ -75,6 +80,8 @@ public class Validator {
VALIDATION, TRANSFORM, NARRATIVE, SNAPSHOT, SCAN, CONVERT, FHIRPATH, VERSION VALIDATION, TRANSFORM, NARRATIVE, SNAPSHOT, SCAN, CONVERT, FHIRPATH, VERSION
} }
private static CliContext cliContext;
private static String getNamedParam(String[] args, String param) { private static String getNamedParam(String[] args, String param) {
boolean found = false; boolean found = false;
for (String a : args) { for (String a : args) {
@ -91,7 +98,29 @@ public class Validator {
return Long.toString(maxMemory / (1024 * 1024)); return Long.toString(maxMemory / (1024 * 1024));
} }
private static CliContext getCliContext() {
if (cliContext == null) {
cliContext = new CliContext();
}
return cliContext;
}
private static void goToWebPage(String url) {
try {
URI uri= new URI(url);
java.awt.Desktop.getDesktop().browse(uri);
System.out.println("Web page opened in browser");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
System.out.println("FHIR Validation tool " + VersionUtil.getVersionString()); System.out.println("FHIR Validation tool " + VersionUtil.getVersionString());
System.out.println("Detected Java version: " + System.getProperty("java.version") + " from " + System.getProperty("java.home") + " on " + System.getProperty("os.arch") + " (" + System.getProperty("sun.arch.data.model") + "bit). " + toMB(Runtime.getRuntime().maxMemory()) + "MB available"); System.out.println("Detected Java version: " + System.getProperty("java.version") + " from " + System.getProperty("java.home") + " on " + System.getProperty("os.arch") + " (" + System.getProperty("sun.arch.data.model") + "bit). " + toMB(Runtime.getRuntime().maxMemory()) + "MB available");
String proxy = getNamedParam(args, Params.PROXY); String proxy = getNamedParam(args, Params.PROXY);
@ -101,7 +130,13 @@ public class Validator {
System.setProperty("http.proxyPort", p[1]); System.setProperty("http.proxyPort", p[1]);
} }
if (Params.hasParam(args, Params.TEST)) { if (Params.hasParam(args, Params.GUI)) {
cliContext = Params.loadCliContext(args);
String v = Common.getVersion(args);
String definitions = VersionUtilities.packageForVersion(v) + "#" + v;
ValidationEngine validationEngine = Common.getValidationEngine(v, definitions, cliContext.getTxLog());
ValidatorGui.start(cliContext, validationEngine, true);
} else if (Params.hasParam(args, Params.TEST)) {
Common.runValidationEngineTests(); Common.runValidationEngineTests();
} else if (args.length == 0 || Params.hasParam(args, Params.HELP) || Params.hasParam(args, "?") || Params.hasParam(args, "-?") || Params.hasParam(args, "/?")) { } else if (args.length == 0 || Params.hasParam(args, Params.HELP) || Params.hasParam(args, "?") || Params.hasParam(args, "-?") || Params.hasParam(args, "/?")) {
Display.displayHelpDetails(); Display.displayHelpDetails();
@ -115,30 +150,32 @@ public class Validator {
else { else {
// first, prepare the context // first, prepare the context
String txLog = Params.getTerminologyServerLog(args); String txLog = Params.getTerminologyServerLog(args);
ValidationEngine validator = Common.getValidationEngine(args, txLog); String v = Common.getVersion(args);
String definitions = VersionUtilities.packageForVersion(v) + "#" + v;
ValidationEngine validator = Common.getValidationEngine(v, definitions, txLog);
Params.checkIGFileReferences(args); Params.checkIGFileReferences(args);
Comparison.doLeftRightComparison(args, dest, validator); ComparisonService.doLeftRightComparison(args, dest, validator);
} }
} else { } else {
Display.printCliArgumentsAndInfo(args); Display.printCliArgumentsAndInfo(args);
CliContext cliContext = Params.loadCliContext(args); cliContext = Params.loadCliContext(args);
// Comment this out because definitions filename doesn't necessarily contain version (and many not even be 14 characters long). Version gets spit out a couple of lines later after we've loaded the context // Comment this out because definitions filename doesn't necessarily contain version (and many not even be 14 characters long). Version gets spit out a couple of lines later after we've loaded the context
String definitions = VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv()); String definitions = VersionUtilities.packageForVersion(cliContext.getSv()) + "#" + VersionUtilities.getCurrentVersion(cliContext.getSv());
ValidationEngine validator = ValidationUtils.getValidator(cliContext, definitions); ValidationEngine validator = ValidationService.getValidator(cliContext, definitions);
if (cliContext.getMode() == EngineMode.VERSION) { if (cliContext.getMode() == EngineMode.VERSION) {
ValidationUtils.transformVersion(cliContext, validator);
ValidationService.transformVersion(cliContext, validator);
} else if (cliContext.getMode() == EngineMode.TRANSFORM) { } else if (cliContext.getMode() == EngineMode.TRANSFORM) {
ValidationUtils.transform(cliContext, validator); ValidationService.transform(cliContext, validator);
} else if (cliContext.getMode() == EngineMode.NARRATIVE) { } else if (cliContext.getMode() == EngineMode.NARRATIVE) {
ValidationUtils.generateNarrative(cliContext, validator); ValidationService.generateNarrative(cliContext, validator);
} else if (cliContext.getMode() == EngineMode.SNAPSHOT) { } else if (cliContext.getMode() == EngineMode.SNAPSHOT) {
ValidationUtils.generateSnapshot(cliContext, validator); ValidationService.generateSnapshot(cliContext, validator);
} else if (cliContext.getMode() == EngineMode.CONVERT) { } else if (cliContext.getMode() == EngineMode.CONVERT) {
ValidationUtils.convertSources(cliContext, validator); ValidationService.convertSources(cliContext, validator);
} else if (cliContext.getMode() == EngineMode.FHIRPATH) { } else if (cliContext.getMode() == EngineMode.FHIRPATH) {
ValidationUtils.evaluateFhirpath(cliContext, validator); ValidationService.evaluateFhirpath(cliContext, validator);
} else { } else {
if (definitions == null) { if (definitions == null) {
throw new Exception("Must provide a defn when doing validation"); throw new Exception("Must provide a defn when doing validation");
@ -150,9 +187,9 @@ public class Validator {
} }
} }
if (cliContext.getMode() == EngineMode.SCAN) { if (cliContext.getMode() == EngineMode.SCAN) {
ValidationUtils.validateScan(cliContext, validator); ValidationService.validateScan(cliContext, validator);
} else { } else {
ValidationUtils.validateSources(cliContext, validator); ValidationService.validateSources(cliContext, validator);
} }
} }
} }

View File

@ -0,0 +1,29 @@
package org.hl7.fhir.validation.cli;
import io.javalin.Javalin;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.controller.CliContextController;
import org.hl7.fhir.validation.cli.controller.ValidationController;
import org.hl7.fhir.validation.cli.controller.UIController;
import org.hl7.fhir.validation.cli.model.CliContext;
public class RestEndpoints {
public UIController myUIController;
public CliContextController myCliContextController;
public ValidationController myValidationController;
public void initRestEndpoints(Javalin app, CliContext cliContext, ValidationEngine validationEngine) {
myUIController = new UIController();
myCliContextController = new CliContextController(cliContext);
myValidationController = new ValidationController(validationEngine);
app.get("/home", myUIController.renderLandingPage);
app.get("/context", myCliContextController::handleGetCurrentCliContext);
app.post("/context", myCliContextController::handleSetCurrentCliContext);
app.post("/validate", myValidationController::handleValidationRequest);
}
}

View File

@ -0,0 +1,60 @@
package org.hl7.fhir.validation.cli;
import io.javalin.Javalin;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.hl7.fhir.validation.cli.utils.Common;
import java.awt.*;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
public class ValidatorGui {
private static final int GUI_FRONTEND_PORT = 8080;
private static final String PAGE_ADDRESS = "http://localhost:" + GUI_FRONTEND_PORT + "/home";
private static final String WEB_APP_FILE_LOCATION = "/public";
private static Javalin app;
private ValidatorGui(){}
/**
* N.B. this entry point, is only for testing. Please start from command line using the argument {@code -gui} for
* actual use.
*/
public static void main(String[] args) throws Exception {
CliContext cliContext = new CliContext();
String v = Common.getVersion(args);
String definitions = VersionUtilities.packageForVersion(v) + "#" + v;
ValidationEngine validationEngine = Common.getValidationEngine(v, definitions, cliContext.getTxLog());
start(new CliContext(), validationEngine, false);
}
public static void start(CliContext currentContext, ValidationEngine validationEngine, boolean bootBrowser) {
app = Javalin.create();
new RestEndpoints().initRestEndpoints(app, currentContext, validationEngine);
app.config.addStaticFiles(WEB_APP_FILE_LOCATION);
app.start(GUI_FRONTEND_PORT);
if (bootBrowser) {
openBrowser();
}
}
public static void openBrowser() {
if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
try {
Desktop.getDesktop().browse(new URI(PAGE_ADDRESS));
} catch (Exception e) {
System.out.println("Error opening web browser to validator GUI.\nYou can try to open the page manually at:: "
+ PAGE_ADDRESS + "\nError:: " + e.getMessage());
}
}
}
public static void stop() {
app.stop();
}
}

View File

@ -0,0 +1,30 @@
package org.hl7.fhir.validation.cli.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.javalin.http.Context;
import org.apache.http.HttpStatus;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.jetbrains.annotations.NotNull;
public class CliContextController {
private final String JSON_MIME_TYPE = "application/json";
private CliContext myCliContext;
public CliContextController(CliContext cliContext) {
this.myCliContext = cliContext;
}
public void handleGetCurrentCliContext(@NotNull Context ctx) throws JsonProcessingException {
ObjectMapper Obj = new ObjectMapper();
String jsonStr = Obj.writeValueAsString(myCliContext);
ctx.result(jsonStr).contentType(JSON_MIME_TYPE).status(HttpStatus.SC_OK);
}
public void handleSetCurrentCliContext(@NotNull Context ctx) {
myCliContext = ctx.bodyAsClass(CliContext.class);
ctx.status(HttpStatus.SC_OK);
}
}

View File

@ -0,0 +1,13 @@
package org.hl7.fhir.validation.cli.controller;
import io.javalin.http.Handler;
public class UIController {
public UIController() {}
public Handler renderLandingPage = ctx -> {
ctx.render("/public/index.html");
};
}

View File

@ -0,0 +1,38 @@
package org.hl7.fhir.validation.cli.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.javalin.http.Context;
import io.javalin.http.Handler;
import org.apache.http.HttpStatus;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.ValidationRequest;
import org.hl7.fhir.validation.cli.model.ValidationResponse;
import org.hl7.fhir.validation.cli.services.ValidationService;
public class ValidationController {
private ValidationEngine myValidationEngine;
public ValidationController(ValidationEngine validationEngine) {
this.myValidationEngine = validationEngine;
}
public void handleValidationRequest(Context ctx) {
ValidationRequest request = ctx.bodyAsClass(ValidationRequest.class);
ValidationResponse response = null;
try {
response = ValidationService.validateSources(request, myValidationEngine);
ObjectMapper Obj = new ObjectMapper();
/*
* TODO
* Write file contents to temp files to pass to validator instead of creating our own endpoint.
* Create File => new temp file
* Use Option => DeleteOnShutdown
*/
String jsonStr = Obj.writeValueAsString(response);
ctx.status(200).json(jsonStr);
} catch (Exception e) {
ctx.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).result(e.getLocalizedMessage());
}
}
}

View File

@ -1,6 +1,8 @@
package org.hl7.fhir.validation.cli; package org.hl7.fhir.validation.cli.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hl7.fhir.validation.Validator; import org.hl7.fhir.validation.Validator;
import org.hl7.fhir.validation.cli.utils.SnomedVersion;
import java.util.*; import java.util.*;
@ -9,46 +11,82 @@ import java.util.*;
*/ */
public class CliContext { public class CliContext {
private String map = null; @JsonProperty("doNative")
private List<String> igs = new ArrayList<String>();
private List<String> questionnaires = new ArrayList<String>();
private String txServer = "http://tx.fhir.org";
private boolean doNative = false; private boolean doNative = false;
@JsonProperty("anyExtensionsAllowed")
private boolean anyExtensionsAllowed = true; private boolean anyExtensionsAllowed = true;
@JsonProperty("hintAboutNonMustSupport")
private boolean hintAboutNonMustSupport = false; private boolean hintAboutNonMustSupport = false;
@JsonProperty("recursive")
private boolean recursive = false; private boolean recursive = false;
private Locale locale = null; @JsonProperty("doDebug")
private List<String> profiles = new ArrayList<String>();
private Validator.EngineMode mode = Validator.EngineMode.VALIDATION;
private String output = null;
private Boolean canDoNative = null;
private List<String> sources = new ArrayList<String>();
private Map<String, String> locations = new HashMap<String, String>();
private String sv = "current";
private String txLog = null;
private String mapLog = null;
private String lang = null;
private String fhirpath = null;
private String snomedCT = SnomedVersion.INTL.getCode();
private String targetVer = null;
private boolean doDebug = false; private boolean doDebug = false;
private boolean noInternalCaching = false; // internal, for when debugging terminology validation @JsonProperty("assumeValidRestReferences")
private boolean noExtensibleBindingMessages = false;
private boolean assumeValidRestReferences = false; private boolean assumeValidRestReferences = false;
@JsonProperty("canDoNative")
private boolean canDoNative = false;
@JsonProperty("noInternalCaching")
private boolean noInternalCaching = false; // internal, for when debugging terminology validation
@JsonProperty("noExtensibleBindingMessages")
private boolean noExtensibleBindingMessages = false;
@JsonProperty("map")
private String map = null;
@JsonProperty("output")
private String output = null;
@JsonProperty("txServer")
private String txServer = "http://tx.fhir.org";
@JsonProperty("sv")
private String sv = "current";
@JsonProperty("txLog")
private String txLog = null;
@JsonProperty("mapLog")
private String mapLog = null;
@JsonProperty("lang")
private String lang = null;
@JsonProperty("fhirpath")
private String fhirpath = null;
@JsonProperty("snomedCT")
private String snomedCT = SnomedVersion.INTL.getCode();
@JsonProperty("targetVer")
private String targetVer = null;
@JsonProperty("igs")
private List<String> igs = new ArrayList<String>();
@JsonProperty("questionnaires")
private List<String> questionnaires = new ArrayList<String>();
@JsonProperty("profiles")
private List<String> profiles = new ArrayList<String>();
@JsonProperty("sources")
private List<String> sources = new ArrayList<String>();
@JsonProperty("mode")
private Validator.EngineMode mode = Validator.EngineMode.VALIDATION;
@JsonProperty("locale")
private String locale = Locale.ENGLISH.getDisplayLanguage();
@JsonProperty("locations")
private Map<String, String> locations = new HashMap<String, String>();
@JsonProperty("map")
public String getMap() { public String getMap() {
return map; return map;
} }
@JsonProperty("map")
public CliContext setMap(String map) { public CliContext setMap(String map) {
this.map = map; this.map = map;
return this; return this;
} }
@JsonProperty("igs")
public List<String> getIgs() { public List<String> getIgs() {
return igs; return igs;
} }
@JsonProperty("igs")
public CliContext setIgs(List<String> igs) { public CliContext setIgs(List<String> igs) {
this.igs = igs; this.igs = igs;
return this; return this;
@ -62,10 +100,12 @@ public class CliContext {
return this; return this;
} }
@JsonProperty("questionnaires")
public List<String> getQuestionnaires() { public List<String> getQuestionnaires() {
return questionnaires; return questionnaires;
} }
@JsonProperty("questionnaires")
public CliContext setQuestionnaires(List<String> questionnaires) { public CliContext setQuestionnaires(List<String> questionnaires) {
this.questionnaires = questionnaires; this.questionnaires = questionnaires;
return this; return this;
@ -79,64 +119,87 @@ public class CliContext {
return this; return this;
} }
@JsonProperty("txServer")
public String getTxServer() { public String getTxServer() {
return txServer; return txServer;
} }
@JsonProperty("txServer")
public CliContext setTxServer(String txServer) { public CliContext setTxServer(String txServer) {
this.txServer = txServer; this.txServer = txServer;
return this; return this;
} }
@JsonProperty("doNative")
public boolean isDoNative() { public boolean isDoNative() {
return doNative; return doNative;
} }
@JsonProperty("doNative")
public CliContext setDoNative(boolean doNative) { public CliContext setDoNative(boolean doNative) {
this.doNative = doNative; this.doNative = doNative;
return this; return this;
} }
@JsonProperty("anyExtensionsAllowed")
public boolean isAnyExtensionsAllowed() { public boolean isAnyExtensionsAllowed() {
return anyExtensionsAllowed; return anyExtensionsAllowed;
} }
@JsonProperty("anyExtensionsAllowed")
public CliContext setAnyExtensionsAllowed(boolean anyExtensionsAllowed) { public CliContext setAnyExtensionsAllowed(boolean anyExtensionsAllowed) {
this.anyExtensionsAllowed = anyExtensionsAllowed; this.anyExtensionsAllowed = anyExtensionsAllowed;
return this; return this;
} }
@JsonProperty("hintAboutNonMustSupport")
public boolean isHintAboutNonMustSupport() { public boolean isHintAboutNonMustSupport() {
return hintAboutNonMustSupport; return hintAboutNonMustSupport;
} }
@JsonProperty("hintAboutNonMustSupport")
public CliContext setHintAboutNonMustSupport(boolean hintAboutNonMustSupport) { public CliContext setHintAboutNonMustSupport(boolean hintAboutNonMustSupport) {
this.hintAboutNonMustSupport = hintAboutNonMustSupport; this.hintAboutNonMustSupport = hintAboutNonMustSupport;
return this; return this;
} }
@JsonProperty("recursive")
public boolean isRecursive() { public boolean isRecursive() {
return recursive; return recursive;
} }
@JsonProperty("recursive")
public CliContext setRecursive(boolean recursive) { public CliContext setRecursive(boolean recursive) {
this.recursive = recursive; this.recursive = recursive;
return this; return this;
} }
public Locale getLocale() { @JsonProperty("locale")
public String getLanguageCode() {
return locale; return locale;
} }
public CliContext setLocale(Locale locale) { public Locale getLocale() {
this.locale = locale; return Locale.forLanguageTag(this.locale);
}
@JsonProperty("locale")
public CliContext setLocale(String languageString) {
this.locale = languageString;
return this; return this;
} }
public CliContext setLocale(Locale locale) {
this.locale = locale.getDisplayLanguage();
return this;
}
@JsonProperty("profiles")
public List<String> getProfiles() { public List<String> getProfiles() {
return profiles; return profiles;
} }
@JsonProperty("profiles")
public CliContext setProfiles(List<String> profiles) { public CliContext setProfiles(List<String> profiles) {
this.profiles = profiles; this.profiles = profiles;
return this; return this;
@ -150,37 +213,45 @@ public class CliContext {
return this; return this;
} }
@JsonProperty("mode")
public Validator.EngineMode getMode() { public Validator.EngineMode getMode() {
return mode; return mode;
} }
@JsonProperty("mode")
public CliContext setMode(Validator.EngineMode mode) { public CliContext setMode(Validator.EngineMode mode) {
this.mode = mode; this.mode = mode;
return this; return this;
} }
@JsonProperty("output")
public String getOutput() { public String getOutput() {
return output; return output;
} }
@JsonProperty("output")
public CliContext setOutput(String output) { public CliContext setOutput(String output) {
this.output = output; this.output = output;
return this; return this;
} }
public Boolean getCanDoNative() { @JsonProperty("canDoNative")
public boolean getCanDoNative() {
return canDoNative; return canDoNative;
} }
public CliContext setCanDoNative(Boolean canDoNative) { @JsonProperty("canDoNative")
public CliContext setCanDoNative(boolean canDoNative) {
this.canDoNative = canDoNative; this.canDoNative = canDoNative;
return this; return this;
} }
@JsonProperty("sources")
public List<String> getSources() { public List<String> getSources() {
return sources; return sources;
} }
@JsonProperty("sources")
public CliContext setSources(List<String> sources) { public CliContext setSources(List<String> sources) {
this.sources = sources; this.sources = sources;
return this; return this;
@ -194,10 +265,12 @@ public class CliContext {
return this; return this;
} }
@JsonProperty("locations")
public Map<String, String> getLocations() { public Map<String, String> getLocations() {
return locations; return locations;
} }
@JsonProperty("locations")
public CliContext setLocations(Map<String, String> locations) { public CliContext setLocations(Map<String, String> locations) {
this.locations = locations; this.locations = locations;
return this; return this;
@ -208,104 +281,129 @@ public class CliContext {
return this; return this;
} }
@JsonProperty("sv")
public String getSv() { public String getSv() {
return sv; return sv;
} }
@JsonProperty("sv")
public CliContext setSv(String sv) { public CliContext setSv(String sv) {
this.sv = sv; this.sv = sv;
return this; return this;
} }
@JsonProperty("txLog")
public String getTxLog() { public String getTxLog() {
return txLog; return txLog;
} }
@JsonProperty("txLog")
public CliContext setTxLog(String txLog) { public CliContext setTxLog(String txLog) {
this.txLog = txLog; this.txLog = txLog;
return this; return this;
} }
@JsonProperty("mapLog")
public String getMapLog() { public String getMapLog() {
return mapLog; return mapLog;
} }
@JsonProperty("mapLog")
public CliContext setMapLog(String mapLog) { public CliContext setMapLog(String mapLog) {
this.mapLog = mapLog; this.mapLog = mapLog;
return this; return this;
} }
@JsonProperty("lang")
public String getLang() { public String getLang() {
return lang; return lang;
} }
@JsonProperty("lang")
public CliContext setLang(String lang) { public CliContext setLang(String lang) {
this.lang = lang; this.lang = lang;
return this; return this;
} }
@JsonProperty("fhirpath")
public String getFhirpath() { public String getFhirpath() {
return fhirpath; return fhirpath;
} }
@JsonProperty("fhirpath")
public CliContext setFhirpath(String fhirpath) { public CliContext setFhirpath(String fhirpath) {
this.fhirpath = fhirpath; this.fhirpath = fhirpath;
return this; return this;
} }
public String getSnomedCT() { public SnomedVersion getSnomedCT() {
return SnomedVersion.getFromCode(snomedCT);
}
@JsonProperty("snomedCT")
public String getSnomedCTCode() {
return snomedCT; return snomedCT;
} }
@JsonProperty("snomedCT")
public CliContext setSnomedCT(String snomedCT) { public CliContext setSnomedCT(String snomedCT) {
this.snomedCT = SnomedVersion.resolveSnomedCTCode(snomedCT); this.snomedCT = snomedCT;
return this; return this;
} }
@JsonProperty("targetVer")
public String getTargetVer() { public String getTargetVer() {
return targetVer; return targetVer;
} }
@JsonProperty("targetVer")
public CliContext setTargetVer(String targetVer) { public CliContext setTargetVer(String targetVer) {
this.targetVer = targetVer; this.targetVer = targetVer;
return this; return this;
} }
@JsonProperty("doDebug")
public boolean isDoDebug() { public boolean isDoDebug() {
return doDebug; return doDebug;
} }
@JsonProperty("doDebug")
public CliContext setDoDebug(boolean doDebug) { public CliContext setDoDebug(boolean doDebug) {
this.doDebug = doDebug; this.doDebug = doDebug;
return this; return this;
} }
@JsonProperty("assumeValidRestReferences")
public boolean isAssumeValidRestReferences() { public boolean isAssumeValidRestReferences() {
return assumeValidRestReferences; return assumeValidRestReferences;
} }
@JsonProperty("assumeValidRestReferences")
public CliContext setAssumeValidRestReferences(boolean assumeValidRestReferences) { public CliContext setAssumeValidRestReferences(boolean assumeValidRestReferences) {
this.assumeValidRestReferences = assumeValidRestReferences; this.assumeValidRestReferences = assumeValidRestReferences;
return this; return this;
} }
@JsonProperty("noInternalCaching")
public boolean isNoInternalCaching() { public boolean isNoInternalCaching() {
return noInternalCaching; return noInternalCaching;
} }
@JsonProperty("noInternalCaching")
public CliContext setNoInternalCaching(boolean noInternalCaching) { public CliContext setNoInternalCaching(boolean noInternalCaching) {
this.noInternalCaching = noInternalCaching; this.noInternalCaching = noInternalCaching;
return this; return this;
} }
@JsonProperty("noExtensibleBindingMessages")
public boolean isNoExtensibleBindingMessages() { public boolean isNoExtensibleBindingMessages() {
return noExtensibleBindingMessages; return noExtensibleBindingMessages;
} }
@JsonProperty("noExtensibleBindingMessages")
public CliContext setNoExtensibleBindingMessages(boolean noExtensibleBindingMessages) { public CliContext setNoExtensibleBindingMessages(boolean noExtensibleBindingMessages) {
this.noExtensibleBindingMessages = noExtensibleBindingMessages; this.noExtensibleBindingMessages = noExtensibleBindingMessages;
return this; return this;
} }
} }

View File

@ -0,0 +1,80 @@
package org.hl7.fhir.validation.cli.model;
/**
* A POJO for storing the flags/values for the CLI validator.
*/
public class CliContextTest {
private boolean doNative = false;
private boolean anyExtensionsAllowed = true;
private boolean hintAboutNonMustSupport = false;
private boolean recursive = false;
private boolean doDebug = false;
private boolean assumeValidRestReferences = false;
public boolean isDoNative() {
return doNative;
}
public CliContextTest setDoNative(boolean doNative) {
this.doNative = doNative;
return this;
}
public boolean isAnyExtensionsAllowed() {
return anyExtensionsAllowed;
}
public CliContextTest setAnyExtensionsAllowed(boolean anyExtensionsAllowed) {
this.anyExtensionsAllowed = anyExtensionsAllowed;
return this;
}
public boolean isHintAboutNonMustSupport() {
return hintAboutNonMustSupport;
}
public CliContextTest setHintAboutNonMustSupport(boolean hintAboutNonMustSupport) {
this.hintAboutNonMustSupport = hintAboutNonMustSupport;
return this;
}
public boolean isRecursive() {
return recursive;
}
public CliContextTest setRecursive(boolean recursive) {
this.recursive = recursive;
return this;
}
public boolean isDoDebug() {
return doDebug;
}
public CliContextTest setDoDebug(boolean doDebug) {
this.doDebug = doDebug;
return this;
}
public boolean isAssumeValidRestReferences() {
return assumeValidRestReferences;
}
public CliContextTest setAssumeValidRestReferences(boolean assumeValidRestReferences) {
this.assumeValidRestReferences = assumeValidRestReferences;
return this;
}
@Override
public String toString() {
return "CliContext{" +
", doNative=" + doNative +
", anyExtensionsAllowed=" + anyExtensionsAllowed +
", hintAboutNonMustSupport=" + hintAboutNonMustSupport +
", recursive=" + recursive +
", doDebug=" + doDebug +
", assumeValidRestReferences=" + assumeValidRestReferences +
'}';
}
}

View File

@ -0,0 +1,49 @@
package org.hl7.fhir.validation.cli.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hl7.fhir.r5.elementmodel.Manager;
public class FileInfo {
@JsonProperty("fileName")
private String fileName;
@JsonProperty("fileContent")
private String fileContent;
@JsonProperty("fileType")
private String fileType;
@JsonProperty("fileName")
public String getFileName() {
return fileName;
}
@JsonProperty("fileName")
public FileInfo setFileName(String fileName) {
this.fileName = fileName;
return this;
}
@JsonProperty("fileContent")
public String getFileContent() {
return fileContent;
}
@JsonProperty("fileContent")
public FileInfo setFileContent(String fileContent) {
this.fileContent = fileContent;
return this;
}
@JsonProperty("fileType")
public Manager.FhirFormat getFileType() {
return Manager.FhirFormat.JSON;
}
@JsonProperty("fileType")
public FileInfo setFileType(String fileType) {
this.fileType = fileType;
return this;
}
}

View File

@ -0,0 +1,32 @@
package org.hl7.fhir.validation.cli.model;
import com.fasterxml.jackson.annotation.JsonProperty;
public class ValidationIssue {
@JsonProperty("severity")
private String severity;
@JsonProperty("details")
private String details;
@JsonProperty("severity")
public String getSeverity() {
return severity;
}
@JsonProperty("severity")
public ValidationIssue setSeverity(String severity) {
this.severity = severity;
return this;
}
@JsonProperty("details")
public String getDetails() {
return details;
}
@JsonProperty("details")
public ValidationIssue setDetails(String details) {
this.details = details;
return this;
}
}

View File

@ -0,0 +1,47 @@
package org.hl7.fhir.validation.cli.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import org.hl7.fhir.r5.model.OperationOutcome;
import java.util.ArrayList;
import java.util.List;
public class ValidationOutcome {
@JsonProperty("fileInfo")
private FileInfo fileInfo;
@JsonProperty("issues")
private List<ValidationIssue> issues = new ArrayList<>();
@JsonProperty("fileInfo")
public FileInfo getFileInfo() {
return fileInfo;
}
@JsonProperty("fileInfo")
public ValidationOutcome setFileInfo(FileInfo fileInfo) {
this.fileInfo = fileInfo;
return this;
}
@JsonProperty("issues")
public List<ValidationIssue> getIssues() {
return issues;
}
@JsonProperty("issues")
public ValidationOutcome setIssues(List<ValidationIssue> issues) {
this.issues = issues;
return this;
}
public ValidationOutcome addIssue(OperationOutcome.OperationOutcomeIssueComponent outcome) {
String text = outcome.getDetails().getText();
text.replace("\'", "\"");
issues.add(new ValidationIssue()
.setSeverity(outcome.getSeverity().getDisplay())
.setDetails(outcome.getDetails().getText()));
return this;
}
}

View File

@ -0,0 +1,45 @@
package org.hl7.fhir.validation.cli.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
public class ValidationRequest {
@JsonProperty("cliContext")
private CliContext cliContext;
@JsonProperty("filesToValidate")
private List<FileInfo> filesToValidate = new ArrayList<>();
@JsonProperty("cliContext")
public CliContext getCliContext() {
return cliContext;
}
@JsonProperty("cliContext")
public ValidationRequest setCliContext(CliContext cliContext) {
this.cliContext = cliContext;
return this;
}
@JsonProperty("filesToValidate")
public List<FileInfo> getFilesToValidate() {
return filesToValidate;
}
@JsonProperty("filesToValidate")
public ValidationRequest setFilesToValidate(List<FileInfo> filesToValidate) {
this.filesToValidate = filesToValidate;
return this;
}
public String listSourceFiles() {
List<String> fileNames = new ArrayList<>();
for (FileInfo fp : filesToValidate) {
fileNames.add(fp.getFileName());
}
return String.join(", ", fileNames);
}
}

View File

@ -0,0 +1,33 @@
package org.hl7.fhir.validation.cli.model;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.ArrayList;
import java.util.List;
public class ValidationResponse {
@JsonProperty("outcomes")
public List<ValidationOutcome> outcomes = new ArrayList<>();
@JsonProperty("outcomes")
public List<ValidationOutcome> getOutcomes() {
return outcomes;
}
@JsonProperty("outcomes")
public ValidationResponse setOutcomes(List<ValidationOutcome> outcomes) {
this.outcomes = outcomes;
return this;
}
public ValidationResponse addOutcome(ValidationOutcome outcome) {
if (outcomes == null) {
outcomes = new ArrayList<>();
}
outcomes.add(outcome);
return this;
}
}

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.validation.cli; package org.hl7.fhir.validation.cli.services;
import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities; import org.hl7.fhir.r5.conformance.CapabilityStatementUtilities;
import org.hl7.fhir.r5.conformance.ProfileComparer; import org.hl7.fhir.r5.conformance.ProfileComparer;
@ -14,6 +14,7 @@ import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.validation.ValidationEngine; import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.utils.Params;
import java.awt.*; import java.awt.*;
import java.io.File; import java.io.File;
@ -21,7 +22,7 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.UUID; import java.util.UUID;
public class Comparison { public class ComparisonService {
public static void doLeftRightComparison(String[] args, String dest, ValidationEngine validator) throws IOException { public static void doLeftRightComparison(String[] args, String dest, ValidationEngine validator) throws IOException {
// ok now set up the comparison // ok now set up the comparison
@ -39,9 +40,9 @@ public class Comparison {
if (resLeft != null && resRight != null) { if (resLeft != null && resRight != null) {
if (resLeft instanceof StructureDefinition && resRight instanceof StructureDefinition) { if (resLeft instanceof StructureDefinition && resRight instanceof StructureDefinition) {
Comparison.compareStructureDefinitions(dest, validator, left, right, (StructureDefinition) resLeft, (StructureDefinition) resRight); ComparisonService.compareStructureDefinitions(dest, validator, left, right, (StructureDefinition) resLeft, (StructureDefinition) resRight);
} else if (resLeft instanceof CapabilityStatement && resRight instanceof CapabilityStatement) { } else if (resLeft instanceof CapabilityStatement && resRight instanceof CapabilityStatement) {
Comparison.compareCapabilityStatements(args, dest, validator, left, right, (CanonicalResource) resLeft, (CanonicalResource) resRight); ComparisonService.compareCapabilityStatements(args, dest, validator, left, right, (CanonicalResource) resLeft, (CanonicalResource) resRight);
} else } else
System.out.println("Unable to compare left resource " + left + " (" + resLeft.fhirType() + ") with right resource " + right + " (" + resRight.fhirType() + ")"); System.out.println("Unable to compare left resource " + left + " (" + resLeft.fhirType() + ") with right resource " + right + " (" + resRight.fhirType() + ")");
} }
@ -71,7 +72,7 @@ public class Comparison {
new JsonParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-intersection.json")), output.getSubset()); new JsonParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "CapabilityStatement-intersection.json")), output.getSubset());
new JsonParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "OperationOutcome-issues.json")), output.getOutcome()); new JsonParser().setOutputStyle(IParser.OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(dest, "OperationOutcome-issues.json")), output.getOutcome());
String destHtml = Utilities.path(dest, "index.html"); String destHtml = Utilities.path(dest, "public/index.html");
File htmlFile = new File(destHtml); File htmlFile = new File(destHtml);
Desktop.getDesktop().browse(htmlFile.toURI()); Desktop.getDesktop().browse(htmlFile.toURI());
System.out.println("Done"); System.out.println("Done");

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.validation.cli; package org.hl7.fhir.validation.cli.services;
import org.hl7.fhir.r5.context.TerminologyCache; import org.hl7.fhir.r5.context.TerminologyCache;
import org.hl7.fhir.r5.elementmodel.Manager; import org.hl7.fhir.r5.elementmodel.Manager;
@ -10,6 +10,7 @@ import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.validation.ValidationEngine; import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.*;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -17,13 +18,37 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
public class ValidationUtils { public class ValidationService {
public static ValidationResponse validateSources(ValidationRequest request, ValidationEngine validator) throws Exception {
if (request.getCliContext().getProfiles().size() > 0) {
System.out.println(" .. validate " + request.listSourceFiles() + " against " + request.getCliContext().getProfiles().toString());
} else {
System.out.println(" .. validate " + request.listSourceFiles());
}
validator.prepare(); // generate any missing snapshots
ValidationResponse response = new ValidationResponse();
for (FileInfo fp : request.getFilesToValidate()) {
OperationOutcome operationOutcome = validator.validate(fp.getFileContent().getBytes(), fp.getFileType(),
request.getCliContext().getProfiles());
ValidationOutcome outcome = new ValidationOutcome();
// Need to set file content to null as server can't handle json in json
fp.setFileContent(null);
outcome.setFileInfo(fp);
operationOutcome.getIssue().forEach(outcome::addIssue);
response.addOutcome(outcome);
}
return response;
}
public static void validateSources(CliContext cliContext, ValidationEngine validator) throws Exception { public static void validateSources(CliContext cliContext, ValidationEngine validator) throws Exception {
if (cliContext.getProfiles().size() > 0) if (cliContext.getProfiles().size() > 0) {
System.out.println(" .. validate " + cliContext.getSources() + " against " + cliContext.getProfiles().toString()); System.out.println(" .. validate " + cliContext.getSources() + " against " + cliContext.getProfiles().toString());
else } else {
System.out.println(" .. validate " + cliContext.getSources()); System.out.println(" .. validate " + cliContext.getSources());
}
validator.prepare(); // generate any missing snapshots validator.prepare(); // generate any missing snapshots
Resource r = validator.validate(cliContext.getSources(), cliContext.getProfiles()); Resource r = validator.validate(cliContext.getSources(), cliContext.getProfiles());
int ec = 0; int ec = 0;
@ -154,7 +179,7 @@ public class ValidationUtils {
validator.setAnyExtensionsAllowed(cliContext.isAnyExtensionsAllowed()); validator.setAnyExtensionsAllowed(cliContext.isAnyExtensionsAllowed());
validator.setLanguage(cliContext.getLang()); validator.setLanguage(cliContext.getLang());
validator.setLocale(cliContext.getLocale()); validator.setLocale(cliContext.getLocale());
validator.setSnomedExtension(cliContext.getSnomedCT()); validator.setSnomedExtension(cliContext.getSnomedCTCode());
validator.setAssumeValidRestReferences(cliContext.isAssumeValidRestReferences()); validator.setAssumeValidRestReferences(cliContext.isAssumeValidRestReferences());
validator.setNoExtensibleBindingMessages(cliContext.isNoExtensibleBindingMessages()); validator.setNoExtensibleBindingMessages(cliContext.isNoExtensibleBindingMessages());
TerminologyCache.setNoCaching(cliContext.isNoInternalCaching()); TerminologyCache.setNoCaching(cliContext.isNoInternalCaching());

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.validation.cli; package org.hl7.fhir.validation.cli.utils;
import org.hl7.fhir.r5.model.Constants; import org.hl7.fhir.r5.model.Constants;
import org.hl7.fhir.r5.model.FhirPublication; import org.hl7.fhir.r5.model.FhirPublication;
@ -75,11 +75,9 @@ public class Common {
} }
} }
public static ValidationEngine getValidationEngine(String[] args, String txLog) throws Exception { public static ValidationEngine getValidationEngine(String version, String definitions, String txLog) throws Exception {
String v = Common.getVersion(args); System.out.println("Loading (v = " + version + ", tx server http://tx.fhir.org)");
String definitions = VersionUtilities.packageForVersion(v) + "#" + v; return new ValidationEngine(definitions, "http://tx.fhir.org", txLog, FhirPublication.fromCode(version), version);
System.out.println("Loading (v = " + v + ", tx server http://tx.fhir.org)");
return new ValidationEngine(definitions, "http://tx.fhir.org", txLog, FhirPublication.fromCode(v), v);
} }
} }

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.validation.cli; package org.hl7.fhir.validation.cli.utils;
import org.hl7.fhir.r5.model.Constants; import org.hl7.fhir.r5.model.Constants;
import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.VersionUtilities;

View File

@ -1,7 +1,8 @@
package org.hl7.fhir.validation.cli; package org.hl7.fhir.validation.cli.utils;
import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.validation.Validator; import org.hl7.fhir.validation.Validator;
import org.hl7.fhir.validation.cli.model.CliContext;
import java.io.File; import java.io.File;
import java.util.Arrays; import java.util.Arrays;
@ -9,6 +10,7 @@ import java.util.Locale;
public class Params { public class Params {
public static final String GUI = "-gui";
public static final String VERSION = "-version"; public static final String VERSION = "-version";
public static final String OUTPUT = "-output"; public static final String OUTPUT = "-output";
public static final String PROXY = "-proxy"; public static final String PROXY = "-proxy";

View File

@ -1,6 +1,8 @@
package org.hl7.fhir.validation.cli; package org.hl7.fhir.validation.cli.utils;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
public enum SnomedVersion { public enum SnomedVersion {
@ -32,15 +34,15 @@ public enum SnomedVersion {
return code; return code;
} }
public static String resolveSnomedCTCode(String s) { public static SnomedVersion getFromCode(String code) {
String foundCode; return lookup.get(code);
Optional<SnomedVersion> opt = Arrays.stream(values()) }
.filter(v -> v.lang.equals(s))
.findFirst(); private static final Map<String, SnomedVersion> lookup = new HashMap<>();
if (opt.isPresent()) {
return opt.get().code; static {
} else { for (SnomedVersion s : SnomedVersion.values()) {
throw new Error("Snomed edition '" + s + "' not known"); lookup.put(s.getCode(), s);
} }
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
<g>
<path d="M33.655,45.988c-0.232-0.31-0.497-0.533-0.793-0.67s-0.608-0.205-0.937-0.205c-0.337,0-0.658,0.063-0.964,0.191
s-0.579,0.344-0.82,0.649s-0.431,0.699-0.567,1.183c-0.137,0.483-0.21,1.075-0.219,1.777c0.009,0.684,0.08,1.267,0.212,1.75
s0.314,0.877,0.547,1.183s0.497,0.528,0.793,0.67s0.608,0.212,0.937,0.212c0.337,0,0.658-0.066,0.964-0.198s0.579-0.349,0.82-0.649
s0.431-0.695,0.567-1.183s0.21-1.082,0.219-1.784c-0.009-0.684-0.08-1.265-0.212-1.743S33.888,46.298,33.655,45.988z"/>
<path d="M51.5,39V13.978c0-0.766-0.092-1.333-0.55-1.792L39.313,0.55C38.964,0.201,38.48,0,37.985,0H8.963
C7.777,0,6.5,0.916,6.5,2.926V39H51.5z M29.5,33c0,0.552-0.447,1-1,1s-1-0.448-1-1v-3c0-0.552,0.447-1,1-1s1,0.448,1,1V33z
M37.5,3.391c0-0.458,0.553-0.687,0.877-0.363l10.095,10.095C48.796,13.447,48.567,14,48.109,14H37.5V3.391z M36.5,24v-4
c0-0.551-0.448-1-1-1c-0.553,0-1-0.448-1-1s0.447-1,1-1c1.654,0,3,1.346,3,3v4c0,1.103,0.897,2,2,2c0.553,0,1,0.448,1,1
s-0.447,1-1,1c-1.103,0-2,0.897-2,2v4c0,1.654-1.346,3-3,3c-0.553,0-1-0.448-1-1s0.447-1,1-1c0.552,0,1-0.449,1-1v-4
c0-1.2,0.542-2.266,1.382-3C37.042,26.266,36.5,25.2,36.5,24z M28.5,22c0.828,0,1.5,0.672,1.5,1.5S29.328,25,28.5,25
c-0.828,0-1.5-0.672-1.5-1.5S27.672,22,28.5,22z M16.5,26c1.103,0,2-0.897,2-2v-4c0-1.654,1.346-3,3-3c0.553,0,1,0.448,1,1
s-0.447,1-1,1c-0.552,0-1,0.449-1,1v4c0,1.2-0.542,2.266-1.382,3c0.84,0.734,1.382,1.8,1.382,3v4c0,0.551,0.448,1,1,1
c0.553,0,1,0.448,1,1s-0.447,1-1,1c-1.654,0-3-1.346-3-3v-4c0-1.103-0.897-2-2-2c-0.553,0-1-0.448-1-1S15.947,26,16.5,26z"/>
<path d="M6.5,41v15c0,1.009,1.22,2,2.463,2h40.074c1.243,0,2.463-0.991,2.463-2V41H6.5z M18.021,51.566
c0,0.474-0.087,0.873-0.26,1.196s-0.405,0.583-0.697,0.779s-0.627,0.333-1.005,0.41c-0.378,0.077-0.768,0.116-1.169,0.116
c-0.2,0-0.436-0.021-0.704-0.062s-0.547-0.104-0.834-0.191s-0.563-0.185-0.827-0.294s-0.487-0.232-0.67-0.369l0.697-1.107
c0.091,0.063,0.221,0.13,0.39,0.198s0.354,0.132,0.554,0.191s0.41,0.111,0.629,0.157s0.424,0.068,0.615,0.068
c0.483,0,0.868-0.094,1.155-0.28s0.439-0.504,0.458-0.95v-7.711h1.668V51.566z M25.958,52.298c-0.15,0.342-0.362,0.643-0.636,0.902
s-0.61,0.467-1.012,0.622s-0.856,0.232-1.367,0.232c-0.219,0-0.444-0.012-0.677-0.034s-0.467-0.062-0.704-0.116
c-0.237-0.055-0.463-0.13-0.677-0.226s-0.398-0.212-0.554-0.349l0.287-1.176c0.128,0.073,0.289,0.144,0.485,0.212
s0.398,0.132,0.608,0.191s0.419,0.107,0.629,0.144s0.405,0.055,0.588,0.055c0.556,0,0.982-0.13,1.278-0.39s0.444-0.645,0.444-1.155
c0-0.31-0.104-0.574-0.314-0.793s-0.472-0.417-0.786-0.595s-0.654-0.355-1.019-0.533s-0.706-0.388-1.025-0.629
s-0.583-0.526-0.793-0.854s-0.314-0.738-0.314-1.23c0-0.446,0.082-0.843,0.246-1.189s0.385-0.641,0.663-0.882
s0.602-0.426,0.971-0.554s0.759-0.191,1.169-0.191c0.419,0,0.843,0.039,1.271,0.116s0.774,0.203,1.039,0.376
c-0.055,0.118-0.118,0.248-0.191,0.39s-0.142,0.273-0.205,0.396s-0.118,0.226-0.164,0.308s-0.073,0.128-0.082,0.137
c-0.055-0.027-0.116-0.063-0.185-0.109s-0.166-0.091-0.294-0.137s-0.296-0.077-0.506-0.096s-0.479-0.014-0.807,0.014
c-0.183,0.019-0.355,0.07-0.52,0.157s-0.31,0.193-0.438,0.321s-0.228,0.271-0.301,0.431s-0.109,0.313-0.109,0.458
c0,0.364,0.104,0.658,0.314,0.882s0.47,0.419,0.779,0.588s0.647,0.333,1.012,0.492s0.704,0.354,1.019,0.581
s0.576,0.513,0.786,0.854s0.314,0.781,0.314,1.319C26.184,51.603,26.108,51.956,25.958,52.298z M35.761,51.156
c-0.214,0.647-0.511,1.185-0.889,1.613s-0.82,0.752-1.326,0.971s-1.06,0.328-1.661,0.328s-1.155-0.109-1.661-0.328
s-0.948-0.542-1.326-0.971s-0.675-0.966-0.889-1.613s-0.321-1.395-0.321-2.242s0.107-1.593,0.321-2.235s0.511-1.178,0.889-1.606
s0.82-0.754,1.326-0.978s1.06-0.335,1.661-0.335s1.155,0.111,1.661,0.335s0.948,0.549,1.326,0.978s0.675,0.964,0.889,1.606
s0.321,1.388,0.321,2.235S35.975,50.509,35.761,51.156z M45.68,54h-1.668l-3.951-6.945V54h-1.668V43.924h1.668l3.951,6.945v-6.945
h1.668V54z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 58 58" style="enable-background:new 0 0 58 58;" xml:space="preserve">
<g>
<path d="M51.5,39V13.978c0-0.766-0.092-1.333-0.55-1.792L39.313,0.55C38.964,0.201,38.48,0,37.985,0H8.963
C7.777,0,6.5,0.916,6.5,2.926V39H51.5z M37.5,3.391c0-0.458,0.553-0.687,0.877-0.363l10.095,10.095
C48.796,13.447,48.567,14,48.109,14H37.5V3.391z M33.793,18.707c-0.391-0.391-0.391-1.023,0-1.414s1.023-0.391,1.414,0l6,6
c0.391,0.391,0.391,1.023,0,1.414l-6,6C35.012,30.902,34.756,31,34.5,31s-0.512-0.098-0.707-0.293
c-0.391-0.391-0.391-1.023,0-1.414L39.086,24L33.793,18.707z M24.557,31.667l6-17c0.185-0.521,0.753-0.795,1.276-0.61
c0.521,0.184,0.794,0.755,0.61,1.276l-6,17C26.298,32.744,25.912,33,25.5,33c-0.11,0-0.223-0.019-0.333-0.058
C24.646,32.759,24.373,32.188,24.557,31.667z M15.793,23.293l6-6c0.391-0.391,1.023-0.391,1.414,0s0.391,1.023,0,1.414L17.914,24
l5.293,5.293c0.391,0.391,0.391,1.023,0,1.414C23.012,30.902,22.756,31,22.5,31s-0.512-0.098-0.707-0.293l-6-6
C15.402,24.316,15.402,23.684,15.793,23.293z"/>
<path d="M6.5,41v15c0,1.009,1.22,2,2.463,2h40.074c1.243,0,2.463-0.991,2.463-2V41H6.5z M22.936,54h-1.9l-1.6-3.801h-0.137
L17.576,54h-1.9l2.557-4.895l-2.721-5.182h1.873l1.777,4.102h0.137l1.928-4.102H23.1l-2.721,5.182L22.936,54z M34.666,54h-1.668
v-6.932l-2.256,5.605h-1.449l-2.27-5.605V54h-1.668V43.924h1.668l2.994,6.891l2.98-6.891h1.668V54z M43.498,54h-6.303V43.924h1.668
v8.832h4.635V54z"/>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
<g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,56 @@
<!--aside id="sidebar">
<div class="">
<label>Validation Options</label>
<ul>
<li><input type="checkbox" id="doNative">doNative</li>
<li><input type="checkbox" id="anyExtensionAllowed">anyExtensionAllowed</li>
<li><input type="checkbox" id="hintAboutNonMustSupport">hintAboutNonMustSupport</li>
<li><input type="checkbox" id="recursive">recursive</li>
<li><input type="checkbox" id="doDebug">doDebug</li>
<li><input type="checkbox" id="assumeValidRestReferences">assumeValidRestReferences</li>
<li><input type="checkbox" name="canDoNative" id="canDoNative">canDoNative</li>
<li>
<label>Map</label>
<input type="text" name="map" placeholder="enter map">
</li>
<li>
<label>Output</label>
<input type="text" name="output" placeholder="enter output">
</li>
<li>
<label>txServer</label>
<input type="text" name="txServer" placeholder="enter txServer">
</li>
<li>
<label>sv</label>
<input type="text" name="sv" placeholder="enter sv">
</li>
<li>
<label>txLog</label>
<input type="text" name="txLog" placeholder="enter txLog">
</li>
<li>
<label>mapLog</label>
<input type="text" name="mapLog" placeholder="enter mapLog">
</li>
<li>
<label>lang</label>
<input type="text" name="lang" placeholder="enter lang">
</li>
<li>
<label>fhirpath</label>
<input type="text" name="fhirpath" placeholder="enter fhirpath">
</li>
<li>
<label>snomedCT</label>
<input type="text" name="snomedCT" placeholder="enter snomedCT">
</li>
<li>
<label>targetVer</label>
<input type="text" name="targetVer" placeholder="enter targetVer">
</li>
</ul>
<input id='test_button' class="btn" type="submit" value="Submit">
</div>
</aside-->

View File

@ -0,0 +1,10 @@
export const EngineMode {
VALIDATION,
TRANSFORM,
NARRATIVE,
SNAPSHOT,
SCAN,
CONVERT,
FHIRPATH,
VERSION
}

View File

@ -0,0 +1,17 @@
export const FhirFormat = {
XML: {
extension:'xml'
},
JSON: {
extension:'json'
},
TURTLE: {
extension:'ttl'
},
TEXT: {
extension:'txt'
},
VBAR: {
extension:'hl7'
},
}

View File

@ -0,0 +1,14 @@
export const IssueSeverity = {
FATAL: {
code: 'Fatal'
},
ERROR: {
code: 'Error'
},
WARNING: {
code: 'Warning'
},
INFORMATION: {
code: 'Information'
}
}

View File

@ -0,0 +1,30 @@
export const Locale = {
ENGLISH: {
code: 'en',
value: 'English'
},
FRENCH: {
code: 'fr',
value: 'French'
},
GERMAN: {
code: 'de',
value: 'German'
},
ITALIAN: {
code: 'it',
value: 'Italian'
},
JAPANESE: {
code: 'ja',
value: 'Japanese'
},
KOREAN: {
code: 'ko',
value: 'Korean'
},
CHINESE: {
code: 'zh',
value: 'Chinese'
}
}

View File

@ -0,0 +1,38 @@
export const SnomedVersion = {
INTL: {
value: "intl",
code: "900000000000207008"
},
US: {
value: "us",
code: "731000124108"
},
UK: {
value: "uk",
code: "999000041000000102"
},
AU: {
value: "au",
code: "32506021000036107"
},
CA: {
value: "ca",
code: "20611000087101"
},
NL: {
value: "nl",
code: "11000146104"
},
SE: {
value: "se",
code: "45991000052106"
},
ES: {
value: "es",
code: "449081005"
},
DK: {
value: "dk",
code: "554471000005108"
}
}

View File

@ -0,0 +1,99 @@
<html lang="en"><head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="Mark Iantorno">
<meta name="generator" content="Jekyll v3.8.6">
<title>FHIR HL7 Resrouce Validator GUI</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="canonical" href="https://getbootstrap.com/docs/4.4/examples/offcanvas/">
<!-- Favicons -->
<meta name="theme-color" content="#563d7c">
<style>
.bd-placeholder-img {
font-size: 1.125rem;
text-anchor: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
@media (min-width: 768px) {
.bd-placeholder-img-lg {
font-size: 3.5rem;
}
}
</style>
<!-- Custom styles for this template -->
<link href="style.css" rel="stylesheet">
</head>
<body class="bg-light">
<main role="main" class="container">
<div class="d-flex align-items-center p-3 my-3 text-white-50 bg-secondary rounded shadow-sm">
<img class="mr-3" src="./assets/fhir-logo-www.png" alt="" height="48">
<div class="lh-100">
<h6 class="mb-0 text-white lh-100">FHIR Core Validator</h6>
<small class="text-white">Experimental</small>
</div>
</div>
<div class="my-3 p-3 bg-white rounded shadow-sm">
<div class="d-flex border-bottom border-gray justify-content-between align-items-center w-100">
<h6 class="pb-2 mb-0">Files to Validate</h6>
<button id="validate_button" class="btn btn-secondary mb-1">Validate</button>
</div>
<div class="accordion" id="accordionExample">
<ul id="file_entry_file_list" class="list-group border">
</ul>
</div>
<div class="bd-callout bd-callout-info">
<small class="d-block text-right mt-3">
<input type="file" style="display:none;" id="files" name="files[]" multiple="" >
<button type="button" class="btn btn-secondary" onclick="document.getElementById('files').click()">Add Files</button>
</small>
</div>
<template id="file_entry_template">
<li class="list-unstyled">
<div class="card">
<div class="card-header" id="headingOne">
<div class="d-flex">
<div class="d-flex align-items-center">
<img id="file_entry_type_icon" class="bd-placeholder-img mr-2 rounded" src="./assets/json-svgrepo-com.svg" width="32" height="32" preserveaspectratio="xMidYMid slice">
<button id="file_entry_name_field" class="btn btn-link" type="button" data-toggle="collapse" data-target="#file_entry_collapse_section" aria-expanded="true" aria-controls="file_entry_collapse_section">
Collapsible Group Item #1
</button>
</div>
<div class="ml-auto d-flex">
<svg id="file_entry_delete_button" class="delete_button align-self-center" height="24" viewBox="0 0 24 24" width="24">
<path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zm2.46-7.12l1.41-1.41L12 12.59l2.12-2.12 1.41 1.41L13.41 14l2.12 2.12-1.41 1.41L12 15.41l-2.12 2.12-1.41-1.41L10.59 14l-2.13-2.12zM15.5 4l-1-1h-5l-1 1H5v2h14V4z"></path>
</svg>
</div>
</div>
</div>
<div id="file_entry_collapse_section" class="collapse" aria-labelledby="headingOne" data-parent="#accordionExample">
<div class="card-body">
<ul id="file_entry_outcome_list" class="list-group">
</ul>
</div>
</div>
</div>
</li>
</template>
</div>
</main>
<script type="module" src="./main.js"></script>
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
</body></html>

View File

@ -0,0 +1,206 @@
import {CliContext} from './model/CliContext.js';
import {ValidationRequest} from './model/ValidationRequest.js';
import {FileInfo} from './model/FileInfo.js';
import {FhirFormat} from './enums/FhirFormat.js';
import {IssueSeverity} from './enums/IssueSeverity.js';
/*
<Constants>
*/
// Icon Constants
const jsonIcon = './assets/json-svgrepo-com.svg';
const xmlIcon = './assets/xml-svgrepo-com.svg';
// HTML classes
const CLASS_LIST_ITEM_PLAIN = "list-group-item";
const CLASS_LIST_ITEM_FATAL = "list-group-item list-group-item-dark";
const CLASS_LIST_ITEM_ERROR = "list-group-item list-group-item-danger";
const CLASS_LIST_ITEM_WARNING = "list-group-item list-group-item-warning";
const CLASS_LIST_ITEM_INFORMATION = "list-group-item list-group-item-info";
// HTML IDs
const ID_FILE_ENTRY_TEMPLATE = '#file_entry_template';
const ID_FILE_ENTRY_NAME = 'file_entry_name_field';
const ID_FILE_ENTRY_ICON = 'file_entry_type_icon';
const ID_FILE_ENTRY_DELETE_BUTTON = 'file_entry_delete_button';
const ID_FILE_ENTRY_FILE_LIST = 'file_entry_file_list';
const ID_FILE_ENTRY_OUTCOME_LIST = 'file_entry_outcome_list';
const ID_FILE_ENTRY_COLLAPSE_SECTION = 'file_entry_collapse_section';
/*
</Constants>
*/
// Data Fields
const cli = new CliContext();
const filesToValidate = [];
document.getElementById('validate_button').addEventListener("click", validateCurrentFiles);
document.getElementById('files').addEventListener('change', handleFileSelect, false);
// File reading
function handleFileSelect(evt) {
var files = evt.target.files;
var output = [];
for (var i = 0, f; f = files[i]; i++) {
generateFileEntry(f);
// Check for proper file type here
console.log(f.type);
}
}
function generateFileEntry(file) {
var fr = new FileReader();
fr.onload = function(e) {
// TODO there may be timing issues here
filesToValidate.push(new FileInfo(file.name, e.target.result, file.type));
document.getElementById(ID_FILE_ENTRY_FILE_LIST).appendChild(generateNewFileListItemFromTemplate(file, filesToValidate.length - 1));
};
fr.readAsText(file);
}
// File List Template Generation
function generateNewFileListItemFromTemplate(file, index) {
var template = document.querySelector(ID_FILE_ENTRY_TEMPLATE);
var clone = template.content.cloneNode(true);
// Add file name
clone.getElementById(ID_FILE_ENTRY_NAME).textContent = file.name;
// Add appropriate icon for filetype
if (file.type.includes(FhirFormat.JSON.extension)) {
clone.getElementById(ID_FILE_ENTRY_ICON).src = jsonIcon;
} else if (file.type.includes(FhirFormat.XML.extension)) {
clone.getElementById(ID_FILE_ENTRY_ICON).src = xmlIcon;
}
clone.getElementById(ID_FILE_ENTRY_COLLAPSE_SECTION).setAttribute("id", (ID_FILE_ENTRY_COLLAPSE_SECTION + index));
clone.getElementById(ID_FILE_ENTRY_NAME).setAttribute("aria-controls", (ID_FILE_ENTRY_COLLAPSE_SECTION + index));
clone.getElementById(ID_FILE_ENTRY_NAME).setAttribute("data-target", ('#' + ID_FILE_ENTRY_COLLAPSE_SECTION + index));
// Add delete listener
clone.getElementById(ID_FILE_ENTRY_DELETE_BUTTON).addEventListener("click", handleDeleteOnFileList);
return clone;
}
function addIssueToFileEntryList(index, severity, details) {
var ul = document.getElementById(ID_FILE_ENTRY_FILE_LIST);
var listItems = ul.children;
var fileEntry = listItems[index];
console.log(fileEntry);
var listOfIssues = fileEntry.querySelector('#' + ID_FILE_ENTRY_OUTCOME_LIST);
var issueItem = createIssueListItem(severity, details);
listOfIssues.appendChild(issueItem);
}
function createIssueListItem(severity, details) {
var newIssue = document.createElement('li');
switch(severity) {
case IssueSeverity.FATAL.code:
newIssue.setAttribute("class", CLASS_LIST_ITEM_FATAL);
break;
case IssueSeverity.ERROR.code:
newIssue.setAttribute("class", CLASS_LIST_ITEM_ERROR);
break;
case IssueSeverity.WARNING.code:
newIssue.setAttribute("class", CLASS_LIST_ITEM_WARNING);
break;
case IssueSeverity.INFORMATION.code:
newIssue.setAttribute("class", CLASS_LIST_ITEM_INFORMATION);
break;
default:
console.error('Passed in bad severity: ' + severity);
}
newIssue.innerHTML = details;
return newIssue;
}
function handleDeleteOnFileList(e) {
var li = e.target.closest('li');
var nodes = Array.from( li.closest('ul').children );
var index = nodes.indexOf( li );
nodes[index].remove();
filesToValidate.splice((index - 1), 1);
console.log('Index -> ' + index);
console.log(filesToValidate);
}
// Validation
function validateCurrentFiles() {
sendFilesToValidate(filesToValidate);
}
function sendFilesToValidate(arrayOfFileInfo) {
var req = new ValidationRequest();
req.filesToValidate = arrayOfFileInfo;
var xhr = new XMLHttpRequest();
xhr.open("POST", 'http://localhost:8080/' + 'validate', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(req));
xhr.onload = function () {
const response = '{"outcomes": [{"fileInfo": {"fileName": "account-example.canonical.json", "fileType": "JSON"}, "issues": [{"severity": "Error", "details": "\"null\""}, {"severity": "Error", "details": "Profile http://hl7.org/fhir/StructureDefinition/Account, Element: minimum required = 1, but only found 0"}]}, {"fileInfo": {"fileName": "account-example(example).xml", "fileType": "XML"}, "issues": []}]}';
var test = xhr.responseText;
processValidationResponse(JSON.parse(JSON.parse(xhr.responseText)));
};
}
function processValidationResponse(validationResponse) {
console.log(validationResponse);
console.log(validationResponse.length);
console.log(validationResponse.outcomes);
console.log(validationResponse.outcomes.length);
for (var i = 0; i < validationResponse.outcomes.length; i++) {
console.log(validationResponse.outcomes[i]);
var fileInfo = validationResponse.outcomes[i].fileInfo;
var issues = validationResponse.outcomes[i].issues;
issues.forEach(issue => {
console.log(issue);
addIssueToFileEntryList(i, issue.severity, issue.details);
});
}
}
// window.onload = function loadCliContext() {
// var xhr = new XMLHttpRequest();
// xhr.open("GET", 'http://localhost:8080/currentContext');
// xhr.send();
// xhr.onreadystatechange = function() {
// if (this.readyState == 4 && this.status == 200) {
// console.log(xhr.responseText);
// }
// }
// }
// /*
// Escape JSON
// */
// var escapeJSON = exports.escapeJSON = function(json) {
// var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
// var meta = { // table of character substitutions
// '\b': '\\b',
// '\t': '\\t',
// '\n': '\\n',
// '\f': '\\f',
// '\r': '\\r',
// '"' : '\\"',
// '\\': '\\\\'
// };
//
// escapable.lastIndex = 0;
// return escapable.test(json) ? '"' + json.replace(escapable, function (a) {
// var c = meta[a];
// return (typeof c === 'string') ? c
// : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
// }) + '"' : '"' + json + '"';
//
// };

View File

@ -0,0 +1,34 @@
import {SnomedVersion} from '../enums/SnomedVersion.js';
import {Locale} from '../enums/Locale.js';
export class CliContext {
constructor(){
this.doNative = false;
this.anyExtensionsAllowed = true;
this.hintAboutNonMustSupport = false;
this.recursive = false;
this.doDebug = false;
this.assumeValidRestReferences = false;
this.canDoNative = false;
this.map = null;
this.output = null;
this.txServer = "http://tx.fhir.org";
this.sv = "current";
this.txLog = null;
this.mapLog = null;
this.lang = null;
this.fhirpath = null;
this.snomedCT = SnomedVersion.ES.value;
this.targetVer = null;
this.igs = [];
this.questionnaires = [];
this.profiles = [];
this.sources = [];
this.locale = Locale.ENGLISH.value;
this.locations = new Map();
}
}

View File

@ -0,0 +1,13 @@
import {FhirFormat} from '../enums/FhirFormat.js';
export class FileInfo {
constructor(fileName, fileContent, type){
this.fileName = fileName;
this.fileContent = fileContent;
if (type.includes('json')) {
this.fileType = FhirFormat.JSON.extension;
} else if (type.includes('xml')) {
this.fileType = FhirFormat.XML.extension;
}
}
}

View File

@ -0,0 +1,6 @@
export class ValidationIssue {
constructor(){
this.severity = "";
this.details = "";
}
}

View File

@ -0,0 +1,8 @@
import {FileInfo} from './FileInfo.js';
export class ValidationOutcome {
constructor(){
this.fileInfo = null;
this.issues = [];
}
}

View File

@ -0,0 +1,8 @@
import {CliContext} from './CliContext.js';
export class ValidationRequest {
constructor(){
this.cliContext = new CliContext();
this.filesToValidate = [];
}
}

View File

@ -0,0 +1,7 @@
import {ValidationOutcome} from './ValidationOutcome.js';
export class ValidationResponse {
constructor() {
this.outcomes = [];
}
}

View File

@ -0,0 +1,94 @@
/* */
.container{
width:80%;
margin:auto;
overflow: hidden;
}
#main{
float: left;
width: 70%;
}
#sidebar{
float: right;
width: 30%;
}
html,
body {
overflow-x: hidden; /* Prevent scroll on narrow devices */
}
body {
padding-top: 56px;
}
@media (max-width: 991.98px) {
.offcanvas-collapse {
position: fixed;
top: 56px; /* Height of navbar */
bottom: 0;
left: 100%;
width: 100%;
padding-right: 1rem;
padding-left: 1rem;
overflow-y: auto;
visibility: hidden;
background-color: #343a40;
transition: visibility .3s ease-in-out, -webkit-transform .3s ease-in-out;
transition: transform .3s ease-in-out, visibility .3s ease-in-out;
transition: transform .3s ease-in-out, visibility .3s ease-in-out, -webkit-transform .3s ease-in-out;
}
.offcanvas-collapse.open {
visibility: visible;
-webkit-transform: translateX(-100%);
transform: translateX(-100%);
}
}
.nav-scroller {
position: relative;
z-index: 2;
height: 2.75rem;
overflow-y: hidden;
}
.nav-scroller .nav {
display: -ms-flexbox;
display: flex;
-ms-flex-wrap: nowrap;
flex-wrap: nowrap;
padding-bottom: 1rem;
margin-top: -1px;
overflow-x: auto;
color: rgba(255, 255, 255, .75);
text-align: center;
white-space: nowrap;
-webkit-overflow-scrolling: touch;
}
.nav-underline .nav-link {
padding-top: .75rem;
padding-bottom: .75rem;
font-size: .875rem;
color: #6c757d;
}
.nav-underline .nav-link:hover {
color: #007bff;
}
.nav-underline .active {
font-weight: 500;
color: #343a40;
}
.text-white-50 { color: rgba(255, 255, 255, .5); }
.bg-purple { background-color: #6f42c1; }
.lh-100 { line-height: 1; }
.lh-125 { line-height: 1.25; }
.lh-150 { line-height: 1.5; }

View File

@ -0,0 +1,36 @@
package org.hl7.fhir.validation.cli;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import java.io.IOException;
public abstract class BaseRestTest {
protected final String JSON_MIME_TYPE = "application/json";
@BeforeAll
public static void startServer() {
ValidatorGui.start(new CliContext(), null, false);
}
@AfterAll
public static void stopServer() {
ValidatorGui.stop();
}
public static <T> T retrieveResourceFromResponse(HttpResponse response, Class<T> clazz)
throws IOException {
String jsonFromResponse = EntityUtils.toString(response.getEntity());
ObjectMapper mapper = new ObjectMapper()
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper.readValue(jsonFromResponse, clazz);
}
}

View File

@ -0,0 +1,33 @@
package org.hl7.fhir.validation.cli;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import java.io.IOException;
class ValidatorGuiTest {
private final String HTML_TITLE_TAG = "<title>FHIR HL7 Resrouce Validator GUI</title>";
@Test
@DisplayName("Page boots correctly, and displays index.html")
public void UI_contains_correct_heading() throws IOException {
ValidatorGui.start(new CliContext(), null, false);
WebDriverManager.chromedriver().setup();
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless");
options.addArguments("--disable-gpu");
WebDriver driver = new ChromeDriver(options);
driver.get("http://localhost:8080/home");
Assertions.assertTrue(driver.getPageSource().contains(HTML_TITLE_TAG));
driver.quit();
ValidatorGui.stop();
}
}

View File

@ -0,0 +1,47 @@
package org.hl7.fhir.validation.cli.controller;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.HttpClientBuilder;
import org.hl7.fhir.validation.cli.BaseRestTest;
import org.hl7.fhir.validation.cli.model.CliContext;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import java.io.IOException;
class HttpGetContextTest extends BaseRestTest {
private final String GET_CONTEXT_URL = "http://localhost:8080/context";
@Test
@DisplayName("Testing status code on get context endpoint.")
public void testStatus() throws IOException {
HttpUriRequest request = new HttpGet(GET_CONTEXT_URL);
HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request);
Assertions.assertEquals(httpResponse.getStatusLine().getStatusCode(), HttpStatus.SC_OK);
}
@Test
@DisplayName("Testing media type on get context endpoint.")
public void testMediaType() throws IOException {
HttpUriRequest request = new HttpGet(GET_CONTEXT_URL);
HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request);
String mimeType = ContentType.getOrDefault(httpResponse.getEntity()).getMimeType();
Assertions.assertEquals(JSON_MIME_TYPE, mimeType );
}
@Test
@DisplayName("Testing status code on get context endpoint.")
public void testJSONPayload() throws IOException {
HttpUriRequest request = new HttpGet(GET_CONTEXT_URL);
HttpResponse httpResponse = HttpClientBuilder.create().build().execute(request);
CliContext resource = retrieveResourceFromResponse(httpResponse, CliContext.class);
Assertions.assertEquals(new CliContext(), resource);
}
}

View File

@ -0,0 +1,24 @@
package org.hl7.fhir.validation.cli.controller;
import io.javalin.http.Context;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
class HttpPutContextTest {
public CliContextController myCliContextController;
public HttpPutContextTest() {
this.myCliContextController = new CliContextController(null);
}
@Test
void handleSetCurrentCliContext() {
Context context = mock(Context.class);
this.myCliContextController.handleSetCurrentCliContext(context);
verify(context).status(200);
}
}