Merge pull request #7 from fhircat/ShExGenerator_R5_2023

ShEx Generator Updates for FHIR R5 Spec
This commit is contained in:
Deepak Sharma 2023-02-16 19:23:17 -06:00 committed by GitHub
commit d76f953e23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 1411 additions and 173 deletions

View File

@ -50,6 +50,13 @@
<optional>true</optional>
</dependency>
<!-- ShEx -->
<dependency>
<groupId>es.weso</groupId>
<artifactId>shexs_2.12</artifactId>
<version>0.2.31</version>
</dependency>
<!-- JSON Parsers -->
<dependency>
<groupId>com.google.code.gson</groupId>

View File

@ -0,0 +1,168 @@
package org.hl7.fhir.r5.test;
import org.hl7.fhir.r5.model.StructureDefinition;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ShexGeneratorTestUtils {
public class resDef {
public String name;
public String url;
public String info;
public resDef(String _name, String _url, String _info){
this.name = _name;
this.url = _url;
this.info = _info;
}
@Override
public String toString() {
return " " + name + "[ " + url + " ] ";
}
}
public enum RESOURCE_CATEGORY{
LOGICAL_NAMES, STRUCTURE_DEFINITIONS, EXTENSIONS, PROFILES, ALL
}
public List<resDef> getSDs(List<StructureDefinition> sds, RESOURCE_CATEGORY cat) {
List<resDef> selSDs = new ArrayList<resDef>();
sds.forEach((StructureDefinition sd) -> {
switch(cat) {
case STRUCTURE_DEFINITIONS:
if (sd.getType().trim().equals(sd.getName().trim()))
selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd)));
break;
case LOGICAL_NAMES:
if (sd.getBaseDefinition() == null)
selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd)));
break;
case EXTENSIONS:
if ("Extension".equals(sd.getType()))
selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd)));
break;
case PROFILES:
if (!((sd.getBaseDefinition() == null) ||
("Extension".equals(sd.getType())) ||
(sd.getType().trim().equals(sd.getName().trim()))))
selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd)));
break;
default:
selSDs.add(new resDef(sd.getName(), sd.getUrl(), getSDInfo(sd)));
}
});
Collections.sort(selSDs, new Comparator<resDef>() {
@Override
public int compare(resDef o1, resDef o2) {
return o1.name.compareTo(o2.name);
}
});
return selSDs;
}
public static List<String> getMetaStructureDefinitionsToSkip(){
List<String> skipSDs = new ArrayList<String>();
skipSDs.add("http://hl7.org/fhir/StructureDefinition/ActivityDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/AdministrableProductDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/ChargeItemDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/ClinicalUseDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/CodeSystem");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/ConceptMap");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/CompartmentDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/CompartmentDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/DeviceDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/ElementDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/EventDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/GraphDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/GuidanceResponse");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/Library");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/ManufacturedItemDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/Measure");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/MeasureReport");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/MedicinalProductDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/MessageDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/NamingSystem");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/ObservationDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/OperationDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/PackagedProductDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/ParameterDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/PlanDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/SearchParameter");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/SpecimenDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/StructureDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/SubstanceDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/Task");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/TerminologyCapabilities");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/TriggerDefinition");
skipSDs.add("http://hl7.org/fhir/StructureDefinition/ValueSet");
return skipSDs;
}
/**
* This method prepares selected extensions to test the policy in which ShEx Generator
* only translates selected resource extensions
* @return List<String> List of selected resource extensions for testing
*/
public static List<String> getSelectedExtensions(){
List<String> selectedExtesnsions = new ArrayList<String>();
selectedExtesnsions.add("http://fhir-registry.smarthealthit.org/StructureDefinition/capabilities");
selectedExtesnsions.add("http://fhir-registry.smarthealthit.org/StructureDefinition/oauth-uris");
return selectedExtesnsions;
}
public static String getSDInfo(StructureDefinition sd) {
if (sd != null) {
String kind = " ";
try {
kind = sd.getKind().name();
} catch (Exception e) {
System.out.println("Kind is null");
}
String name = " ";
try {
name = sd.getName();
} catch (Exception e) {
System.out.println("Name is null");
}
String type = " ";
try {
type = sd.getType();
} catch (Exception e) {
System.out.println("Type is null");
}
String derv = " ";
try {
derv = sd.getDerivation().name();
} catch (Exception e) {
System.out.println("Derivation is null");
}
String url = " ";
try {
url = sd.getUrl();
} catch (Exception e) {
System.out.println("URL is null");
}
String base = " ";
try {
base = sd.getBaseDefinition();
} catch (Exception e) {
System.out.println("Base is null");
}
return kind + "\t" + name + "\t" + type + "\t" + derv + "\t" + url + "\t" + base;
}
return "";
}
public static void printList(String title, List<ShexGeneratorTestUtils.resDef> items) {
System.out.println("************************************************************************");
System.out.println("Printing " + title);
System.out.println("************************************************************************");
items.forEach((resDef item) -> {
System.out.println(item.name + " [" + item.url + "]");
});
}
}

