Merge pull request #1605 from hapifhir/2024-04-gg-tx-tests

2024 04 gg tx tests
This commit is contained in:
Grahame Grieve 2024-04-22 18:40:38 +10:00 committed by GitHub
commit 9bf309e9df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
45 changed files with 783 additions and 349 deletions

View File

@ -380,7 +380,7 @@ public class ICD11Generator {
cs.setPublisher("WHO");
cs.setCopyright("Consult WHO For terms of use");
cs.setCaseSensitive(true);
cs.setHierarchyMeaning(CodeSystemHierarchyMeaning.ISA); // though we aren't going to have a heirarchy
cs.setHierarchyMeaning(CodeSystemHierarchyMeaning.ISA); // though we aren't going to have a hierarchy
// cs.setCompositional(true);
// cs.setVersionNeeded(true);
cs.setValueSet("http://id.who.int/icd11/ValueSet/all-foundation");

View File

@ -92,6 +92,7 @@ public class VSACImporter extends OIDBasedValueSetImporter {
t = System.currentTimeMillis();
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("Unable to fetch OID " + oid + ": " + e.getMessage());
errs.put(oid, e.getMessage());
}
@ -126,7 +127,7 @@ public class VSACImporter extends OIDBasedValueSetImporter {
try {
Parameters p = new Parameters();
p.addParameter("url", new UriType(vs.getUrl()));
ValueSet vse = fhirToolingClient.expandValueset(vs, p);
ValueSet vse = fhirToolingClient.expandValueset(null, p);
vs.setExpansion(vse.getExpansion());
} catch (Exception e) {
errs.put(oid, "Expansion: " +e.getMessage());
@ -139,7 +140,7 @@ public class VSACImporter extends OIDBasedValueSetImporter {
p.addParameter("url", new UriType(vs.getUrl()));
t = System.currentTimeMillis();
try {
ValueSet vse = fhirToolingClient.expandValueset(vs, p);
ValueSet vse = fhirToolingClient.expandValueset(null, p);
vs.getExpansion().getContains().addAll(vse.getExpansion().getContains());
vs.getExpansion().setParameter(vse.getExpansion().getParameter());
} catch (Exception e2) {
@ -163,6 +164,9 @@ public class VSACImporter extends OIDBasedValueSetImporter {
} else {
vs.setTitle(vs.getName());
}
if (vs.getUrl().startsWith("https://")) {
System.out.println("URL is https: "+vs.getUrl());
}
vs.setName(makeValidName(vs.getName()));
JurisdictionUtilities.setJurisdictionCountry(vs.getJurisdiction(), "US");
new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path(dest, "ValueSet-" + oid + ".json")), vs);
@ -171,8 +175,8 @@ public class VSACImporter extends OIDBasedValueSetImporter {
}
private boolean isIncomplete(ValueSetExpansionComponent expansion) {
IntegerType c = expansion.getParameter("count").getValueIntegerType();
IntegerType offset = expansion.getParameter("offset").getValueIntegerType();
IntegerType c = expansion.getParameter("count") != null ? expansion.getParameter("count").getValueIntegerType() : new IntegerType(0);
IntegerType offset = expansion.getParameter("offset") != null ? expansion.getParameter("offset").getValueIntegerType() : new IntegerType(0);
return c.getValue() + offset.getValue() < expansion.getTotal();
}

View File

@ -107,7 +107,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
private List<ValueSetExpansionContainsComponent> roots = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
private IWorkerContext context;
private boolean canBeHeirarchy = true;
private boolean canBeHierarchy = true;
private Set<String> excludeKeys = new HashSet<String>();
private Set<String> excludeSystems = new HashSet<String>();
private ValueSetExpanderFactory factory;
@ -151,13 +151,13 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
String s = key(n);
if (map.containsKey(s) || excludeKeys.contains(s)) {
canBeHeirarchy = false;
canBeHierarchy = false;
} else {
codes.add(n);
map.put(s, n);
total++;
}
if (canBeHeirarchy && parent != null) {
if (canBeHierarchy && parent != null) {
parent.getContains().add(n);
} else {
roots.add(n);
@ -198,7 +198,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
ValueSetExpansionContainsComponent np = null;
boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);
boolean inc = CodeSystemUtilities.isInactive(cs, def);
if (canBeHeirarchy || !abs)
if (canBeHierarchy || !abs)
np = addCode(system, def.getCode(), def.getDisplay(), parent, def.getDesignation(), profile, abs, inc, filters);
for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, system, c, np, profile, filters);
@ -280,7 +280,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
if (source.hasCompose())
handleCompose(source.getCompose(), focus.getExpansion().getParameter(), profile);
if (canBeHeirarchy) {
if (canBeHierarchy) {
for (ValueSetExpansionContainsComponent c : roots) {
focus.getExpansion().getContains().add(c);
}
@ -288,7 +288,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
for (ValueSetExpansionContainsComponent c : codes) {
if (map.containsKey(key(c)) && !c.getAbstract()) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them
focus.getExpansion().getContains().add(c);
c.getContains().clear(); // make sure any heirarchy is wiped
c.getContains().clear(); // make sure any hierarchy is wiped
}
}
}
@ -321,12 +321,6 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
return res;
}
private void addToHeirarchy(List<ValueSetExpansionContainsComponent> target, List<ValueSetExpansionContainsComponent> source) {
for (ValueSetExpansionContainsComponent s : source) {
target.add(s);
}
}
private String getCodeDisplay(CodeSystem cs, String code) throws TerminologyServiceException {
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), code);
if (def == null)
@ -350,13 +344,13 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
// Exclude comes first because we build up a map of things to exclude
for (ConceptSetComponent inc : compose.getExclude())
excludeCodes(inc, params);
canBeHeirarchy = !profile.getExcludeNested() && excludeKeys.isEmpty() && excludeSystems.isEmpty();
canBeHierarchy = !profile.getExcludeNested() && excludeKeys.isEmpty() && excludeSystems.isEmpty();
boolean first = true;
for (ConceptSetComponent inc : compose.getInclude()) {
if (first == true)
first = false;
else
canBeHeirarchy = false;
canBeHierarchy = false;
includeCodes(inc, params, profile);
}
@ -381,7 +375,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p);
}
canBeHeirarchy = false; // if we're importing a value set, we have to be combining, so we won't try for a heirarchy
canBeHierarchy = false; // if we're importing a value set, we have to be combining, so we won't try for a hierarchy
return vso.getValueset();
}
@ -406,7 +400,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
} else {
CodeSystem cs = context.fetchCodeSystem(inc.getSystem());
if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(inc.getSystem())) {
addCodes(context.expandVS(inc, canBeHeirarchy), params, profile, imports);
addCodes(context.expandVS(inc, canBeHierarchy), params, profile, imports);
return;
}
@ -430,14 +424,14 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
}
if (!inc.getConcept().isEmpty()) {
canBeHeirarchy = false;
canBeHierarchy = false;
for (ConceptReferenceComponent c : inc.getConcept()) {
addCode(inc.getSystem(), c.getCode(), Utilities.noString(c.getDisplay()) ? getCodeDisplay(cs, c.getCode()) : c.getDisplay(), null, convertDesignations(c.getDesignation()), profile, false,
CodeSystemUtilities.isInactive(cs, c.getCode()), imports);
}
}
if (inc.getFilter().size() > 1) {
canBeHeirarchy = false; // which will bt the case if we get around to supporting this
canBeHierarchy = false; // which will bt the case if we get around to supporting this
throw new TerminologyServiceException("Multiple filters not handled yet"); // need to and them, and this isn't done yet. But this shouldn't arise in non loinc and snomed value sets
}
if (inc.getFilter().size() == 1) {
@ -457,7 +451,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
addCodeAndDescendents(cs, inc.getSystem(), c, null, profile, imports);
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
// gg; note: wtf is this: if the filter is display=v, look up the code 'v', and see if it's diplsay is 'v'?
canBeHeirarchy = false;
canBeHierarchy = false;
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def != null) {
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {

View File

@ -251,7 +251,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
org.hl7.fhir.dstu3.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false),
withVer(getPreferredResourceFormat(), "3.0"),
generateHeaders(),
"Update " + resource.fhirType() + "/" + resource.getId(),
@ -279,7 +279,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
ResourceRequest<T> result = null;
try {
result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false),
withVer(getPreferredResourceFormat(), "3.0"),
generateHeaders(),
"Update " + resource.fhirType() + "/" + id,
@ -316,7 +316,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
ResourceRequest<T> result;
URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps);
if (complex) {
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()));
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true);
if (client.getLogger() != null) {
client.getLogger().logRequest("POST", url.toString(), null, body);
}
@ -349,7 +349,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
recordUse();
Bundle transactionResult = null;
try {
transactionResult = client.postBatchRequest(resourceAddress.getBaseServiceUri(), ByteUtils.resourceToByteArray(batch, false, isJson(getPreferredResourceFormat())), withVer(getPreferredResourceFormat(), "3.0"), "transaction", timeoutOperation + (timeoutEntry * batch.getEntry().size()));
transactionResult = client.postBatchRequest(resourceAddress.getBaseServiceUri(), ByteUtils.resourceToByteArray(batch, false, isJson(getPreferredResourceFormat()), false), withVer(getPreferredResourceFormat(), "3.0"), "transaction", timeoutOperation + (timeoutEntry * batch.getEntry().size()));
} catch (Exception e) {
handleException("An error occurred trying to process this transaction request", e);
}
@ -362,7 +362,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
ResourceRequest<T> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveValidateUri(resourceClass, id),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false),
withVer(getPreferredResourceFormat(), "3.0"), generateHeaders(),
"POST " + resourceClass.getName() + (id != null ? "/" + id : "") + "/$validate", timeoutLong);
if (result.isUnsuccessfulRequest()) {
@ -437,7 +437,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
org.hl7.fhir.dstu3.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup"),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "3.0"),
generateHeaders(),
"CodeSystem/$lookup",
@ -456,7 +456,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
org.hl7.fhir.dstu3.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(ConceptMap.class, "transform"),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "3.0"),
generateHeaders(),
"ConceptMap/$transform",
@ -477,7 +477,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
org.hl7.fhir.dstu3.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand"),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "3.0"),
generateHeaders(),
"ValueSet/$expand?url=" + source.getUrl(),
@ -502,7 +502,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "3.0"),
generateHeaders(),
"Closure?name=" + name,
@ -524,7 +524,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
org.hl7.fhir.dstu3.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "3.0"),
generateHeaders(),
"UpdateClosure?name=" + name,

View File

