This commit is contained in:
markiantorno 2020-04-16 10:17:33 -04:00
parent 7affad01fc
commit 8c3e03399e
43 changed files with 1549 additions and 72 deletions

View File

@ -24,6 +24,8 @@ package org.hl7.fhir.r5.elementmodel;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
@ -34,22 +36,34 @@ import org.hl7.fhir.r5.model.StructureDefinition;
public class Manager {
public enum FhirFormat { XML, JSON, TURTLE, TEXT, VBAR;
//TODO use EnumMap
public enum FhirFormat {
XML("xml"),
JSON("json"),
TURTLE("ttl"),
TEXT("txt"),
VBAR("hl7");
final String extension;
FhirFormat(String extension) {
this.extension = extension;
}
public String getExtension() {
switch (this) {
case JSON:
return "json";
case TURTLE:
return "ttl";
case XML:
return "xml";
case TEXT:
return "txt";
case VBAR:
return "hl7";
return this.extension;
}
private static final Map<String, FhirFormat> lookup = new HashMap<>();
static {
for(FhirFormat ff : FhirFormat.values()) {
lookup.put(ff.getExtension(), ff);
}
return null;
}
public static FhirFormat get(String extension) {
return lookup.get(extension);
}
}

View File

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

View File

@ -12,6 +12,10 @@
<artifactId>org.hl7.fhir.validation</artifactId>
<packaging>bundle</packaging>
<properties>
<micronaut.version>1.3.4</micronaut.version>
</properties>
<dependencies>
<!-- HAPI Dependencies -->
@ -99,12 +103,26 @@
<version>1.0.2</version>
</dependency>
<!-- Test Dependencies -->
<dependency>
<groupId>org.hl7.fhir.testcases</groupId>
<artifactId>fhir-test-cases</artifactId>
<version>${validator_test_case_version}</version>
<scope>test</scope>
<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>
</dependencies>

View File

@ -1070,6 +1070,13 @@ public class ValidationEngine implements IValidatorResourceFetcher {
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 {
List<ValidationMessage> messages = new ArrayList<ValidationMessage>();
if (doNative) {

View File

@ -49,12 +49,17 @@ POSSIBILITY OF SUCH DAMAGE.
*/
import java.io.File;
import java.net.URI;
import org.hl7.fhir.r5.model.ImplementationGuide;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.utilities.Utilities;
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
@ -75,6 +80,8 @@ public class Validator {
VALIDATION, TRANSFORM, NARRATIVE, SNAPSHOT, SCAN, CONVERT, FHIRPATH, VERSION
}
private static CliContext cliContext;
private static String getNamedParam(String[] args, String param) {
boolean found = false;
for (String a : args) {
@ -91,7 +98,29 @@ public class Validator {
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 {
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");
String proxy = getNamedParam(args, Params.PROXY);
@ -101,7 +130,13 @@ public class Validator {
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);
}else if (Params.hasParam(args, Params.TEST)) {
Common.runValidationEngineTests();
} else if (args.length == 0 || Params.hasParam(args, Params.HELP) || Params.hasParam(args, "?") || Params.hasParam(args, "-?") || Params.hasParam(args, "/?")) {
Display.displayHelpDetails();
@ -115,30 +150,32 @@ public class Validator {
else {
// first, prepare the context
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);
Comparison.doLeftRightComparison(args, dest, validator);
ComparisonService.doLeftRightComparison(args, dest, validator);
}
} else {
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
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) {
ValidationUtils.transformVersion(cliContext, validator);
ValidationService.transformVersion(cliContext, validator);
} else if (cliContext.getMode() == EngineMode.TRANSFORM) {
ValidationUtils.transform(cliContext, validator);
ValidationService.transform(cliContext, validator);
} else if (cliContext.getMode() == EngineMode.NARRATIVE) {
ValidationUtils.generateNarrative(cliContext, validator);
ValidationService.generateNarrative(cliContext, validator);
} else if (cliContext.getMode() == EngineMode.SNAPSHOT) {
ValidationUtils.generateSnapshot(cliContext, validator);
ValidationService.generateSnapshot(cliContext, validator);
} else if (cliContext.getMode() == EngineMode.CONVERT) {
ValidationUtils.convertSources(cliContext, validator);
ValidationService.convertSources(cliContext, validator);
} else if (cliContext.getMode() == EngineMode.FHIRPATH) {
ValidationUtils.evaluateFhirpath(cliContext, validator);
ValidationService.evaluateFhirpath(cliContext, validator);
} else {
if (definitions == null) {
throw new Exception("Must provide a defn when doing validation");
@ -150,9 +187,9 @@ public class Validator {
}
}
if (cliContext.getMode() == EngineMode.SCAN) {
ValidationUtils.validateScan(cliContext, validator);
ValidationService.validateScan(cliContext, validator);
} else {
ValidationUtils.validateSources(cliContext, validator);
ValidationService.validateSources(cliContext, validator);
}
}
}

View File

@ -0,0 +1,30 @@
package org.hl7.fhir.validation.cli;
import com.fasterxml.jackson.databind.ObjectMapper;
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.getCurrentCliContext);
app.post("/context", myCliContextController.setCurrentCliContext);
app.post("/validate", myValidationController.handleValidationRequest);
}
}

View File

@ -0,0 +1,37 @@
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;
public class ValidatorGui {
private static final String WEB_APP_FILE_LOCATION = "/public";
private static Javalin app;
/**
* 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);
}
public static void start(CliContext currentContext, ValidationEngine validationEngine) {
app = Javalin.create();
new RestEndpoints().initRestEndpoints(app, new CliContext(), validationEngine);
app.config.addStaticFiles(WEB_APP_FILE_LOCATION);
app.start(8080);
}
public static void stop() {
app.stop();
}
}

View File

@ -0,0 +1,27 @@
package org.hl7.fhir.validation.cli.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.javalin.http.Handler;
import org.apache.http.HttpStatus;
import org.hl7.fhir.validation.cli.model.CliContext;
public class CliContextController {
private CliContext myCliContext;
public CliContextController(CliContext cliContext) {
this.myCliContext = cliContext;
}
public Handler getCurrentCliContext = ctx -> {
ObjectMapper Obj = new ObjectMapper();
String jsonStr = Obj.writeValueAsString(myCliContext);
ctx.result(jsonStr);
};
public Handler setCurrentCliContext = ctx -> {
myCliContext = ctx.bodyAsClass(CliContext.class);
ctx.status(HttpStatus.SC_CREATED);
};
}

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/test.html");
};
}

View File

@ -0,0 +1,31 @@
package org.hl7.fhir.validation.cli.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.javalin.http.Handler;
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 Handler handleValidationRequest = ctx -> {
ValidationRequest request = ctx.bodyAsClass(ValidationRequest.class);
ValidationResponse response = ValidationService.validateSources(request, myValidationEngine);
// File new temp file
// DeleteOnShutdown
ObjectMapper Obj = new ObjectMapper();
String jsonStr = Obj.writeValueAsString(response);
ctx.status(200).json(jsonStr);
};
}

View File

@ -0,0 +1,29 @@
{
"outcomes": [
{
"fileInfo": {
"fileName": "account-example.canonical.json",
"fileContent": "{\"id\":\"example\",\"resourceType\":\"Account\",\"text\":{\"div\":\"<div>[Put rendering here]</div>\",\"status\":\"generated\"}}",
"fileType": "JSON"
},
"issues": [
{
"severity": "Error",
"details": "Wrong namespace on the XHTML (\"null\", should be \"http://www.w3.org/1999/xhtml\")"
},
{
"severity": "Error",
"details": "Profile http://hl7.org/fhir/StructureDefinition/Account, Element 'Account.status': minimum required = 1, but only found 0"
}
]
},
{
"fileInfo": {
"fileName": "account-example(example).xml",
"fileContent": "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Account xmlns=\"http://hl7.org/fhir\">\n\t<id value=\"example\"/>\n\t<meta>\n <security>\n <system value=\"http://terminology.hl7.org/CodeSystem/v3-ActReason\"/>\n <code value=\"HTEST\"/>\n <display value=\"test health data\"/>\n </security>\n </meta>\n <text>\n\t\t<status value=\"generated\"/>\n\t\t<div xmlns=\"http://www.w3.org/1999/xhtml\">HACC Funded Billing for Peter James Chalmers</div>\n\t</text>\n\t<identifier>\n\t\t<system value=\"urn:oid:0.1.2.3.4.5.6.7\"/>\n\t\t<value value=\"654321\"/>\n\t</identifier>\n\t<status value=\"active\"/>\n\t<type>\n\t\t<coding>\n\t\t\t<system value=\"http://terminology.hl7.org/CodeSystem/v3-ActCode\"/>\n\t\t\t<code value=\"PBILLACCT\"/>\n\t\t\t<display value=\"patient billing account\"/>\n\t\t</coding>\n\t\t<text value=\"patient\"/>\n\t</type>\n\t<name value=\"HACC Funded Billing for Peter James Chalmers\"/>\n\t<subject>\n\t\t<reference value=\"Patient/example\"/>\n\t\t<display value=\"Peter James Chalmers\"/>\n\t</subject>\n\t<servicePeriod>\n\t\t<start value=\"2016-01-01\"/>\n\t\t<end value=\"2016-06-30\"/>\n\t</servicePeriod>\n\t<coverage>\n\t\t<coverage>\n\t\t\t<reference value=\"Coverage/7546D\"/>\n\t\t</coverage>\n\t\t<priority value=\"1\"/>\n\t</coverage>\n\t<owner>\n\t\t<reference value=\"Organization/hl7\"/>\n\t</owner>\n\t<description value=\"Hospital charges\"/>\n</Account>",
"fileType": "XML"
},
"issues": []
}
]
}

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.cli.utils.SnomedVersion;
import java.util.*;
@ -9,44 +11,78 @@ import java.util.*;
*/
public class CliContext {
private String map = null;
private List<String> igs = new ArrayList<String>();
private List<String> questionnaires = new ArrayList<String>();
private String txServer = "http://tx.fhir.org";
@JsonProperty("doNative")
private boolean doNative = false;
@JsonProperty("anyExtensionsAllowed")
private boolean anyExtensionsAllowed = true;
@JsonProperty("hintAboutNonMustSupport")
private boolean hintAboutNonMustSupport = false;
@JsonProperty("recursive")
private boolean recursive = false;
private Locale locale = null;
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;
@JsonProperty("doDebug")
private boolean doDebug = false;
@JsonProperty("assumeValidRestReferences")
private boolean assumeValidRestReferences = false;
@JsonProperty("canDoNative")
private boolean canDoNative = 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 Locale locale = null;
@JsonProperty("locations")
private Map<String, String> locations = new HashMap<String, String>();
@JsonProperty("map")
public String getMap() {
return map;
}
@JsonProperty("map")
public CliContext setMap(String map) {
this.map = map;
return this;
}
@JsonProperty("igs")
public List<String> getIgs() {
return igs;
}
@JsonProperty("igs")
public CliContext setIgs(List<String> igs) {
this.igs = igs;
return this;
@ -60,10 +96,12 @@ public class CliContext {
return this;
}
@JsonProperty("questionnaires")
public List<String> getQuestionnaires() {
return questionnaires;
}
@JsonProperty("questionnaires")
public CliContext setQuestionnaires(List<String> questionnaires) {
this.questionnaires = questionnaires;
return this;
@ -77,64 +115,87 @@ public class CliContext {
return this;
}
@JsonProperty("txServer")
public String getTxServer() {
return txServer;
}
@JsonProperty("txServer")
public CliContext setTxServer(String txServer) {
this.txServer = txServer;
return this;
}
@JsonProperty("doNative")
public boolean isDoNative() {
return doNative;
}
@JsonProperty("doNative")
public CliContext setDoNative(boolean doNative) {
this.doNative = doNative;
return this;
}
@JsonProperty("anyExtensionsAllowed")
public boolean isAnyExtensionsAllowed() {
return anyExtensionsAllowed;
}
@JsonProperty("anyExtensionsAllowed")
public CliContext setAnyExtensionsAllowed(boolean anyExtensionsAllowed) {
this.anyExtensionsAllowed = anyExtensionsAllowed;
return this;
}
@JsonProperty("hintAboutNonMustSupport")
public boolean isHintAboutNonMustSupport() {
return hintAboutNonMustSupport;
}
@JsonProperty("hintAboutNonMustSupport")
public CliContext setHintAboutNonMustSupport(boolean hintAboutNonMustSupport) {
this.hintAboutNonMustSupport = hintAboutNonMustSupport;
return this;
}
@JsonProperty("recursive")
public boolean isRecursive() {
return recursive;
}
@JsonProperty("recursive")
public CliContext setRecursive(boolean recursive) {
this.recursive = recursive;
return this;
}
@JsonProperty("locale")
public String getLanguageCode() {
return this.locale.getDisplayLanguage();
}
public Locale getLocale() {
return locale;
}
@JsonProperty("locale")
public CliContext setLocale(String languageString) {
this.locale = new Locale(languageString);
return this;
}
public CliContext setLocale(Locale locale) {
this.locale = locale;
return this;
}
@JsonProperty("profiles")
public List<String> getProfiles() {
return profiles;
}
@JsonProperty("profiles")
public CliContext setProfiles(List<String> profiles) {
this.profiles = profiles;
return this;
@ -148,37 +209,45 @@ public class CliContext {
return this;
}
@JsonProperty("mode")
public Validator.EngineMode getMode() {
return mode;
}
@JsonProperty("mode")
public CliContext setMode(Validator.EngineMode mode) {
this.mode = mode;
return this;
}
@JsonProperty("output")
public String getOutput() {
return output;
}
@JsonProperty("output")
public CliContext setOutput(String output) {
this.output = output;
return this;
}
public Boolean getCanDoNative() {
@JsonProperty("canDoNative")
public boolean getCanDoNative() {
return canDoNative;
}
public CliContext setCanDoNative(Boolean canDoNative) {
@JsonProperty("canDoNative")
public CliContext setCanDoNative(boolean canDoNative) {
this.canDoNative = canDoNative;
return this;
}
@JsonProperty("sources")
public List<String> getSources() {
return sources;
}
@JsonProperty("sources")
public CliContext setSources(List<String> sources) {
this.sources = sources;
return this;
@ -192,10 +261,12 @@ public class CliContext {
return this;
}
@JsonProperty("locations")
public Map<String, String> getLocations() {
return locations;
}
@JsonProperty("locations")
public CliContext setLocations(Map<String, String> locations) {
this.locations = locations;
return this;
@ -206,84 +277,132 @@ public class CliContext {
return this;
}
@JsonProperty("sv")
public String getSv() {
return sv;
}
@JsonProperty("sv")
public CliContext setSv(String sv) {
this.sv = sv;
return this;
}
@JsonProperty("txLog")
public String getTxLog() {
return txLog;
}
@JsonProperty("txLog")
public CliContext setTxLog(String txLog) {
this.txLog = txLog;
return this;
}
@JsonProperty("mapLog")
public String getMapLog() {
return mapLog;
}
@JsonProperty("mapLog")
public CliContext setMapLog(String mapLog) {
this.mapLog = mapLog;
return this;
}
@JsonProperty("lang")
public String getLang() {
return lang;
}
@JsonProperty("lang")
public CliContext setLang(String lang) {
this.lang = lang;
return this;
}
@JsonProperty("fhirpath")
public String getFhirpath() {
return fhirpath;
}
@JsonProperty("fhirpath")
public CliContext setFhirpath(String fhirpath) {
this.fhirpath = fhirpath;
return this;
}
@JsonProperty("snomedCT")
public String getSnomedCT() {
return snomedCT;
}
@JsonProperty("snomedCT")
public CliContext setSnomedCT(String snomedCT) {
this.snomedCT = SnomedVersion.resolveSnomedCTCode(snomedCT);
return this;
}
@JsonProperty("targetVer")
public String getTargetVer() {
return targetVer;
}
@JsonProperty("targetVer")
public CliContext setTargetVer(String targetVer) {
this.targetVer = targetVer;
return this;
}
@JsonProperty("doDebug")
public boolean isDoDebug() {
return doDebug;
}
@JsonProperty("doDebug")
public CliContext setDoDebug(boolean doDebug) {
this.doDebug = doDebug;
return this;
}
@JsonProperty("assumeValidRestReferences")
public boolean isAssumeValidRestReferences() {
return assumeValidRestReferences;
}
@JsonProperty("assumeValidRestReferences")
public CliContext setAssumeValidRestReferences(boolean assumeValidRestReferences) {
this.assumeValidRestReferences = assumeValidRestReferences;
return this;
}
@Override
public String toString() {
return "CliContext{" +
"map='" + map + '\'' +
", igs=" + igs +
", questionnaires=" + questionnaires +
", txServer='" + txServer + '\'' +
", doNative=" + doNative +
", anyExtensionsAllowed=" + anyExtensionsAllowed +
", hintAboutNonMustSupport=" + hintAboutNonMustSupport +
", recursive=" + recursive +
", locale=" + locale +
", profiles=" + profiles +
", mode=" + mode +
", output='" + output + '\'' +
", canDoNative=" + canDoNative +
", sources=" + sources +
", locations=" + locations +
", sv='" + sv + '\'' +
", txLog='" + txLog + '\'' +
", mapLog='" + mapLog + '\'' +
", lang='" + lang + '\'' +
", fhirpath='" + fhirpath + '\'' +
", snomedCT='" + snomedCT + '\'' +
", targetVer='" + targetVer + '\'' +
", doDebug=" + doDebug +
", assumeValidRestReferences=" + assumeValidRestReferences +
'}';
}
}

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.get(fileType);
}
@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.ProfileComparer;
@ -14,6 +14,7 @@ import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.utils.Params;
import java.awt.*;
import java.io.File;
@ -21,7 +22,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.util.UUID;
public class Comparison {
public class ComparisonService {
public static void doLeftRightComparison(String[] args, String dest, ValidationEngine validator) throws IOException {
// ok now set up the comparison
@ -39,9 +40,9 @@ public class Comparison {
if (resLeft != null && resRight != null) {
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) {
Comparison.compareCapabilityStatements(args, dest, validator, left, right, (CanonicalResource) resLeft, (CanonicalResource) resRight);
ComparisonService.compareCapabilityStatements(args, dest, validator, left, right, (CanonicalResource) resLeft, (CanonicalResource) resRight);
} else
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, "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);
Desktop.getDesktop().browse(htmlFile.toURI());
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.elementmodel.Manager;
import org.hl7.fhir.r5.formats.IParser;
@ -9,6 +9,7 @@ import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.validation.ValidationEngine;
import org.hl7.fhir.validation.cli.model.*;
import java.io.File;
import java.io.FileOutputStream;
@ -16,13 +17,37 @@ import java.util.HashSet;
import java.util.List;
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 {
if (cliContext.getProfiles().size() > 0)
if (cliContext.getProfiles().size() > 0) {
System.out.println(" .. validate " + cliContext.getSources() + " against " + cliContext.getProfiles().toString());
else
} else {
System.out.println(" .. validate " + cliContext.getSources());
}
validator.prepare(); // generate any missing snapshots
Resource r = validator.validate(cliContext.getSources(), cliContext.getProfiles());
int ec = 0;

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.FhirPublication;
@ -75,11 +75,9 @@ public class Common {
}
}
public static ValidationEngine getValidationEngine(String[] args, String txLog) throws Exception {
String v = Common.getVersion(args);
String definitions = VersionUtilities.packageForVersion(v) + "#" + v;
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);
public static ValidationEngine getValidationEngine(String version, String definitions, String txLog) throws Exception {
System.out.println("Loading (v = " + version + ", tx server http://tx.fhir.org)");
return new ValidationEngine(definitions, "http://tx.fhir.org", txLog, FhirPublication.fromCode(version), version);
}
}

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.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.validation.Validator;
import org.hl7.fhir.validation.cli.model.CliContext;
import java.io.File;
import java.util.Arrays;
@ -9,6 +10,7 @@ import java.util.Locale;
public class Params {
public static final String GUI = "-gui";
public static final String VERSION = "-version";
public static final String OUTPUT = "-output";
public static final String PROXY = "-proxy";

View File

@ -1,4 +1,4 @@
package org.hl7.fhir.validation.cli;
package org.hl7.fhir.validation.cli.utils;
import java.util.Arrays;
import java.util.Optional;

View File

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="#fff" viewBox="0 180 612 612" focusable="false" role="img">
<title>Bootstrap</title>
<path d="M510 186c25.5 0 49.6 10 67.8 28.2S606 256.5 606 282v408c0 25.5-10 49.6-28.2 67.8S535.5 786 510 786H102c-25.5 0-49.6-10-67.8-28.2S6 715.5 6 690V282c0-25.5 10-49.6 28.2-67.8S76.5 186 102 186h408m0-6H102C45.9 180 0 225.9 0 282v408c0 56.1 45.9 102 102 102h408c56.1 0 102-45.9 102-102V282c0-56.1-45.9-102-102-102z"/>
<path d="M166.3 313h173.5c32 0 57.7 7.3 77 22s29 36.8 29 66.5c0 18-4.4 33.4-13.2 46.2-8.8 12.8-21.4 22.8-37.8 29.8v1c22 4.7 38.7 15.1 50 31.2 11.3 16.2 17 36.4 17 60.8 0 14-2.5 27.1-7.5 39.2-5 12.2-12.8 22.7-23.5 31.5s-24.3 15.8-41 21-36.5 7.8-59.5 7.8h-164V313zm62.5 149.5h102c15 0 27.5-4.2 37.5-12.8s15-20.8 15-36.8c0-18-4.5-30.7-13.5-38s-22-11-39-11h-102v98.6zm0 156.5h110.5c19 0 33.8-4.9 44.2-14.8 10.5-9.8 15.8-23.8 15.8-41.8 0-17.7-5.2-31.2-15.8-40.8s-25.2-14.2-44.2-14.2H228.8V619z"/>
</svg>

After

Width:  |  Height:  |  Size: 957 B

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,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,66 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>JS For Beginners</title>
<!-- link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css"-->
<!-- Compiled and minified JavaScript -->
<!--link rel="stylesheet" href="style.css"-->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
</head>
<body>
<header>
<h1>Validator 2.0</h1>
</header>
<div class="container">
<section id="main">
<div class="">
<ul>
<li class="line1">
<text></text>
<button class="delete_button"></button>
</li>
<li class="line1">
<text>Filename Here</text>
<svg class="delete_button" 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"/>
</svg>
</li>
</ul>
<button id="add_file_button" type="button" name="button">Add File</button>
</div>
<div>
<input type="file" style="display:none;" id="files" name="files[]" multiple />
<input type="button" id="loadFileXml" value="Add File Please" onclick="document.getElementById('files').click()" />
<ul id="list"></ul>
</div>
<template id="file_entry">
<li>
<details>
<summary>
<span id="file_name_field" class="summary-title">asdasdasd</span>
<div class="summary-chevron-up">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg>
</div>
</summary>
<div id="file_content_field" class="summary-content">asdasdasdas</div>
<div class="summary-chevron-down">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-up"><polyline points="18 15 12 9 6 15"></polyline></svg>
</details>
</li>
</template>
</section>
</div>
<footer id="main-footer">
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
</footer>
<script type="module" src="./main.js"></script>
</body>
</html>

View File

@ -0,0 +1,133 @@
import {CliContext} from './model/CliContext.js';
import {ValidationRequest} from './model/ValidationRequest.js';
import {FileInfo} from './model/FileInfo.js';
// Constants
const jsonIcon = './assets/json-svgrepo-com.svg';
const xmlIcon = './assets/xml-svgrepo-com.svg';
// 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('loaded_file_list').appendChild(generateNewFileListItemFromTemplate(file));
};
fr.readAsText(file);
}
// File List Template Generation
function generateNewFileListItemFromTemplate(file) {
var template = document.querySelector('#loaded_file_entry');
var clone = template.content.cloneNode(true);
// Add file name
clone.getElementById('file_name_field').textContent = file.name;
// Add appropriate icon for filetype
if (file.type.includes("json")) {
clone.getElementById('file_type_icon').src = jsonIcon;
} else if (file.type.includes("xml")) {
clone.getElementById('file_type_icon').src = xmlIcon;
}
// Add delete listener
clone.getElementById("delete_button").addEventListener("click", handleDeleteOnFileList);
return clone;
}
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.outcomes[0]);
console.log(validationResponse.outcomes[1]);
//console.log(validationResponse);
//Do something
}
// 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,154 @@
<!doctype html>
<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>
<ul id="loaded_file_list">
<template id="loaded_file_entry">
<li class="media text-muted pt-3">
<img id="file_type_icon" class="bd-placeholder-img mr-2 rounded" src="./assets/json-svgrepo-com.svg" width="32" height="32" preserveAspectRatio="xMidYMid slice">
<div class="media-body pb-3 mb-0 small lh-125 border-bottom border-gray">
<div class="d-flex justify-content-between align-items-center w-100">
<strong class="text-gray-dark" id="file_name_field"></strong>
<svg id="delete_button" class="delete_button" 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"/>
</svg>
</div>
<span class="d-block"></span>
</div>
</li>
</template>
</ul>
<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>
<div class="accordion" id="accordionExample">
<div class="card z-depth-0 bordered">
<div class="card-header" id="headingOne">
<h5 class="mb-0">
<button class="btn btn-link" type="button" data-toggle="collapse" data-target="#collapseOne"
aria-expanded="true" aria-controls="collapseOne">
Collapsible Group Item #1
</button>
</h5>
</div>
<div id="collapseOne" class="collapse show" aria-labelledby="headingOne"
data-parent="#accordionExample">
<div class="card-body">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3
wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum
eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla
assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred
nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer
farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus
labore sustainable.
</div>
</div>
</div>
<div class="card z-depth-0 bordered">
<div class="card-header" id="headingTwo">
<h5 class="mb-0">
<button class="btn btn-link collapsed" type="button" data-toggle="collapse"
data-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
Collapsible Group Item #2
</button>
</h5>
</div>
<div id="collapseTwo" class="collapse" aria-labelledby="headingTwo" data-parent="#accordionExample">
<div class="card-body">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3
wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum
eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla
assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred
nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer
farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus
labore sustainable.
</div>
</div>
</div>
<div class="card z-depth-0 bordered">
<div class="card-header" id="headingThree">
<h5 class="mb-0">
<button class="btn btn-link collapsed" type="button" data-toggle="collapse"
data-target="#collapseThree" aria-expanded="false" aria-controls="collapseThree">
Collapsible Group Item #3
</button>
</h5>
</div>
<div id="collapseThree" class="collapse" aria-labelledby="headingThree" data-parent="#accordionExample">
<div class="card-body">
Anim pariatur cliche reprehenderit, enim eiusmod high life accusamus terry richardson ad squid. 3
wolf moon officia aute, non cupidatat skateboard dolor brunch. Food truck quinoa nesciunt laborum
eiusmod. Brunch 3 wolf moon tempor, sunt aliqua put a bird on it squid single-origin coffee nulla
assumenda shoreditch et. Nihil anim keffiyeh helvetica, craft beer labore wes anderson cred
nesciunt sapiente ea proident. Ad vegan excepteur butcher vice lomo. Leggings occaecat craft beer
farm-to-table, raw denim aesthetic synth nesciunt you probably haven't heard of them accusamus
labore sustainable.
</div>
</div>
</div>
</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>