View File

@ -4,18 +4,37 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import es.weso.shex.Schema;
import es.weso.shex.validator.ShExsValidator;
import es.weso.shex.validator.ShExsValidatorBuilder;
import org.apache.commons.lang3.StringUtils;
import org.fhir.ucum.UcumException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.conformance.ShExGenerator;
import org.hl7.fhir.r5.conformance.ShExGenerator.HTMLLinkPolicy;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.test.utils.TestingUtilities;
import org.hl7.fhir.utilities.TextFile;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.hl7.fhir.r5.test.ShexGeneratorTestUtils.printList;
public class ShexGeneratorTests {
public static List<String> selectedExtesnsions = new ArrayList<String>();
public ShExGenerator shexGenerator;
@BeforeAll
public static void setup() {
}
private void doTest(String name) throws FileNotFoundException, IOException, FHIRException, UcumException {
StructureDefinition sd = TestingUtilities.getSharedWorkerContext().fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, null));
@ -24,8 +43,11 @@ public class ShexGeneratorTests {
}
Path outPath = FileSystems.getDefault().getPath(System.getProperty("java.io.tmpdir"), name.toLowerCase() + ".shex");
TextFile.stringToFile(new ShExGenerator(TestingUtilities.getSharedWorkerContext()).generate(HTMLLinkPolicy.NONE, sd), outPath.toString());
}
// For Testing Schema Processing and Constraint Mapping related Development
// If you un-comment the following lines, please comment all other lines in this method.
//this.doTestThis(name.toLowerCase(), name, false, ShExGenerator.ConstraintTranslationPolicy.ALL, false, true);
}
@Test
public void testId() throws FHIRException, IOException, UcumException {
doTest("id");
@ -75,4 +97,193 @@ public class ShexGeneratorTests {
public void testSignature() throws FHIRException, IOException, UcumException {
doTest("Signature");
}
@Test
public void testString() throws FHIRException, IOException, UcumException {
doTest("string");
}
private void doTestThis(String shortName, String name, boolean useSelectedExtensions, ShExGenerator.ConstraintTranslationPolicy policy, boolean debugMode, boolean validateShEx) {
IWorkerContext ctx = TestingUtilities.getSharedWorkerContext();
StructureDefinition sd = ctx.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(name, null));
if (sd == null) {
throw new FHIRException("StructuredDefinition for " + name + " was null");
}
//Path outPath = FileSystems.getDefault().getPath(System.getProperty("java.io.tmpdir"), name.toLowerCase() + ".shex");
Path outPath = FileSystems.getDefault().getPath(System.getProperty("user.home") + "/runtime_environments/ShExSchemas", shortName + ".shex");
try {
this.shexGenerator = new ShExGenerator(ctx);
this.shexGenerator.debugMode = debugMode;
this.shexGenerator.constraintPolicy = policy;
// ShEx Generator skips resources which are at Meta level of FHIR Resource definitions
this.shexGenerator.setExcludedStructureDefinitionUrls(
ShexGeneratorTestUtils.getMetaStructureDefinitionsToSkip());
// when ShEx translates only selected resource extensions
if (useSelectedExtensions) {
List<StructureDefinition> selExtns = new ArrayList<StructureDefinition>();
for (String eUrl : ShexGeneratorTestUtils.getSelectedExtensions()) {
StructureDefinition esd = ctx.fetchResource(StructureDefinition.class, ProfileUtilities.sdNs(eUrl, null));
if (esd != null)
selExtns.add(esd);
}
this.shexGenerator.setSelectedExtension(selExtns);
}
String schema = this.shexGenerator.generate(HTMLLinkPolicy.NONE, sd);
if (!schema.isEmpty()) {
if (validateShEx) {
try {
ShExsValidator validator = ShExsValidatorBuilder.fromStringSync(schema, "ShexC");
Schema sch = validator.schema();
Assert.assertNotNull(sch);
System.out.println("VALIDATION PASSED for ShEx Schema " + sd.getName());
} catch (Exception e) {
System.out.println("VALIDATION FAILED for ShEx Schema " + sd.getName());
//System.out.println("\t\t\tMessage: " + e.getMessage());
}
}
TextFile.stringToFile(schema, outPath.toString());
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Ignore
public void doTestAll() throws FileNotFoundException, IOException, FHIRException, UcumException {
List<StructureDefinition> sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
processSDList(
ShexGeneratorTestUtils.RESOURCE_CATEGORY.ALL, // Processing All kinds of Structure Definitions
sds, // List of Structure Definitions
false, //Process all extensions
ShExGenerator.ConstraintTranslationPolicy.ALL
// Process all types of constraints, do not skip
);
}
@Ignore
public void doTestGenericExtensionsOnlyPolicy() throws FileNotFoundException, IOException, FHIRException, UcumException {
List<StructureDefinition> sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
processSDList(
ShexGeneratorTestUtils.RESOURCE_CATEGORY.ALL, // Processing All kinds of Structure Definitions
sds, // List of Structure Definitions
false, //Process all extensions
ShExGenerator.ConstraintTranslationPolicy.GENERIC_ONLY
// Process generic constraints only, ignore constraints of type 'context of use'
);
}
@Ignore
public void doTestContextOfUseExtensionsOnlyPolicy() throws FileNotFoundException, IOException, FHIRException, UcumException {
List<StructureDefinition> sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
processSDList(
ShexGeneratorTestUtils.RESOURCE_CATEGORY.ALL, // Processing All kinds of Structure Definitions
sds, // List of Structure Definitions
false, //Process all extensions
ShExGenerator.ConstraintTranslationPolicy.CONTEXT_OF_USE_ONLY
// Process constraints only where context of use found, skip otherwise
);
}
@Ignore
public void doTestSelectedExtensions() throws FileNotFoundException, IOException, FHIRException, UcumException {
List<StructureDefinition> sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
processSDList(
ShexGeneratorTestUtils.RESOURCE_CATEGORY.ALL, // Processing All kinds of Structure Definitions
sds, // List of Structure Definitions
true, //Process only given/selected extensions, ignore other extensions
ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints
);
}
@Ignore
public void testStructureDefinitionsOnly() throws FileNotFoundException, IOException, FHIRException, UcumException {
List<StructureDefinition> sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
processSDList(
ShexGeneratorTestUtils.RESOURCE_CATEGORY.STRUCTURE_DEFINITIONS, // Processing All kinds of Structure Definitions
sds, // List of Structure Definitions
false, //Process only given/selected extensions, ignore other extensions
ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints
);
}
@Ignore
public void testExtensionsOnly() throws FileNotFoundException, IOException, FHIRException, UcumException {
List<StructureDefinition> sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
processSDList(
ShexGeneratorTestUtils.RESOURCE_CATEGORY.EXTENSIONS, // Processing All kinds of Structure Definitions
sds, // List of Structure Definitions
false, //Process only given/selected extensions, ignore other extensions
ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints
);
}
@Ignore
public void testLogicalNamesOnly() throws FileNotFoundException, IOException, FHIRException, UcumException {
List<StructureDefinition> sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
processSDList(
ShexGeneratorTestUtils.RESOURCE_CATEGORY.LOGICAL_NAMES, // Processing All kinds of Structure Definitions
sds, // List of Structure Definitions
false, //Process only given/selected extensions, ignore other extensions
ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints
);
}
@Ignore
public void testProfilesOnly() throws FileNotFoundException, IOException, FHIRException, UcumException {
List<StructureDefinition> sds = TestingUtilities.getSharedWorkerContext().fetchResourcesByType(StructureDefinition.class);
processSDList(
ShexGeneratorTestUtils.RESOURCE_CATEGORY.PROFILES, // Processing All kinds of Structure Definitions
sds, // List of Structure Definitions
false, //Process only given/selected extensions, ignore other extensions
ShExGenerator.ConstraintTranslationPolicy.ALL // Process all type of constraints
);
}
private void processSDList(ShexGeneratorTestUtils.RESOURCE_CATEGORY cat,
List<StructureDefinition> sds,
boolean useSelectedExtensions,
ShExGenerator.ConstraintTranslationPolicy policy) {
if ((sds == null) || (sds.isEmpty())) {
throw new FHIRException("No StructuredDefinition found!");
}
ShexGeneratorTestUtils shexTestUtils = new ShexGeneratorTestUtils();
List<ShexGeneratorTestUtils.resDef> sdDefs = shexTestUtils.getSDs(sds, cat);
printList(cat.toString(), sdDefs);
System.out.println("************************************************************************");
System.out.println("Processing " + cat);
System.out.println("************************************************************************");
sdDefs.forEach((ShexGeneratorTestUtils.resDef resDef) -> {
String name = resDef.url;
if (resDef.url.indexOf("/") != -1) {
String els[] = resDef.url.split("/");
name = els[els.length - 1];
}
System.out.println("******************** " + resDef + " *********************");
doTestThis(name, resDef.url, useSelectedExtensions, policy, true, true);
});
System.out.println("************************ END PROCESSING ******************************");
System.out.println("************************************************************************");
List<String> skipped = this.shexGenerator.getExcludedStructureDefinitionUrls();
System.out.println("Total Items processed: " + sds.size());
System.out.println("************************************************************************");
}
}