@ -14,7 +14,7 @@ import org.hl7.fhir.dstu3.utils.client.EFhirClientException;
public class ByteUtils {
public static <T extends Resource> byte[] resourceToByteArray(T resource, boolean pretty, boolean isJson) {
public static <T extends Resource> byte[] resourceToByteArray(T resource, boolean pretty, boolean isJson, boolean noXhtml) {
ByteArrayOutputStream baos = null;
byte[] byteArray = null;
try {
@ -26,6 +26,9 @@ public class ByteUtils {
parser = new XmlParser();
}
parser.setOutputStyle(pretty ? IParser.OutputStyle.PRETTY : IParser.OutputStyle.NORMAL);
if (noXhtml) {
parser.setSuppressXhtml("Narrative removed");
}
parser.compose(baos, resource);
baos.close();
byteArray = baos.toByteArray();

View File

@ -299,7 +299,7 @@ public class CodeSystemUtilities {
}
// see http://hl7.org/fhir/R4/codesystem.html#hierachy
// returns additional parents not in the heirarchy
// returns additional parents not in the hierarchy
public static List<String> getOtherChildren(CodeSystem cs, ConceptDefinitionComponent c) {
List<String> res = new ArrayList<String>();
for (ConceptPropertyComponent p : c.getProperty()) {

View File

@ -106,7 +106,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
private List<ValueSetExpansionContainsComponent> roots = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
private IWorkerContext context;
private boolean canBeHeirarchy = true;
private boolean canBeHierarchy = true;
private Set<String> excludeKeys = new HashSet<String>();
private Set<String> excludeSystems = new HashSet<String>();
private ValueSet focus;
@ -152,13 +152,13 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
String s = key(n);
if (map.containsKey(s) || excludeKeys.contains(s)) {
canBeHeirarchy = false;
canBeHierarchy = false;
} else {
codes.add(n);
map.put(s, n);
total++;
}
if (canBeHeirarchy && parent != null) {
if (canBeHierarchy && parent != null) {
parent.getContains().add(n);
} else {
roots.add(n);
@ -227,7 +227,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
ValueSetExpansionContainsComponent np = null;
boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);
boolean inc = CodeSystemUtilities.isInactive(cs, def);
if (canBeHeirarchy || !abs)
if (canBeHierarchy || !abs)
np = addCode(system, def.getCode(), def.getDisplay(), parent, def.getDesignation(), expParams, abs, inc,
filters);
for (ConceptDefinitionComponent c : def.getConcept())
@ -343,7 +343,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
if (source.hasCompose())
handleCompose(source.getCompose(), focus.getExpansion().getParameter(), expParams, source.getUrl());
if (canBeHeirarchy) {
if (canBeHierarchy) {
for (ValueSetExpansionContainsComponent c : roots) {
focus.getExpansion().getContains().add(c);
}
@ -353,7 +353,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
// thought it might be heirarchical, but later we gave up, so
// now ignore them
focus.getExpansion().getContains().add(c);
c.getContains().clear(); // make sure any heirarchy is wiped
c.getContains().clear(); // make sure any hierarchy is wiped
}
}
}
@ -372,13 +372,6 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
return res;
}
private void addToHeirarchy(List<ValueSetExpansionContainsComponent> target,
List<ValueSetExpansionContainsComponent> source) {
for (ValueSetExpansionContainsComponent s : source) {
target.add(s);
}
}
private String getCodeDisplay(CodeSystem cs, String code) throws TerminologyServiceException {
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), code);
if (def == null)
@ -403,14 +396,14 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
// Exclude comes first because we build up a map of things to exclude
for (ConceptSetComponent inc : compose.getExclude())
excludeCodes(inc, params, ctxt);
canBeHeirarchy = !expParams.getParameterBool("excludeNested") && excludeKeys.isEmpty() && excludeSystems.isEmpty();
canBeHierarchy = !expParams.getParameterBool("excludeNested") && excludeKeys.isEmpty() && excludeSystems.isEmpty();
boolean first = true;
for (ConceptSetComponent inc : compose.getInclude()) {
if (first == true)
first = false;
else
canBeHeirarchy = false;
includeCodes(inc, params, expParams, canBeHeirarchy);
canBeHierarchy = false;
includeCodes(inc, params, expParams, canBeHierarchy);
}
}
@ -433,8 +426,8 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p);
}
canBeHeirarchy = false; // if we're importing a value set, we have to be combining, so we won't try for
// a heirarchy
canBeHierarchy = false; // if we're importing a value set, we have to be combining, so we won't try for
// a hierarchy
return vso.getValueset();
}
@ -518,7 +511,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
}
if (!inc.getConcept().isEmpty()) {
canBeHeirarchy = false;
canBeHierarchy = false;
for (ConceptReferenceComponent c : inc.getConcept()) {
c.checkNoModifiers("Code in Code System", "expanding");
addCode(inc.getSystem(), c.getCode(),
@ -528,7 +521,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
}
}
if (inc.getFilter().size() > 1) {
canBeHeirarchy = false; // which will bt the case if we get around to supporting this
canBeHierarchy = false; // which will bt the case if we get around to supporting this
throw new TerminologyServiceException("Multiple filters not handled yet"); // need to and them, and this isn't
// done yet. But this shouldn't arise
// in non loinc and snomed value sets
@ -562,7 +555,7 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
// gg; note: wtf is this: if the filter is display=v, look up the code 'v', and
// see if it's diplsay is 'v'?
canBeHeirarchy = false;
canBeHierarchy = false;
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def != null) {
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {

View File

@ -243,7 +243,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
try {
result = client.issuePutRequest(
resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false),
withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(), "Update " + resource.fhirType() + "/" + resource.getId(),
timeoutOperation);
if (result.isUnsuccessfulRequest()) {
@ -273,7 +273,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
ResourceRequest<T> result = null;
try {
result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false),
withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(), "Update " + resource.fhirType() + "/" + id,
timeoutOperation);
if (result.isUnsuccessfulRequest()) {
@ -311,7 +311,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
ResourceRequest<T> result;
URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps);
if (complex) {
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()));
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true);
result = client.issuePostRequest(url, body, withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(),
"POST " + resourceClass.getName() + "/$" + name, timeoutLong);
} else {
@ -336,7 +336,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
Bundle transactionResult = null;
try {
transactionResult = client.postBatchRequest(resourceAddress.getBaseServiceUri(),
ByteUtils.resourceToByteArray(batch, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(batch, false, isJson(getPreferredResourceFormat()), false),
withVer(getPreferredResourceFormat(), "4.0"), "transaction", timeoutOperation + (timeoutEntry * batch.getEntry().size()));
} catch (Exception e) {
handleException("An error occurred trying to process this transaction request", e);
@ -350,7 +350,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
ResourceRequest<T> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveValidateUri(resourceClass, id),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false),
withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(),
"POST " + resourceClass.getName() + (id != null ? "/" + id : "") + "/$validate", timeoutLong);
if (result.isUnsuccessfulRequest()) {
@ -424,7 +424,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup"),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(), "CodeSystem/$lookup", timeoutNormal);
} catch (IOException e) {
throw new FHIRException(e);
@ -441,7 +441,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(ConceptMap.class, "translate"),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(), "ConceptMap/$translate", timeoutNormal);
} catch (IOException e) {
throw new FHIRException(e);
@ -456,11 +456,13 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
public ValueSet expandValueset(ValueSet source, Parameters expParams) {
recordUse();
Parameters p = expParams == null ? new Parameters() : expParams.copy();
p.addParameter().setName("valueSet").setResource(source);
if (source != null) {
p.addParameter().setName("valueSet").setResource(source);
}
org.hl7.fhir.r4.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand"),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())), withVer(getPreferredResourceFormat(), "4.0"),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true), withVer(getPreferredResourceFormat(), "4.0"),
generateHeaders(), source == null ? "ValueSet/$expand" : "ValueSet/$expand?url=" + source.getUrl(),
timeoutExpand);
if (result.isUnsuccessfulRequest()) {
@ -491,7 +493,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
try {
result = client.issuePostRequest(
resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(), "Closure?name=" + name, timeoutNormal);
if (result.isUnsuccessfulRequest()) {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(),
@ -512,7 +514,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
try {
result = client.issuePostRequest(
resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(), "UpdateClosure?name=" + name, timeoutOperation);
if (result.isUnsuccessfulRequest()) {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(),

View File

@ -14,7 +14,7 @@ import org.hl7.fhir.r4.utils.client.EFhirClientException;
public class ByteUtils {
public static <T extends Resource> byte[] resourceToByteArray(T resource, boolean pretty, boolean isJson) {
public static <T extends Resource> byte[] resourceToByteArray(T resource, boolean pretty, boolean isJson, boolean noXhtml) {
ByteArrayOutputStream baos = null;
byte[] byteArray = null;
try {
@ -26,6 +26,9 @@ public class ByteUtils {
parser = new XmlParser();
}
parser.setOutputStyle(pretty ? IParser.OutputStyle.PRETTY : IParser.OutputStyle.NORMAL);
if (noXhtml) {
parser.setSuppressXhtml("Narrative removed");
}
parser.compose(baos, resource);
baos.close();
byteArray = baos.toByteArray();

View File

@ -420,7 +420,7 @@ public class CodeSystemUtilities {
}
// see http://hl7.org/fhir/R4/codesystem.html#hierachy
// returns additional parents not in the heirarchy
// returns additional parents not in the hierarchy
public static List<String> getOtherChildren(CodeSystem cs, ConceptDefinitionComponent c) {
List<String> res = new ArrayList<String>();
for (ConceptPropertyComponent p : c.getProperty()) {

View File

@ -183,7 +183,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
private List<ValueSetExpansionContainsComponent> roots = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
private IWorkerContext context;
private boolean canBeHeirarchy = true;
private boolean canBeHierarchy = true;
private boolean includeAbstract = true;
private Set<String> excludeKeys = new HashSet<String>();
private Set<String> excludeSystems = new HashSet<String>();
@ -238,13 +238,13 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
String s = key(n);
if (map.containsKey(s) || excludeKeys.contains(s)) {
canBeHeirarchy = false;
canBeHierarchy = false;
} else {
codes.add(n);
map.put(s, n);
total++;
}
if (canBeHeirarchy && parent != null) {
if (canBeHierarchy && parent != null) {
parent.getContains().add(n);
} else {
roots.add(n);
@ -446,7 +446,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
handleCompose(source.getCompose(), focus.getExpansion(), expParams, source.getUrl(),
focus.getExpansion().getExtension());
if (canBeHeirarchy) {
if (canBeHierarchy) {
for (ValueSetExpansionContainsComponent c : roots) {
focus.getExpansion().getContains().add(c);
}
@ -457,7 +457,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
// might be heirarchical, but later we
// gave up, so now ignore them
focus.getExpansion().getContains().add(c);
c.getContains().clear(); // make sure any heirarchy is wiped
c.getContains().clear(); // make sure any hierarchy is wiped
}
}
}
@ -493,15 +493,15 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
// Exclude comes first because we build up a map of things to exclude
for (ConceptSetComponent inc : compose.getExclude())
excludeCodes(inc, exp.getParameter(), ctxt);
canBeHeirarchy = !expParams.getParameterBool("excludeNested") && excludeKeys.isEmpty() && excludeSystems.isEmpty();
canBeHierarchy = !expParams.getParameterBool("excludeNested") && excludeKeys.isEmpty() && excludeSystems.isEmpty();
includeAbstract = !expParams.getParameterBool("excludeNotForUI");
boolean first = true;
for (ConceptSetComponent inc : compose.getInclude()) {
if (first == true)
first = false;
else
canBeHeirarchy = false;
includeCodes(inc, exp, expParams, canBeHeirarchy, extensions);
canBeHierarchy = false;
includeCodes(inc, exp, expParams, canBeHierarchy, extensions);
}
}
@ -540,8 +540,8 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
exp.getParameter().add(p);
}
copyExpansion(vso.getValueset().getExpansion().getContains());
canBeHeirarchy = false; // if we're importing a value set, we have to be combining, so we won't try for
// a heirarchy
canBeHierarchy = false; // if we're importing a value set, we have to be combining, so we won't try for
// a hierarchy
return vso.getValueset();
}
@ -681,7 +681,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
}
if (!inc.getConcept().isEmpty()) {
canBeHeirarchy = false;
canBeHierarchy = false;
for (ConceptReferenceComponent c : inc.getConcept()) {
c.checkNoModifiers("Code in Code System", "expanding");
ConceptDefinitionComponent def = CodeSystemUtilities.findCode(cs.getConcept(), c.getCode());
@ -705,7 +705,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
}
}
if (inc.getFilter().size() > 1) {
canBeHeirarchy = false; // which will bt the case if we get around to supporting this
canBeHierarchy = false; // which will bt the case if we get around to supporting this
throw failTSE("Multiple filters not handled yet"); // need to and them, and this isn't done yet. But this
// shouldn't arise in non loinc and snomed value sets
}
@ -745,7 +745,7 @@ public class ValueSetExpanderSimple extends ValueSetWorker implements ValueSetEx
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
// gg; note: wtf is this: if the filter is display=v, look up the code 'v', and
// see if it's diplsay is 'v'?
canBeHeirarchy = false;
canBeHierarchy = false;
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def != null) {
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {

View File

@ -237,7 +237,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient{
try {
result = client.issuePutRequest(
resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false),
getPreferredResourceFormat(), generateHeaders(), "Update " + resource.fhirType() + "/" + resource.getId(),
timeoutOperation);
if (result.isUnsuccessfulRequest()) {
@ -266,7 +266,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient{
ResourceRequest<T> result = null;
try {
result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false),
getPreferredResourceFormat(), generateHeaders(), "Update " + resource.fhirType() + "/" + id,
timeoutOperation);
if (result.isUnsuccessfulRequest()) {
@ -304,7 +304,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient{
ResourceRequest<T> result;
URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps);
if (complex) {
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()));
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true);
result = client.issuePostRequest(url, body, getPreferredResourceFormat(), generateHeaders(),
"POST " + resourceClass.getName() + "/$" + name, timeoutLong);
} else {
@ -333,7 +333,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient{
Bundle transactionResult = null;
try {
transactionResult = client.postBatchRequest(resourceAddress.getBaseServiceUri(),
ByteUtils.resourceToByteArray(batch, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(batch, false, isJson(getPreferredResourceFormat()), false),
getPreferredResourceFormat(), generateHeaders(), "transaction",
timeoutOperation + (timeoutEntry * batch.getEntry().size()));
} catch (Exception e) {
@ -347,7 +347,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient{
ResourceRequest<T> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveValidateUri(resourceClass, id),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false),
getPreferredResourceFormat(), generateHeaders(),
"POST " + resourceClass.getName() + (id != null ? "/" + id : "") + "/$validate", timeoutLong);
if (result.isUnsuccessfulRequest()) {
@ -405,7 +405,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient{
org.hl7.fhir.r4b.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand"),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true), getPreferredResourceFormat(),
generateHeaders(), "ValueSet/$expand?url=" + source.getUrl(), timeoutExpand);
if (result.isUnsuccessfulRequest()) {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(),
@ -442,7 +442,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient{
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand", params),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())), getPreferredResourceFormat(),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true), getPreferredResourceFormat(),
generateHeaders(), "ValueSet/$expand?url=" + source.getUrl(), timeoutExpand);
if (result.isUnsuccessfulRequest()) {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(),
@ -465,7 +465,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient{
try {
result = client.issuePostRequest(
resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true),
getPreferredResourceFormat(), generateHeaders(), "Closure?name=" + name, timeoutNormal);
if (result.isUnsuccessfulRequest()) {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(),
@ -485,7 +485,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient{
try {
result = client.issuePostRequest(
resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true),
getPreferredResourceFormat(), generateHeaders(), "UpdateClosure?name=" + name, timeoutOperation);
if (result.isUnsuccessfulRequest()) {
throw new EFhirClientException("Server returned error code " + result.getHttpStatus(),

View File

@ -15,7 +15,7 @@ import java.util.Map;
public class ByteUtils {
public static <T extends Resource> byte[] resourceToByteArray(T resource, boolean pretty, boolean isJson) {
public static <T extends Resource> byte[] resourceToByteArray(T resource, boolean pretty, boolean isJson, boolean noXhtml) {
ByteArrayOutputStream baos = null;
byte[] byteArray = null;
try {
@ -27,6 +27,9 @@ public class ByteUtils {
parser = new XmlParser();
}
parser.setOutputStyle(pretty ? IParser.OutputStyle.PRETTY : IParser.OutputStyle.NORMAL);
if (noXhtml) {
parser.setSuppressXhtml("Narrative removed");
}
parser.compose(baos, resource);
baos.close();
byteArray = baos.toByteArray();

View File

@ -96,7 +96,7 @@ class ClientTest {
@Test
@DisplayName("PUT request, test payload received by server matches sent.")
void test_put() throws IOException, URISyntaxException, InterruptedException {
byte[] payload = ByteUtils.resourceToByteArray(patient, true, false);
byte[] payload = ByteUtils.resourceToByteArray(patient, true, false, false);
// Mock server response of 200, with the same resource payload returned that we
// included in the PUT request
server.enqueue(new MockResponse().setResponseCode(200).setBody(new String(payload)));
@ -111,7 +111,7 @@ class ClientTest {
@Test
@DisplayName("POST request, test payload received by server matches sent.")
void test_post() throws IOException, URISyntaxException, InterruptedException {
byte[] payload = ByteUtils.resourceToByteArray(patient, true, false);
byte[] payload = ByteUtils.resourceToByteArray(patient, true, false, false);
// Mock server response of 200, with the same resource payload returned that we
// included in the PUT request
server.enqueue(new MockResponse().setResponseCode(200).setBody(new String(payload)));
@ -126,7 +126,7 @@ class ClientTest {
@Test
@DisplayName("Testing the logger works.")
void test_logger() throws IOException, URISyntaxException, InterruptedException {
byte[] payload = ByteUtils.resourceToByteArray(patient, true, false);
byte[] payload = ByteUtils.resourceToByteArray(patient, true, false, false);
server.enqueue(new MockResponse().setResponseCode(200).setBody(new String(payload)));
HTMLClientLogger mockLogger = Mockito.mock(HTMLClientLogger.class);
client.setLogger(mockLogger);

View File

@ -76,6 +76,7 @@ import org.hl7.fhir.r5.model.DomainResource;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.IdType;
import org.hl7.fhir.r5.model.Identifier;
import org.hl7.fhir.r5.model.IntegerType;
@ -157,9 +158,11 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
class OIDSource {
private String folder;
private Connection db;
protected OIDSource(String folder) {
private String pid;
protected OIDSource(String folder, String pid) {
super();
this.folder = folder;
this.pid = pid;
}
}
@ -261,7 +264,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
private UcumService ucumService;
protected Map<String, byte[]> binaries = new HashMap<String, byte[]>();
protected Map<String, Set<String>> oidCacheManual = new HashMap<>();
protected Map<String, Set<OIDDefinition>> oidCacheManual = new HashMap<>();
protected List<OIDSource> oidSources = new ArrayList<>();
protected Map<String, Map<String, ValidationResult>> validationCache = new HashMap<String, Map<String,ValidationResult>>();
@ -495,7 +498,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (!oidCacheManual.containsKey(s)) {
oidCacheManual.put(s, new HashSet<>());
}
oidCacheManual.get(s).add(url);
oidCacheManual.get(s).add(new OIDDefinition(r.fhirType(), s, url, ((CanonicalResource) r).getVersion(), null));
}
}
}
@ -1778,15 +1781,24 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
if (vs != null && !hasCanonicalResource(pin, "tx-resource", vs.getVUrl())) {
cache = checkAddToParams(tc, pin, vs) || cache;
addDependentResources(tc, pin, vs);
for (Extension ext : vs.getExtensionsByUrl(ToolingExtensions.EXT_VS_CS_SUPPL_NEEDED)) {
if (ext.hasValueCanonicalType()) {
String url = ext.getValueCanonicalType().asStringValue();
CodeSystem supp = fetchResource(CodeSystem.class, url);
if (supp != null) {
cache = checkAddToParams(tc, pin, supp) || cache;
}
}
}
}
}
CodeSystem cs = fetchResource(CodeSystem.class, inc.getSystem(), src);
if (cs != null && !hasCanonicalResource(pin, "tx-resource", cs.getVUrl()) && (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == CodeSystemContentMode.FRAGMENT)) {
cache = checkAddToParams(tc, pin, cs) || cache;
for (CodeSystem supp : fetchResourcesByType(CodeSystem.class)) {
if (supp.getContent() == CodeSystemContentMode.SUPPLEMENT && supp.getSupplements().equals(cs.getUrl())) {
cache = checkAddToParams(tc, pin, supp) || cache;
}
}
for (CodeSystem supp : fetchResourcesByType(CodeSystem.class)) {
if (supp.getContent() == CodeSystemContentMode.SUPPLEMENT && supp.getSupplements().equals(inc.getSystem())) {
cache = checkAddToParams(tc, pin, supp) || cache;
}
}
return cache;
@ -2522,7 +2534,8 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
String[] parts = uri.split("\\/");
if (!Utilities.noString(type) && parts.length == 1) {
if (allResourcesById.containsKey(type)) {
return allResourcesById.get(type).get(parts[0]).getResource();
ResourceProxy res = allResourcesById.get(type).get(parts[0]);
return res == null ? null : res.getResource();
} else {
return null;
}
@ -3101,62 +3114,66 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte
}
@Override
public Set<String> urlsForOid(boolean codeSystem, String oid) {
Set<String> set = urlsForOid(codeSystem, oid, true);
if (set.size() > 1) {
set = urlsForOid(codeSystem, oid, false);
public OIDSummary urlsForOid(String oid, String resourceType) {
OIDSummary set = urlsForOid(oid, resourceType, true);
if (set.getDefinitions().size() > 1) {
set = urlsForOid(oid, resourceType, false);
}
return set;
}
public Set<String> urlsForOid(boolean codeSystem, String oid, boolean retired) {
if (oid == null) {
return null;
}
Set<String> urls = new HashSet<>();
if (oidCacheManual.containsKey(oid)) {
urls.addAll(oidCacheManual.get(oid));
}
for (OIDSource os : oidSources) {
if (os.db == null) {
os.db = connectToOidSource(os.folder);
public OIDSummary urlsForOid(String oid, String resourceType, boolean retired) {
Set<OIDDefinition> urls = new HashSet<>();
if (oid != null) {
if (oidCacheManual.containsKey(oid)) {
urls.addAll(oidCacheManual.get(oid));
}
if (os.db != null) {
try {
PreparedStatement psql = os.db.prepareStatement("Select URL, Status from OIDMap where OID = ?");
psql.setString(1, oid);
ResultSet rs = psql.executeQuery();
while (rs.next()) {
if (retired || !"retired".equals(rs.getString(2))) {
urls.add(rs.getString(1));
}
}
} catch (Exception e) {
// nothing, there would alreagy have been an error
// e.printStackTrace();
for (OIDSource os : oidSources) {
if (os.db == null) {
os.db = connectToOidSource(os.folder);
}
if (os.db != null) {
try {
PreparedStatement psql = resourceType == null ?
os.db.prepareStatement("Select TYPE, URL, VERSION, Status from OIDMap where OID = ?") :
os.db.prepareStatement("Select TYPE, URL, VERSION, Status from OIDMap where TYPE = '"+resourceType+"' and OID = ?");
psql.setString(1, oid);
ResultSet rs = psql.executeQuery();
while (rs.next()) {
if (retired || !"retired".equals(rs.getString(4))) {
String rt = rs.getString(1);
String url = rs.getString(2);
String version = rs.getString(3);
urls.add(new OIDDefinition(rt, oid, url, version, os.pid));
}
}
} catch (Exception e) {
// nothing, there would alreagy have been an error
// e.printStackTrace();
}
}
}
switch (oid) {
case "2.16.840.1.113883.6.1" :
urls.add(new OIDDefinition("CodeSystem", "2.16.840.1.113883.6.1", "http://loinc.org", null, null));
break;
case "2.16.840.1.113883.6.8" :
urls.add(new OIDDefinition("CodeSystem", "2.16.840.1.113883.6.8", "http://unitsofmeasure.org", null, null));
break;
case "2.16.840.1.113883.6.96" :
urls.add(new OIDDefinition("CodeSystem", "2.16.840.1.113883.6.96", "http://snomed.info/sct", null, null));
break;
default:
}
}
switch (oid) {
case "2.16.840.1.113883.6.1" :
urls.add("http://loinc.org");
break;
case "2.16.840.1.113883.6.8" :
urls.add("http://unitsofmeasure.org");
break;
case "2.16.840.1.113883.6.96" :
urls.add("http://snomed.info/sct");
break;
default:
}
return urls;
return new OIDSummary(urls);
}
private Connection connectToOidSource(String folder) {
try {
File ff = ManagedFileAccess.file(folder);
File of = ManagedFileAccess.file(Utilities.path(ff.getAbsolutePath(), ".oid-map.db"));
File of = ManagedFileAccess.file(Utilities.path(ff.getAbsolutePath(), ".oid-map-2.db"));
if (!of.exists()) {
OidIndexBuilder oidBuilder = new OidIndexBuilder(ff, of);
oidBuilder.build();

View File

@ -4,6 +4,7 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
/*
Copyright (c) 2011+, HL7, Inc.
@ -45,6 +46,7 @@ import org.fhir.ucum.UcumService;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.TerminologyServiceException;
import org.hl7.fhir.r5.context.IWorkerContext.OIDDefinition;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.formats.IParser;
import org.hl7.fhir.r5.formats.ParserType;
@ -70,6 +72,7 @@ import org.hl7.fhir.r5.terminologies.utilities.CodingValidationRequest;
import org.hl7.fhir.r5.terminologies.utilities.ValidationResult;
import org.hl7.fhir.r5.utils.validation.IResourceValidator;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.FhirPublication;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
@ -102,6 +105,93 @@ import javax.annotation.Nonnull;
public interface IWorkerContext {
public class OIDDefinition {
private String type;
private String oid;
private String url;
private String version;
private String packageSrc;
protected OIDDefinition(String type, String oid, String url, String version, String packageSrc) {
super();
this.type = type;
this.oid = oid;
this.url = url;
this.version = version;
this.packageSrc = packageSrc;
}
public String getType() {
return type;
}
public String getOid() {
return oid;
}
public String getUrl() {
return url;
}
public String getVersion() {
return version;
}
public String getPackageSrc() {
return packageSrc;
}
public String summary() {
return url+(version == null ? "" : "|"+version)+(packageSrc != null ? "("+packageSrc+")" : "");
}
}
public class OIDSummary {
private Set<OIDDefinition> definitions;
private Set<String> urls = new HashSet<>();
protected OIDSummary(Set<OIDDefinition> definitions) {
super();
this.definitions = definitions;
for (OIDDefinition d : definitions) {
urls.add(d.getUrl());
}
}
public Set<OIDDefinition> getDefinitions() {
return definitions;
}
public String describe() {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (OIDDefinition d : definitions) {
b.append(d.summary());
}
return b.toString();
}
public String chooseBestUrl() {
for (OIDDefinition d : definitions) {
if (d.getPackageSrc() == null) {
return d.getUrl();
}
}
for (OIDDefinition d : definitions) {
if (d.getUrl().startsWith("http://hl7.org/fhir/")) {
return d.getUrl();
}
}
for (OIDDefinition d : definitions) {
if (!d.getUrl().contains("vsac")) {
return d.getUrl();
}
}
return null;
}
public int urlCount() {
return urls.size();
}
public String getUrl() {
return urls.iterator().next();
}
}
/**
* Get the version of the base definitions loaded in context
* This *does not* have to be 5.0 (R5) - the context can load other versions
@ -634,7 +724,13 @@ public interface IWorkerContext {
public boolean isForPublication();
public void setForPublication(boolean value);
public Set<String> urlsForOid(boolean codeSystem, String oid);
/**
*
* @param oid
* @param resourceType - null to search on all resource types
* @return
*/
public OIDSummary urlsForOid(String oid, String resourceType);
/**
* this first does a fetch resource, and if nothing is found, looks in the

View File

@ -6,6 +6,7 @@ import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.HashSet;
import java.util.Set;
@ -33,11 +34,13 @@ public class OidIndexBuilder {
Statement stmt = db.createStatement();
stmt.execute("CREATE TABLE OIDMap (\r\n"+
"OID nvarchar NOT NULL,\r\n"+
"TYPE nvarchar NOT NULL,\r\n"+
"URL nvarchar NOT NULL,\r\n"+
"VERSION nvarchar NOT NULL,\r\n"+
"Status nvarchar NOT NULL,\r\n"+
"PRIMARY KEY (OID, URL))\r\n");
PreparedStatement psql = db.prepareStatement("Insert into OIDMap (OID, URL, Status) values (?, ?, ?)");;
PreparedStatement psql = db.prepareStatement("Insert into OIDMap (OID, TYPE, URL, VERSION, Status) values (?, ?, ?, ?, ?)");
for (File f : folder.listFiles()) {
if (!f.getName().startsWith(".") && f.getName().endsWith(".json")) {
try {
@ -61,6 +64,7 @@ public class OidIndexBuilder {
Set<String> oids = new HashSet<String>();
String url = null;
String status = json.asString("status");
String version = json.asString("version");
if ("NamingSystem".equals(rt)) {
for (JsonObject id : json.getJsonObjects("uniqueId")) {
String t = id.asString("type");
@ -73,7 +77,7 @@ public class OidIndexBuilder {
}
if (url != null) {
for (String s : oids) {
addOid(psql, matches, s, url, status);
addOid(psql, matches, s, rt, url, version, status);
}
}
} else {
@ -97,7 +101,7 @@ public class OidIndexBuilder {
}
if (!oids.isEmpty()) {
for (String s : oids) {
addOid(psql, matches, s, url, status);
addOid(psql, matches, s, rt, url, version, status);
}
}
}
@ -105,13 +109,19 @@ public class OidIndexBuilder {
}
}
private void addOid(PreparedStatement psql, Set<String> matches, String oid, String url, String status) throws SQLException {
private void addOid(PreparedStatement psql, Set<String> matches, String oid, String type, String url, String version, String status) throws SQLException {
String key = oid+"@"+url;
if (!matches.contains(key)) {
matches.add(key);
psql.setString(1, oid);
psql.setString(2, url);
psql.setString(3, status);
psql.setString(2, type);
psql.setString(3, url);
if (version == null) {
psql.setNull(4, Types.NVARCHAR);
} else {
psql.setString(4, version);
}
psql.setString(5, status);
psql.execute();
}

View File

@ -509,7 +509,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
String of = pi.getFolders().get("package").getFolderPath();
if (of != null) {
oidSources.add(new OIDSource(of));
oidSources.add(new OIDSource(of, pi.vid()));
}
if ((types == null || types.size() == 0) && loader != null) {

View File

@ -23,6 +23,7 @@ import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StringType;
import org.hl7.fhir.r5.model.StructureDefinition;
import org.hl7.fhir.r5.terminologies.CodeSystemUtilities;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.AcceptLanguageHeader;
@ -507,5 +508,22 @@ public class LanguageUtils {
}
return null;
}
public static boolean switchLanguage(Element e, String lang) {
if (e.getProperty().isTranslatable()) {
String cnt = getTranslation(e, lang);
e.removeExtension(ToolingExtensions.EXT_TRANSLATION);
if (cnt != null) {
e.setValue(cnt);
}
}
if (e.hasChildren()) {
for (Element c : e.getChildren()) {
if (!switchLanguage(c, lang)) {
return false;
}
}
}
return true;
}
}

View File

@ -275,11 +275,11 @@ public class CodeSystemRenderer extends TerminologyRenderer {
private void makeHierarchyParam(XhtmlNode x, CodeSystem cs, Enumeration<CodeSystemHierarchyMeaning> hm) {
if (hm.hasValue()) {
String s = hm.getValue().getDisplay();
renderStatus(hm, x).tx(" "+/*!#*/"in a "+s+" heirarchy");
renderStatus(hm, x).tx(" "+/*!#*/"in a "+s+" hierarchy");
} else if (VersionComparisonAnnotation.hasDeleted(cs, "hierarchyMeaning")) {
makeHierarchyParam(x, null, (Enumeration<CodeSystemHierarchyMeaning>) VersionComparisonAnnotation.getDeleted(cs, "hierarchyMeaning").get(0));
} else if (CodeSystemUtilities.hasHierarchy(cs)) {
x.tx(" "+/*!#*/"in an undefined heirarchy");
x.tx(" "+/*!#*/"in an undefined hierarchy");
} else {
x.tx("");
}
@ -682,7 +682,7 @@ public class CodeSystemRenderer extends TerminologyRenderer {
}
}
for (ConceptDefinitionDesignationComponent cd : c.getDesignation()) {
if (cd.hasLanguage() && !langs.contains(cd.getLanguage()) && !c.getDefinition().equalsIgnoreCase(cd.getValue())) {
if (cd.hasLanguage() && (langs == null || !langs.contains(cd.getLanguage())) && (c.getDefinition() == null || !c.getDefinition().equalsIgnoreCase(cd.getValue()))) {
list.add(new Translateable(cd.getLanguage(), cd.getValueElement()));
}
}

View File

@ -338,7 +338,7 @@ public class PatientRenderer extends ResourceRenderer {
}
if (!anyComplex) {
XhtmlNode tr = tbl.tr();
nameCell(tr, sd.getTitle()+":", sd.getDescription(), sd.getWebPath());
nameCell(tr, getContext().getTranslated(sd.getTitleElement()), sd.getDescription(), sd.getWebPath());
XhtmlNode td = tr.td();
td.colspan("3");
if (list.size() == 1) {

View File

@ -1732,12 +1732,18 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
if (sd == null) {
p = gen.new Piece(null, iu, null).addStyle("font-weight:bold");
c.addPiece(p);
} else if (sd.hasWebPath()) {
p = gen.new Piece(sd.getWebPath(), sd.present(), null).addStyle("font-weight:bold");
c.addPiece(p);
} else {
p = gen.new Piece(iu, sd.present(), null).addStyle("font-weight:bold");
c.addPiece(p);
String v = "";
if (iu.contains("|") || hasMultipleVersions(context.getContext().fetchResourcesByUrl(StructureDefinition.class, iu))) {
v = " ("+sd.getVersion()+")";
}
if (sd.hasWebPath()) {
p = gen.new Piece(sd.getWebPath(), sd.present()+v, null).addStyle("font-weight:bold");
c.addPiece(p);
} else {
p = gen.new Piece(iu, sd.present()+v, null).addStyle("font-weight:bold");
c.addPiece(p);
}
}
if (bold) p.addStyle("font-weight:bold");
}
@ -1782,10 +1788,14 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
if (root) { // we'll use base instead of types then
StructureDefinition bsd = profile == null ? null : context.getWorker().fetchResource(StructureDefinition.class, profile.getBaseDefinition(), profile);
if (bsd != null) {
String v = "";
if (profile != null && (profile.getBaseDefinition().contains("|") || hasMultipleVersions(context.getWorker().fetchResourcesByUrl(StructureDefinition.class, profile.getBaseDefinition())))) {
v = v +"("+bsd.getVersion()+")";
}
if (bsd.hasWebPath()) {
c.getPieces().add(gen.new Piece(Utilities.isAbsoluteUrl(bsd.getWebPath()) ? bsd.getWebPath() : imagePath +bsd.getWebPath(), bsd.getName(), null));
c.getPieces().add(gen.new Piece(Utilities.isAbsoluteUrl(bsd.getWebPath()) ? bsd.getWebPath() : imagePath +bsd.getWebPath(), bsd.getName()+v, null));
} else {
c.getPieces().add(gen.new Piece(null, bsd.getName(), null));
c.getPieces().add(gen.new Piece(null, bsd.getName()+v, null));
}
}
return c;
@ -1819,9 +1829,15 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
tl = t;
if (t.hasTarget()) {
if (t.hasProfile()) {
StructureDefinition tsd = context.getContext().fetchResource(StructureDefinition.class, t.getProfile().get(0).asStringValue());
String ref = t.getProfile().get(0).asStringValue();
StructureDefinition tsd = context.getContext().fetchResource(StructureDefinition.class, ref);
if (tsd != null) {
c.getPieces().add(gen.new Piece(tsd.getWebPath(), tsd.getName(), tsd.present()));
// if there's multiple possible matches in scope, we will be explicit about the version
if (ref.contains("|") || hasMultipleVersions(context.getContext().fetchResourcesByUrl(StructureDefinition.class, ref))) {
c.getPieces().add(gen.new Piece(tsd.getWebPath(), tsd.getName()+"("+tsd.getVersion()+")", tsd.present()));
} else {
c.getPieces().add(gen.new Piece(tsd.getWebPath(), tsd.getName(), tsd.present()));
}
} else {
c.getPieces().add(gen.new Piece(corePath+"references.html", t.getWorkingCode(), null));
}
@ -1919,6 +1935,14 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
}
private boolean hasMultipleVersions(List<? extends CanonicalResource> list) {
Set<String> vl = new HashSet<>();
for (CanonicalResource cr : list) {
vl.add(cr.getVersion());
}
return vl.size() > 1;
}
private String pfx(String prefix, String url) {
return Utilities.isAbsoluteUrl(url) ? url : prefix + url;
}
@ -1936,7 +1960,11 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
} else if (Utilities.isAbsoluteUrl(u)) {
StructureDefinition sd = context.getWorker().fetchResource(StructureDefinition.class, u, src);
if (sd != null && context.getPkp() != null) {
String disp = sd.hasTitle() ? sd.getTitle() : sd.getName();
String v = "";
if (u.contains("|") || hasMultipleVersions(context.getWorker().fetchResourcesByUrl(StructureDefinition.class, u))) {
v = "("+sd.getVersion()+")";
}
String disp = sd.present()+v;
String ref = context.getPkp().getLinkForProfile(null, sd.getUrl());
if (ref != null && ref.contains("|"))
ref = ref.substring(0, ref.indexOf("|"));

View File

@ -1017,14 +1017,14 @@ public class ValueSetRenderer extends TerminologyRenderer {
}
private void renderExpansionRules(XhtmlNode x, ConceptSetComponent inc, int index, Map<String, ConceptDefinitionComponent> definitions) throws FHIRException, IOException {
String s = /*!#*/"This include specifies a heirarchy for when value sets are generated for use in a User Interface, but the rules are not properly defined";
String s = /*!#*/"This include specifies a hierarchy for when value sets are generated for use in a User Interface, but the rules are not properly defined";
if (inc.hasExtension(ToolingExtensions.EXT_EXPAND_RULES)) {
String rule = inc.getExtensionString(ToolingExtensions.EXT_EXPAND_RULES);
if (rule != null) {
switch (rule) {
case "all-codes": s = /*!#*/"This include specifies a heirarchy for when value sets are generated for use in a User Interface. The expansion contains all the codes, and also this structure:";
case "ungrouped": s = /*!#*/"This include specifies a heirarchy for when value sets are generated for use in a User Interface. The expansion contains this structure, and any codes not found in the structure:";
case "groups-only": s = /*!#*/"This include specifies a heirarchy for when value sets are generated for use in a User Interface. The expansion contains this structure:";
case "all-codes": s = /*!#*/"This include specifies a hierarchy for when value sets are generated for use in a User Interface. The expansion contains all the codes, and also this structure:";
case "ungrouped": s = /*!#*/"This include specifies a hierarchy for when value sets are generated for use in a User Interface. The expansion contains this structure, and any codes not found in the structure:";
case "groups-only": s = /*!#*/"This include specifies a hierarchy for when value sets are generated for use in a User Interface. The expansion contains this structure:";
}
}
}

View File

@ -607,7 +607,7 @@ public class CodeSystemUtilities extends TerminologyUtilities {
}
// see http://hl7.org/fhir/R4/codesystem.html#hierachy
// returns additional parents not in the heirarchy
// returns additional parents not in the hierarchy
public static List<String> getOtherChildren(CodeSystem cs, ConceptDefinitionComponent c) {
List<String> res = new ArrayList<String>();
for (ConceptPropertyComponent p : c.getProperty()) {

View File

@ -320,7 +320,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
String s = key(n);
if (wc.getMap().containsKey(s) || wc.getExcludeKeys().contains(s)) {
wc.setCanBeHeirarchy(false);
wc.setCanBeHierarchy(false);
} else {
wc.getCodes().add(n);
wc.getMap().put(s, n);
@ -335,7 +335,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
// throw failCostly(context.formatMessage(I18nConstants.VALUESET_TOO_COSTLY, focus.getUrl(), ">" + Integer.toString(maxExpansionSize)));
// }
}
if (wc.isCanBeHeirarchy() && parent != null) {
if (wc.isCanBeHierarchy() && parent != null) {
parent.getContains().add(n);
} else if (!wc.getRootMap().containsKey(s)) {
wc.getRootMap().put(s, n);
@ -765,7 +765,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
} else {
throw failCostly(context.formatMessage(I18nConstants.VALUESET_TOO_COSTLY_COUNT, focus.getVersionedUrl(), ">" + MessageFormat.format("{0,number,#}", maxExpansionSize), MessageFormat.format("{0,number,#}", dwc.getTotal())));
}
} else if (dwc.isCanBeHeirarchy() && ((dwc.getCountParam() == 0) || dwc.getCountParam() > dwc.getCodes().size())) {
} else if (dwc.isCanBeHierarchy() && ((dwc.getCountParam() == 0) || dwc.getCountParam() > dwc.getCodes().size())) {
for (ValueSetExpansionContainsComponent c : dwc.getRoots()) {
focus.getExpansion().getContains().add(c);
}
@ -773,7 +773,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
int i = 0;
int cc = 0;
for (ValueSetExpansionContainsComponent c : dwc.getCodes()) {
c.getContains().clear(); // make sure any heirarchy is wiped
c.getContains().clear(); // make sure any hierarchy is wiped
if (dwc.getMap().containsKey(key(c)) && (includeAbstract || !c.getAbstract())) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them
if (dwc.getOffsetParam() == 0 || i >= dwc.getOffsetParam()) {
focus.getExpansion().getContains().add(c);
@ -834,15 +834,15 @@ public class ValueSetExpander extends ValueSetProcessBase {
// Exclude comes first because we build up a map of things to exclude
for (ConceptSetComponent inc : compose.getExclude())
excludeCodes(dwc, inc, expParams, exp, valueSet);
dwc.setCanBeHeirarchy(!expParams.getParameterBool("excludeNested") && dwc.getExcludeKeys().isEmpty() && dwc.getExcludeSystems().isEmpty() && dwc.getOffsetParam() == 0);
dwc.setCanBeHierarchy(!expParams.getParameterBool("excludeNested") && dwc.getExcludeKeys().isEmpty() && dwc.getExcludeSystems().isEmpty() && dwc.getOffsetParam() == 0);
includeAbstract = !expParams.getParameterBool("excludeNotForUI");
boolean first = true;
for (ConceptSetComponent inc : compose.getInclude()) {
if (first == true)
first = false;
else
dwc.setCanBeHeirarchy(false);
includeCodes(inc, exp, expParams, dwc.isCanBeHeirarchy(), compose.hasInactive() ? !compose.getInactive() : checkNoInActiveFromParam(expParams), extensions, valueSet);
dwc.setCanBeHierarchy(false);
includeCodes(inc, exp, expParams, dwc.isCanBeHierarchy(), compose.hasInactive() ? !compose.getInactive() : checkNoInActiveFromParam(expParams), extensions, valueSet);
}
}
@ -910,7 +910,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
if (isValueSetUnionImports(valueSet)) {
copyExpansion(wc, evs.getContains());
}
wc.setCanBeHeirarchy(false); // if we're importing a value set, we have to be combining, so we won't try for a heirarchy
wc.setCanBeHierarchy(false); // if we're importing a value set, we have to be combining, so we won't try for a hierarchy
return vso.getValueset();
}
@ -1109,7 +1109,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
if (!inc.getConcept().isEmpty()) {
dwc.setCanBeHeirarchy(false);
dwc.setCanBeHierarchy(false);
for (ConceptReferenceComponent c : inc.getConcept()) {
c.checkNoModifiers("Code in Value Set", "expanding");
ConceptDefinitionComponent def = CodeSystemUtilities.findCodeOrAltCode(cs.getConcept(), c.getCode(), null);
@ -1136,7 +1136,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
}
if (inc.getFilter().size() > 0) {
if (inc.getFilter().size() > 1) {
dwc.setCanBeHeirarchy(false); // which will be the case if we get around to supporting this
dwc.setCanBeHierarchy(false); // which will be the case if we get around to supporting this
}
if (cs.getContent() == CodeSystemContentMode.FRAGMENT) {
addFragmentWarning(exp, cs);
@ -1203,7 +1203,7 @@ public class ValueSetExpander extends ValueSetProcessBase {
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
// gg; note: wtf is this: if the filter is display=v, look up the code 'v', and see if it's display is 'v'?
dwc.setCanBeHeirarchy(false);
dwc.setCanBeHierarchy(false);
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def != null) {
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {

View File

@ -18,7 +18,7 @@ class WorkingContext {
private Set<String> excludeKeys = new HashSet<String>();
private Set<String> excludeSystems = new HashSet<String>();
private boolean canBeHeirarchy = true;
private boolean canBeHierarchy = true;
private Integer offsetParam;
private Integer countParam; // allowed count. Because of internal processing, we allow more
private int total; // running count. This might be more than actually seen if we call out to an external server and only get the first 1000 codes
@ -48,12 +48,12 @@ class WorkingContext {
return excludeSystems;
}
public boolean isCanBeHeirarchy() {
return canBeHeirarchy;
public boolean isCanBeHierarchy() {
return canBeHierarchy;
}
public void setCanBeHeirarchy(boolean canBeHeirarchy) {
this.canBeHeirarchy = canBeHeirarchy;
public void setCanBeHierarchy(boolean canBeHierarchy) {
this.canBeHierarchy = canBeHierarchy;
}
public boolean hasOffsetParam() {

View File

@ -1082,87 +1082,8 @@ public class ValueSetValidator extends ValueSetProcessBase {
int i = 0;
for (ConceptSetComponent vsi : valueset.getCompose().getInclude()) {
opContext.deadCheck();
if (vsi.hasValueSet()) {
for (CanonicalType u : vsi.getValueSet()) {
if (!checkForCodeInValueSet(code, u.getValue(), sys, problems)) {
return false;
}
}
} else if (!vsi.hasSystem()) {
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_INCLUDE_WITH_NO_SYSTEM, code, valueset.getVersionedUrl(), i)));
return false;
}
if (vsi.hasSystem()) {
if (vsi.hasFilter()) {
ValueSet vsDummy = new ValueSet();
vsDummy.setUrl(Utilities.makeUuidUrn());
vsDummy.setStatus(PublicationStatus.ACTIVE);
vsDummy.getCompose().addInclude(vsi);
Coding c = new Coding().setCode(code).setSystem(vsi.getSystem());
ValidationResult vr = context.validateCode(options.withGuessSystem(false), c, vsDummy);
if (vr.isOk()) {
sys.add(vsi.getSystem());
} else {
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_INCLUDE_WITH_FILTER, code, valueset.getVersionedUrl(), i, vsi.getSystem())));
return false;
}
}
CodeSystemProvider csp = CodeSystemProvider.factory(vsi.getSystem());
if (csp != null) {
Boolean ok = csp.checkCode(code);
if (ok == null) {
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM_SYSTEM_IS_INDETERMINATE, code, valueset.getVersionedUrl(), vsi.getSystem())));
sys.add(vsi.getSystem());
} else if (ok) {
sys.add(vsi.getSystem());
}
} else {
CodeSystem cs = resolveCodeSystem(vsi.getSystem(), vsi.getVersion());
if (cs != null && cs.getContent() == CodeSystemContentMode.COMPLETE) {
if (vsi.hasConcept()) {
for (ConceptReferenceComponent cc : vsi.getConcept()) {
boolean match = cs.getCaseSensitive() ? cc.getCode().equals(code) : cc.getCode().equalsIgnoreCase(code);
if (match) {
sys.add(vsi.getSystem());
}
}
} else {
ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), code, cs.getCaseSensitive(), allAltCodes);
if (cc != null) {
sys.add(vsi.getSystem());
}
}
} else if (vsi.hasConcept()) {
for (ConceptReferenceComponent cc : vsi.getConcept()) {
boolean match = cc.getCode().equals(code);
if (match) {
sys.add(vsi.getSystem());
}
}
} else {
ValueSet vsDummy = new ValueSet();
vsDummy.setUrl(Utilities.makeUuidUrn());
vsDummy.setStatus(PublicationStatus.ACTIVE);
vsDummy.getCompose().addInclude(vsi);
ValidationResult vr = context.validateCode(options.withNoClient(), code, vsDummy);
if (vr.isOk()) {
sys.add(vsi.getSystem());
} else {
// ok, we'll try to expand this one then
ValueSetExpansionOutcome vse = context.expandVS(vsi, false, false);
if (vse.isOk()) {
if (!checkSystems(vse.getValueset().getExpansion().getContains(), code, sys, problems)) {
return false;
}
} else {
problems.add(new StringWithCode(OpIssueCode.NotFound, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_INCLUDE_WITH_UNKNOWN_SYSTEM, code, valueset.getVersionedUrl(), i, vsi.getSystem(), vse.getAllErrors().toString())));
return false;
}
}
}
}
if (scanForCodeInValueSetInclude(code, sys, problems, i, vsi)) {
return true;
}
i++;
}
@ -1175,6 +1096,100 @@ public class ValueSetValidator extends ValueSetProcessBase {
return true;
}
private boolean scanForCodeInValueSetInclude(String code, Set<String> sys, List<StringWithCode> problems, int i, ConceptSetComponent vsi) {
if (vsi.hasValueSet()) {
for (CanonicalType u : vsi.getValueSet()) {
if (!checkForCodeInValueSet(code, u.getValue(), sys, problems)) {
return false;
}
}
} else if (!vsi.hasSystem()) {
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_INCLUDE_WITH_NO_SYSTEM, code, valueset.getVersionedUrl(), i)));
return false;
}
if (vsi.hasSystem()) {
if (vsi.hasFilter()) {
ValueSet vsDummy = new ValueSet();
vsDummy.setUrl(Utilities.makeUuidUrn());
vsDummy.setStatus(PublicationStatus.ACTIVE);
vsDummy.getCompose().addInclude(vsi);
Coding c = new Coding().setCode(code).setSystem(vsi.getSystem());
ValidationResult vr = context.validateCode(options.withGuessSystem(false), c, vsDummy);
if (vr.isOk()) {
sys.add(vsi.getSystem());
} else {
// problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_INCLUDE_WITH_FILTER, code, valueset.getVersionedUrl(), i, vsi.getSystem(), filterSummary(vsi))));
return false;
}
}
CodeSystemProvider csp = CodeSystemProvider.factory(vsi.getSystem());
if (csp != null) {
Boolean ok = csp.checkCode(code);
if (ok == null) {
problems.add(new StringWithCode(OpIssueCode.InferFailed, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM_SYSTEM_IS_INDETERMINATE, code, valueset.getVersionedUrl(), vsi.getSystem())));
sys.add(vsi.getSystem());
} else if (ok) {
sys.add(vsi.getSystem());
}
} else {
CodeSystem cs = resolveCodeSystem(vsi.getSystem(), vsi.getVersion());
if (cs != null && cs.getContent() == CodeSystemContentMode.COMPLETE) {
if (vsi.hasConcept()) {
for (ConceptReferenceComponent cc : vsi.getConcept()) {
boolean match = cs.getCaseSensitive() ? cc.getCode().equals(code) : cc.getCode().equalsIgnoreCase(code);
if (match) {
sys.add(vsi.getSystem());
}
}
} else {
ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), code, cs.getCaseSensitive(), allAltCodes);
if (cc != null) {
sys.add(vsi.getSystem());
}
}
} else if (vsi.hasConcept()) {
for (ConceptReferenceComponent cc : vsi.getConcept()) {
boolean match = cc.getCode().equals(code);
if (match) {
sys.add(vsi.getSystem());
}
}
} else {
ValueSet vsDummy = new ValueSet();
vsDummy.setUrl(Utilities.makeUuidUrn());
vsDummy.setStatus(PublicationStatus.ACTIVE);
vsDummy.getCompose().addInclude(vsi);
ValidationResult vr = context.validateCode(options.withNoClient(), code, vsDummy);
if (vr.isOk()) {
sys.add(vsi.getSystem());
} else {
// ok, we'll try to expand this one then
ValueSetExpansionOutcome vse = context.expandVS(vsi, false, false);
if (vse.isOk()) {
if (!checkSystems(vse.getValueset().getExpansion().getContains(), code, sys, problems)) {
return false;
}
} else {
problems.add(new StringWithCode(OpIssueCode.NotFound, context.formatMessage(I18nConstants.UNABLE_TO_RESOLVE_SYSTEM__VALUE_SET_HAS_INCLUDE_WITH_UNKNOWN_SYSTEM, code, valueset.getVersionedUrl(), i, vsi.getSystem(), vse.getAllErrors().toString())));
return false;
}
}
}
}
}
return false;
}
private String filterSummary(ConceptSetComponent vsi) {
CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder();
for (ConceptSetFilterComponent f : vsi.getFilter()) {
b.append(f.getProperty()+f.getOp().toCode()+f.getValue());
}
return b.toString();
}
private boolean checkForCodeInValueSet(String code, String uri, Set<String> sys, List<StringWithCode> problems) {
ValueSetValidator vs = getVs(uri, null);
return vs.scanForCodeInValueSet(code, sys, problems);

View File

@ -218,7 +218,7 @@ public class ToolingExtensions {
public static final String EXT_Q_DISPLAY_CAT = "http://hl7.org/fhir/StructureDefinition/questionnaire-displayCategory";
public static final String EXT_REND_MD = "http://hl7.org/fhir/StructureDefinition/rendering-markdown";
public static final String EXT_CAP_STMT_EXPECT = "http://hl7.org/fhir/StructureDefinition/capabilitystatement-expectation";
public static final String EXT_ED_HEIRARCHY = "http://hl7.org/fhir/StructureDefinition/elementdefinition-heirarchy";
public static final String EXT_ED_HIERARCHY = "http://hl7.org/fhir/StructureDefinition/elementdefinition-hierarchy";
public static final String EXT_SD_IMPOSE_PROFILE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-imposeProfile";
public static final String EXT_SD_COMPLIES_WITH_PROFILE = "http://hl7.org/fhir/StructureDefinition/structuredefinition-compliesWithProfile";
public static final String EXT_DEF_TYPE = "http://hl7.org/fhir/StructureDefinition/elementdefinition-defaulttype";
@ -272,6 +272,7 @@ public class ToolingExtensions {
public static final String EXT_APPLICABLE_VERSION = "http://hl7.org/fhir/StructureDefinition/version-specific-use";
public static final String EXT_APPLICABLE_VERSION_VALUE = "http://hl7.org/fhir/StructureDefinition/version-specific-value";
public static final String EXT_IG_URL = "http://hl7.org/fhir/tools/StructureDefinition/implementationguide-resource-uri";
public static final String EXT_VS_CS_SUPPL_NEEDED = "http://hl7.org/fhir/StructureDefinition/valueset-supplement";
// specific extension helpers

View File

@ -291,7 +291,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
org.hl7.fhir.r5.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resource.getClass(), resource.getId()),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false),
withVer(getPreferredResourceFormat(), "4.0"),
generateHeaders(),
"Update " + resource.fhirType() + "/" + resource.getId(),
@ -319,7 +319,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
ResourceRequest<T> result = null;
try {
result = client.issuePutRequest(resourceAddress.resolveGetUriFromResourceClassAndId(resourceClass, id),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false),
withVer(getPreferredResourceFormat(), "4.0"),
generateHeaders(),
"Update " + resource.fhirType() + "/" + id,
@ -356,7 +356,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
ResourceRequest<T> result;
URI url = resourceAddress.resolveOperationURLFromClass(resourceClass, name, ps);
if (complex) {
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()));
byte[] body = ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true);
result = client.issuePostRequest(url, body, withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(),
"POST " + resourceClass.getName() + "/$" + name, timeoutLong);
} else {
@ -382,7 +382,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
recordUse();
Bundle transactionResult = null;
try {
transactionResult = client.postBatchRequest(resourceAddress.getBaseServiceUri(), ByteUtils.resourceToByteArray(batch, false, isJson(getPreferredResourceFormat())), withVer(getPreferredResourceFormat(), "4.0"),
transactionResult = client.postBatchRequest(resourceAddress.getBaseServiceUri(), ByteUtils.resourceToByteArray(batch, false, isJson(getPreferredResourceFormat()), false), withVer(getPreferredResourceFormat(), "4.0"),
generateHeaders(),
"transaction", timeoutOperation + (timeoutEntry * batch.getEntry().size()));
} catch (Exception e) {
@ -397,7 +397,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
ResourceRequest<T> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveValidateUri(resourceClass, id),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(resource, false, isJson(getPreferredResourceFormat()), false),
withVer(getPreferredResourceFormat(), "4.0"), generateHeaders(),
"POST " + resourceClass.getName() + (id != null ? "/" + id : "") + "/$validate", timeoutLong);
if (result.isUnsuccessfulRequest()) {
@ -456,7 +456,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
org.hl7.fhir.r5.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(ValueSet.class, "expand"),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "4.0"),
generateHeaders(),
"ValueSet/$expand?url=" + source.getUrl(),
@ -493,7 +493,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
org.hl7.fhir.r5.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(CodeSystem.class, "lookup"),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "4.0"),
generateHeaders(),
"CodeSystem/$lookup",
@ -512,7 +512,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
org.hl7.fhir.r5.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(ConceptMap.class, "translate"),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(p, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "4.0"),
generateHeaders(),
"ConceptMap/$translate",
@ -537,7 +537,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "4.0"),
generateHeaders(),
"Closure?name=" + name,
@ -559,7 +559,7 @@ public class FHIRToolingClient extends FHIRBaseToolingClient {
org.hl7.fhir.r5.utils.client.network.ResourceRequest<Resource> result = null;
try {
result = client.issuePostRequest(resourceAddress.resolveOperationUri(null, "closure", new HashMap<String, String>()),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat())),
ByteUtils.resourceToByteArray(params, false, isJson(getPreferredResourceFormat()), true),
withVer(getPreferredResourceFormat(), "4.0"),
generateHeaders(),
"UpdateClosure?name=" + name,

View File

@ -15,7 +15,7 @@ import java.util.Map;
public class ByteUtils {
public static <T extends Resource> byte[] resourceToByteArray(T resource, boolean pretty, boolean isJson) {
public static <T extends Resource> byte[] resourceToByteArray(T resource, boolean pretty, boolean isJson, boolean noXhtml) {
ByteArrayOutputStream baos = null;
byte[] byteArray = null;
try {
@ -27,6 +27,9 @@ public class ByteUtils {
parser = new XmlParser();
}
parser.setOutputStyle(pretty ? IParser.OutputStyle.PRETTY : IParser.OutputStyle.NORMAL);
if (noXhtml) {
parser.setSuppressXhtml("Narrative removed");
}
parser.compose(baos, resource);
baos.close();
byteArray = baos.toByteArray();

View File

@ -122,7 +122,7 @@ class ClientTest {
@Test
@DisplayName("PUT request, test payload received by server matches sent.")
void test_put() throws IOException, URISyntaxException, InterruptedException {
byte[] payload = ByteUtils.resourceToByteArray(patient, true, false);
byte[] payload = ByteUtils.resourceToByteArray(patient, true, false, false);
// Mock server response of 200, with the same resource payload returned that we included in the PUT request
server.enqueue(
new MockResponse()
@ -140,7 +140,7 @@ class ClientTest {
@Test
@DisplayName("POST request, test payload received by server matches sent.")
void test_post() throws IOException, URISyntaxException, InterruptedException {
byte[] payload = ByteUtils.resourceToByteArray(patient, true, false);
byte[] payload = ByteUtils.resourceToByteArray(patient, true, false, false);
// Mock server response of 200, with the same resource payload returned that we included in the PUT request
server.enqueue(
new MockResponse()
@ -158,7 +158,7 @@ class ClientTest {
@Test
@DisplayName("Testing the logger works.")
void test_logger() throws IOException, URISyntaxException, InterruptedException {
byte[] payload = ByteUtils.resourceToByteArray(patient, true, false);
byte[] payload = ByteUtils.resourceToByteArray(patient, true, false, false);
server.enqueue(
new MockResponse()
.setResponseCode(200)

View File

@ -1022,6 +1022,7 @@ public class I18nConstants {
public static final String XSI_TYPE_WRONG = "XSI_TYPE_WRONG";
public static final String XSI_TYPE_UNNECESSARY = "XSI_TYPE_UNNECESSARY";
public static final String TERMINOLOGY_TX_OID_MULTIPLE_MATCHES = "TERMINOLOGY_TX_OID_MULTIPLE_MATCHES";
public static final String TERMINOLOGY_TX_OID_MULTIPLE_MATCHES_CHOSEN = "TERMINOLOGY_TX_OID_MULTIPLE_MATCHES_CHOSEN";
public static final String CDA_UNKNOWN_TEMPLATE = "CDA_UNKNOWN_TEMPLATE";
public static final String CDA_UNKNOWN_TEMPLATE_EXT = "CDA_UNKNOWN_TEMPLATE_EXT";
public static final String UNABLE_TO_DETERMINE_TYPE_CONTEXT_INV = "UNABLE_TO_DETERMINE_TYPE_CONTEXT_INV";

View File

@ -0,0 +1,129 @@
package org.hl7.fhir.utilities.i18n;
import java.util.ArrayList;
import java.util.List;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.CommaSeparatedStringBuilder;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.subtag.LanguageSubtagRegistry;
public class LanguageTag {
private String code;
private String language;
private String script;
private String region;
private String variant;
private String extension;
private List<String> extLang;
private List<String> privateUse;
private LanguageSubtagRegistry registry;
public LanguageTag(LanguageSubtagRegistry registry, String code) {
this.registry = registry;
this.code = code;
if (!Utilities.noString(code)) {
String[] parts = code.split("\\-");
int c = 0;
int t = parts.length;
if (!registry.hasLanguage(parts[c])) {
throw new FHIRException("Invalid Language code '"+parts[c]+'"');
} else {
language = parts[c];
c++;
for (int i = 1; i <= 3; i++) {
if (c < t && registry.hasExtLanguage(parts[c])) {
if (extLang == null) {
extLang = new ArrayList<>();
}
extLang.add(parts[c]);
c++;
}
}
if (c < t && registry.hasScript(parts[c])) {
script = parts[c];
c++;
}
if (c < t && registry.hasRegion(parts[c])) {
region = parts[c];
c++;
}
if (c < t && registry.hasVariant(parts[c])) {
variant = parts[c];
c++;
}
while (c < t && parts[c].startsWith("x")) {
if (privateUse == null) {
privateUse = new ArrayList<>();
}
privateUse.add(parts[c]);
c++;
}
if (c < t) {
throw new FHIRException( "Unable to recognise part "+(c+1)+" ('"+parts[c]+"') as a valid language part");
}
}
}
}
public String getCode() {
return code;
}
public String getLanguage() {
return language;
}
public String getScript() {
return script;
}
public String getRegion() {
return region;
}
public String getVariant() {
return variant;
}
public String getExtension() {
return extension;
}
public List<String> getExtLang() {
return extLang;
}
public List<String> getPrivateUse() {
return privateUse;
}
public String present() {
StringBuilder b = new StringBuilder();
b.append(registry.getLanguage(language).getDisplay());
if (region != null) {
b.append("/");
b.append(registry.getRegion(region).getDisplay());
}
if (script != null || variant != null) {
CommaSeparatedStringBuilder cb = new CommaSeparatedStringBuilder();
if (script != null) {
cb.append("Script="+ registry.getScript(script).getDisplay());
if (variant != null) {
cb.append("Variant="+ registry.getVariant(variant).getDisplay());
}
b.append(" (");
b.append(cb.toString());
b.append(")");
}
}
return b.toString();
}
}

View File

@ -88,8 +88,29 @@ public class LanguageSubtagRegistry {
public boolean containsVariant(String key) {
return variants.containsKey(key);
}
public VariantSubtag getVariant(String key) {
return variants.get(key);
}
public boolean hasLanguage(String subTag) {
return languages.containsKey(subTag);
}
public boolean hasExtLanguage(String subTag) {
return extLangs.containsKey(subTag);
}
public boolean hasScript(String subTag) {
return scripts.containsKey(subTag);
}
public boolean hasRegion(String subTag) {
return regions.containsKey(subTag);
}
public boolean hasVariant(String subTag) {
return variants.containsKey(subTag);
}
}

View File

@ -42,4 +42,12 @@ public abstract class Subtag {
public List<String> getComments() {
return List.copyOf(comments);
}
public String getDisplay() {
if (descriptions.size() == 0) {
return "";
} else {
return descriptions.get(0);
}
}
}

View File

@ -145,5 +145,14 @@ public class JsonArray extends JsonElement implements Iterable<JsonElement> {
items.remove(e);
}
public boolean has(String key) {
for (JsonElement e : items) {
if (e.isJsonString() && key.equals(e.asString())) {
return true;
}
}
return false;
}
}

View File

@ -1478,5 +1478,9 @@ public class NpmPackage {
this.warned = warned;
}
public String vid() {
return id()+"#"+version();
}
}

View File

@ -28,16 +28,63 @@ import org.hl7.fhir.utilities.json.parser.JsonParser;
public class PackageHacker {
private static boolean useSecureReferences = false;
public static void main(String[] args) throws FileNotFoundException, IOException {
new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/us/vitals/2020Sep/package.tgz");
new PackageHacker().massEdit(new File("/Users/grahamegrieve/web/hl7.org/fhir"));
// new PackageHacker().edit("/Users/grahamegrieve/web/hl7.org/fhir/us/vitals/2020Sep/package.tgz");
}
private void massEdit(File dir) throws IOException {
System.out.println("process "+dir.getAbsolutePath());
for (File f : dir.listFiles()) {
if (f.isDirectory()) {
massEdit(f);
} else if (f.getName().equals("package.tgz")) {
try {
FileInputStream fs = ManagedFileAccess.inStream(f);
NpmPackage pck = NpmPackage.fromPackage(fs);
if ("fhir.core".equals(pck.getNpm().str("type"))) {
System.out.println("!!change "+f.getAbsolutePath());
pck.getNpm().remove("type");
pck.getNpm().set("type", "Core");
FileOutputStream fso = ManagedFileAccess.outStream(f);
try {
pck.save(fso);
} finally {
fso.close();
}
}
} catch (Exception e) {
System.out.println("!!Error: "+e.getMessage());
}
} else if (f.getName().startsWith("hl7.fhir.r") && f.getName().endsWith(".examples.tgz")) {
try {
FileInputStream fs = ManagedFileAccess.inStream(f);
NpmPackage pck = NpmPackage.fromPackage(fs);
if ("fhir.examples".equals(pck.getNpm().str("type"))) {
System.out.println("!!change "+f.getAbsolutePath());
pck.getNpm().remove("type");
pck.getNpm().set("type", "Examples");
FileOutputStream fso = ManagedFileAccess.outStream(f);
try {
pck.save(fso);
} finally {
fso.close();
}
}
} catch (Exception e) {
System.out.println("!!Error: "+e.getMessage());
}
}
}
}
private void edit(String name) throws FileNotFoundException, IOException {
File f = ManagedFileAccess.file(name);
if (!f.exists())
throw new Error("Unable to find "+f.getAbsolutePath());
NpmPackage pck = null;
FileInputStream fs = ManagedFileAccess.inStream(f);
try {
@ -47,7 +94,7 @@ public class PackageHacker {
}
System.out.println("Altering Package "+f.getAbsolutePath());
System.out.println(nice(pck.getNpm()));
change(pck.getNpm());
System.out.println("Revised Package");
@ -68,13 +115,13 @@ public class PackageHacker {
}
private void fixExampleContent(Map<String, byte[]> content) {
// byte[] cnt = content.get("ServiceRequest-SDOHCC-ServiceRequestCompletedFoodPantryApplicationAssistExample.json");
// content.put("ServiceRequest-SDOHCC-ServiceRequestCompletedFoodPantryApplicationAssist.json", cnt);
// content.remove("ServiceRequest-SDOHCC-ServiceRequestCompletedFoodPantryApplicationAssistExample.json");
// byte[] cnt = content.get("ServiceRequest-SDOHCC-ServiceRequestCompletedFoodPantryApplicationAssistExample.json");
// content.put("ServiceRequest-SDOHCC-ServiceRequestCompletedFoodPantryApplicationAssist.json", cnt);
// content.remove("ServiceRequest-SDOHCC-ServiceRequestCompletedFoodPantryApplicationAssistExample.json");
}
private void fixContent(Map<String, byte[]> content) {
// fixVersionInContent(content);
// fixVersionInContent(content);
}
@ -83,7 +130,7 @@ public class PackageHacker {
}
private void change(JsonObject npm) throws FileNotFoundException, IOException {
// fixVersions(npm, ver);
// fixVersions(npm, ver);
npm.remove("notForPublication");
npm.set("name", "hl7.fhir.us.vitals");
}
@ -98,7 +145,7 @@ public class PackageHacker {
}
}
}
}
private void fixVersions(JsonObject npm) {
@ -128,10 +175,10 @@ public class PackageHacker {
private void addContentFrom(String folder, Map<String, byte[]> content) throws FileNotFoundException, IOException {
for (File f : ManagedFileAccess.file(folder).listFiles()) {
if (f.getName().endsWith(".json") && !f.getName().endsWith(".canonical.json")) {
String cnt = TextFile.fileToString(f);
if (cnt.contains("\"resourceType\"")) {
content.put("package/"+f.getName(), TextFile.fileToBytes(f));
}
String cnt = TextFile.fileToString(f);
if (cnt.contains("\"resourceType\"")) {
content.put("package/"+f.getName(), TextFile.fileToBytes(f));
}
}
}
}

View File

@ -544,6 +544,7 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
private String invId;
private String comment;
private List<ValidationMessage> sliceInfo;
private int count;
/**
* Constructor
@ -661,8 +662,13 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
}
public String getMessage() {
return message;
return message+showCount();
}
private String showCount() {
return count == 0 ? "" : " (also in "+count+" other files)";
}
public ValidationMessage setMessage(String message) {
this.message = message;
return this;
@ -718,20 +724,20 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
}
public String summary() {
return level.toString()+" @ "+location+(line>= 0 && col >= 0 ? " (line "+Integer.toString(line)+", col"+Integer.toString(col)+"): " : ": ") +message +(server != null ? " (src = "+server+")" : "");
return level.toString()+" @ "+location+(line>= 0 && col >= 0 ? " (line "+Integer.toString(line)+", col"+Integer.toString(col)+"): " : ": ") +message+showCount() +(server != null ? " (src = "+server+")" : "");
}
public String toXML() {
return "<message source=\"" + source + "\" line=\"" + line + "\" col=\"" + col + "\" location=\"" + Utilities.escapeXml(location) + "\" type=\"" + type + "\" level=\"" + level + "\" display=\"" + Utilities.escapeXml(getDisplay()) + "\" ><plain>" + Utilities.escapeXml(message) + "</plain><html>" + html + "</html></message>";
return "<message source=\"" + source + "\" line=\"" + line + "\" col=\"" + col + "\" location=\"" + Utilities.escapeXml(location) + "\" type=\"" + type + "\" level=\"" + level + "\" display=\"" + Utilities.escapeXml(getDisplay()) + "\" ><plain>" + Utilities.escapeXml(message)+showCount() + "</plain><html>" + html + "</html></message>";
}
public String getHtml() {
return html == null ? Utilities.escapeXml(message) : html;
return (html == null ? Utilities.escapeXml(message) : html)+showCount();
}
public String getDisplay() {
return level + ": " + (location==null || location.isEmpty() ? "" : (location + ": ")) + message;
return level + ": " + (location==null || location.isEmpty() ? "" : (location + ": ")) + message+showCount();
}
/**
@ -745,7 +751,7 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
b.append("level", level);
b.append("type", type);
b.append("location", location);
b.append("message", message);
b.append("message", message+showCount());
return b.build();
}
@ -953,6 +959,10 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
public void setServer(String server) {
this.server = server;
}
public void incCount() {
count++;
}
}

View File

@ -489,7 +489,7 @@ Unable_to_handle_system__filter_with_property__ = Unable to handle system {0} fi
Unable_to_resolve_system__value_set_has_include_with_no_system = Unable to resolve system - value set {0} include #{1} has no system
UNABLE_TO_RESOLVE_SYSTEM_SYSTEM_IS_INDETERMINATE = The code system {1} referred to from value set {0} has a grammar, and the code might be valid in it
Unable_to_resolve_system__value_set_has_include_with_unknown_system = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}'': include #{2} has system {3} which could not be found, and the server returned error {4}
Unable_to_resolve_system__value_set_has_include_with_filter = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}'': include #{2} has a filter on system {3}
Unable_to_resolve_system__value_set_has_include_with_filter = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}'': include #{2} has a filter on system {3}: {4}
Unable_to_resolve_system__value_set_has_imports = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}'': value set has imports
Unable_to_resolve_system__value_set_has_multiple_matches = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}'': value set expansion has multiple matches: {2}
Unable_to_resolve_system__value_set_expansion_has_multiple_systems = The System URI could not be determined for the code ''{0}'' in the ValueSet ''{1}'': value set expansion has multiple systems
@ -1059,7 +1059,8 @@ TERMINOLOGY_TX_UNKNOWN_OID = The OID ''{0}'' is not known, so the code can't be
TERMINOLOGY_TX_SYSTEM_NO_CODE = A code with no system has no defined meaning, and it cannot be validated. A system should be provided
XSI_TYPE_WRONG = The xsi:type value ''{0}'' is wrong (should be ''{1}''). Note that xsi:type is unnecessary at this point
XSI_TYPE_UNNECESSARY = xsi:type is unnecessary at this point
TERMINOLOGY_TX_OID_MULTIPLE_MATCHES = The OID ''{0}'' matches multiple code systems ({1})
TERMINOLOGY_TX_OID_MULTIPLE_MATCHES = The OID ''{0}'' matches multiple resources ({1})
TERMINOLOGY_TX_OID_MULTIPLE_MATCHES_CHOSEN = The OID ''{0}'' matches multiple resources ({2}); {1} was chosen as the most appropriate
CDA_UNKNOWN_TEMPLATE = The CDA Template {0} is not known
CDA_UNKNOWN_TEMPLATE_EXT = The CDA Template {0} / {1} is not known
UNABLE_TO_DETERMINE_TYPE_CONTEXT_INV = The types could not be determined from the extension context, so the invariant can't be validated (types = {0})

View File

@ -70,6 +70,8 @@ import org.hl7.fhir.r5.conformance.profile.ProfileUtilities;
import org.hl7.fhir.r5.conformance.profile.ProfileUtilities.SourcedChildDefinitions;
import org.hl7.fhir.r5.context.ContextUtilities;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.context.IWorkerContext.OIDDefinition;
import org.hl7.fhir.r5.context.IWorkerContext.OIDSummary;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.elementmodel.Element.SpecialElement;
import org.hl7.fhir.r5.elementmodel.JsonParser;
@ -1777,17 +1779,21 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String oid = element.getNamedChildValue("codeSystem", false);
if (oid != null) {
Set<String> urls = context.urlsForOid(true, oid);
if (urls.size() != 1) {
c.setSystem("urn:oid:"+oid);
ok = false;
if (urls.size() == 0) {
warning(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_UNKNOWN_OID, oid);
} else {
rule(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_OID_MULTIPLE_MATCHES, oid, CommaSeparatedStringBuilder.join(",", urls));
}
c.setSystem("urn:oid:"+oid);
OIDSummary urls = context.urlsForOid(oid, "CodeSystem");
if (urls.urlCount() == 1) {
c.setSystem(urls.getUrl());
} else if (urls.urlCount() == 0) {
warning(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_UNKNOWN_OID, oid);
} else {
c.setSystem(urls.iterator().next());
String prefUrl = urls.chooseBestUrl();
if (prefUrl == null) {
rule(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_OID_MULTIPLE_MATCHES, oid, urls.describe());
ok = false;
} else {
c.setSystem(prefUrl);
warning(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_OID_MULTIPLE_MATCHES_CHOSEN, oid, prefUrl, urls.describe());
}
}
} else {
warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_SYSTEM_NO_CODE);
@ -6588,18 +6594,21 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
String code = element.getNamedChildValue(isPQ ? "unit" : "code", false);
String oid = isPQ ? "2.16.840.1.113883.6.8" : element.getNamedChildValue("codeSystem", false);
if (oid != null) {
Set<String> urls = context.urlsForOid(true, oid);
if (urls.size() != 1) {
system = "urn:oid:"+oid;
ok = false;
if (urls.size() == 0) {
warning(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_UNKNOWN_OID, oid);
} else {
rule(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_OID_MULTIPLE_MATCHES, oid, CommaSeparatedStringBuilder.join(",", urls));
}
OIDSummary urls = context.urlsForOid(oid, "CodeSystem");
system = "urn:oid:"+oid;
if (urls.urlCount() == 1) {
system = urls.getUrl();
} else if (urls.urlCount() == 0) {
warning(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_UNKNOWN_OID, oid);
} else {
system = urls.iterator().next();
String prefUrl = urls.chooseBestUrl();
if (prefUrl == null) {
rule(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_OID_MULTIPLE_MATCHES, oid, urls.describe());
ok = false;
} else {
system = prefUrl;
warning(errors, "2023-10-11", IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_OID_MULTIPLE_MATCHES_CHOSEN, oid, prefUrl, urls.describe());
}
}
} else {
warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, code == null, I18nConstants.TERMINOLOGY_TX_SYSTEM_NO_CODE);
@ -7208,19 +7217,22 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// todo: validate everything in this bundle.
}
if (rok) {
if (idstatus == IdStatus.REQUIRED && (element.getNamedChild(ID, false) == null)) {
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.RESOURCE_RES_ID_MISSING) && ok;
} else if (idstatus == IdStatus.PROHIBITED && (element.getNamedChild(ID, false) != null)) {
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.RESOURCE_RES_ID_PROHIBITED) && ok;
}
if (element.getNamedChild(ID, false) != null) {
Element eid = element.getNamedChild(ID, false);
if (eid.getProperty() != null && eid.getProperty().getDefinition() != null && eid.getProperty().getDefinition().getBase().getPath().equals("Resource.id")) {
NodeStack ns = stack.push(eid, -1, eid.getProperty().getDefinition(), null);
if (eid.primitiveValue() != null && eid.primitiveValue().length() > 64) {
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, eid.line(), eid.col(), ns.getLiteralPath(), false, I18nConstants.RESOURCE_RES_ID_MALFORMED_LENGTH, eid.primitiveValue().length()) && ok;
} else {
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, eid.line(), eid.col(), ns.getLiteralPath(), FormatUtilities.isValidId(eid.primitiveValue()), I18nConstants.RESOURCE_RES_ID_MALFORMED_CHARS, eid.primitiveValue()) && ok;
// todo: not clear what we should do with regard to logical models - should they have ids? Should we check anything?
if (element.getProperty().getStructure().getKind() != StructureDefinitionKind.LOGICAL) {
if (idstatus == IdStatus.REQUIRED && (element.getNamedChild(ID, false) == null)) {
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.RESOURCE_RES_ID_MISSING) && ok;
} else if (idstatus == IdStatus.PROHIBITED && (element.getNamedChild(ID, false) != null)) {
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, element.line(), element.col(), stack.getLiteralPath(), false, I18nConstants.RESOURCE_RES_ID_PROHIBITED) && ok;
}
if (element.getNamedChild(ID, false) != null) {
Element eid = element.getNamedChild(ID, false);
if (eid.getProperty() != null && eid.getProperty().getDefinition() != null && eid.getProperty().getDefinition().getBase().getPath().equals("Resource.id")) {
NodeStack ns = stack.push(eid, -1, eid.getProperty().getDefinition(), null);
if (eid.primitiveValue() != null && eid.primitiveValue().length() > 64) {
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, eid.line(), eid.col(), ns.getLiteralPath(), false, I18nConstants.RESOURCE_RES_ID_MALFORMED_LENGTH, eid.primitiveValue().length()) && ok;
} else {
ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, eid.line(), eid.col(), ns.getLiteralPath(), FormatUtilities.isValidId(eid.primitiveValue()), I18nConstants.RESOURCE_RES_ID_MALFORMED_CHARS, eid.primitiveValue()) && ok;
}
}
}
}
@ -7499,7 +7511,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
// first case: the type value set is wrong for primitive special types
for (OperationOutcomeIssueComponent iss : vr.getIssues()) {
if (iss.hasDetails() && iss.getDetails().getText().startsWith("Unable to resolve system - value set expansion has no matches for code 'http://hl7.org/fhirpath/System")) {
if (iss.hasDetails() && iss.getDetails().hasText() && iss.getDetails().getText().startsWith("Unable to resolve system - value set expansion has no matches for code 'http://hl7.org/fhirpath/System")) {
return new ValidationResult("http://hl7.org/fhirpath/System", null, null, null);
}
}

View File

@ -552,7 +552,7 @@ public class CodeSystemValidator extends BaseValidator {
hint(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, s.getLiteralPath(), false, I18nConstants.CODESYSTEM_CS_NONHL7_MISSING_ELEMENT, "caseSensitive");
}
}
if (Utilities.noString(hierarchyMeaning) && hasHeirarchy(cs)) {
if (Utilities.noString(hierarchyMeaning) && hasHierarchy(cs)) {
NodeStack s = stack;
Element c = cs.getNamedChild("hierarchyMeaning", false);
if (c != null) {
@ -604,7 +604,7 @@ public class CodeSystemValidator extends BaseValidator {
}
private boolean hasHeirarchy(Element cs) {
private boolean hasHierarchy(Element cs) {
for (Element c : cs.getChildren("concept")) {
if (c.hasChildren("concept")) {
return true;

View File

@ -212,9 +212,9 @@ public class TxTester {
} else if (test.asString("operation").equals("cs-validate-code")) {
msg = validateCS(test.str("name"),tx, setup, req, resp, fp, lang, profile, ext);
} else if (test.asString("operation").equals("lookup")) {
msg = null; // lookup(test.str("name"),tx, setup, req, resp, fp, lang, profile, ext);
msg = lookup(test.str("name"),tx, setup, req, resp, fp, lang, profile, ext);
} else if (test.asString("operation").equals("translate")) {
msg = null; // translate(test.str("name"),tx, setup, req, resp, fp, lang, profile, ext);
msg = translate(test.str("name"),tx, setup, req, resp, fp, lang, profile, ext);
} else {
throw new Exception("Unknown Operation "+test.asString("operation"));
}

View File

@ -114,6 +114,8 @@ private static TxTestData testData;
} else if (setup.getTest().asString("operation").equals("cs-validate-code")) {
String diff = TxServiceTestHelper.getDiffForValidation(setup.getTest().str("name"), engine.getContext(), setup.getTest().asString("name"), req, resp, setup.getTest().asString("Content-Language"), fp, ext, true);
assertNull(diff, diff);
} else if (Utilities.existsInList(setup.getTest().asString("operation"), "lookup", "translate")) {
Assertions.assertTrue(true); // we don't test these for the internal server
} else {
Assertions.fail("Unknown Operation "+ setup.getTest().asString("operation"));
}

View File

@ -21,7 +21,7 @@
<commons_compress_version>1.26.0</commons_compress_version>
<guava_version>32.0.1-jre</guava_version>
<hapi_fhir_version>6.4.1</hapi_fhir_version>
<validator_test_case_version>1.5.5</validator_test_case_version>
<validator_test_case_version>1.5.6-SNAPSHOT</validator_test_case_version>
<jackson_version>2.17.0</jackson_version>
<junit_jupiter_version>5.9.2</junit_jupiter_version>
<junit_platform_launcher_version>1.8.2</junit_platform_launcher_version>