Merge pull request #844 from hapifhir/gg-202206-amsterdam

Gg 202206 amsterdam
This commit is contained in:
Grahame Grieve 2022-06-24 13:53:33 +02:00 committed by GitHub
commit 95dee12d0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 597 additions and 59 deletions

View File

@ -133,7 +133,7 @@ class FHIRToolingClientTest {
private void checkHeaders(Headers argumentCaptorValue) {
getHeaders().forEach(header -> {
System.out.println("Checking header <" + header.component1().utf8() + ", " + header.component2().utf8() + ">");
// System.out.println("Checking header <" + header.component1().utf8() + ", " + header.component2().utf8() + ">");
Assertions.assertEquals(argumentCaptorValue.get(header.component1().utf8()), header.component2().utf8());
});
}

View File

@ -9,6 +9,8 @@ import java.util.Map;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.comparison.ProfileComparer.ProfileComparison;
import org.hl7.fhir.r5.comparison.ResourceComparer.MessageCounts;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.model.BackboneElement;
@ -86,7 +88,7 @@ public class CapabilityStatementComparer extends CanonicalResourceComparer {
super(session);
}
public CapabilityStatementComparison compare(CapabilityStatement left, CapabilityStatement right) {
public CapabilityStatementComparison compare(CapabilityStatement left, CapabilityStatement right) throws DefinitionException, FHIRFormatError, IOException {
if (left == null)
throw new DefinitionException("No CapabilityStatement provided (left)");
if (right == null)
@ -130,7 +132,7 @@ public class CapabilityStatementComparer extends CanonicalResourceComparer {
return res;
}
private void compareRests(List<CapabilityStatementRestComponent> left, List<CapabilityStatementRestComponent> right, StructuralMatch<Element> combined, List<CapabilityStatementRestComponent> union, List<CapabilityStatementRestComponent> intersection, CapabilityStatement csU, CapabilityStatement csI, CapabilityStatementComparison res, String path) {
private void compareRests(List<CapabilityStatementRestComponent> left, List<CapabilityStatementRestComponent> right, StructuralMatch<Element> combined, List<CapabilityStatementRestComponent> union, List<CapabilityStatementRestComponent> intersection, CapabilityStatement csU, CapabilityStatement csI, CapabilityStatementComparison res, String path) throws DefinitionException, FHIRFormatError, IOException {
List<CapabilityStatementRestComponent> matchR = new ArrayList<>();
for (CapabilityStatementRestComponent l : left) {
CapabilityStatementRestComponent r = findInList(right, l);
@ -203,10 +205,12 @@ public class CapabilityStatementComparer extends CanonicalResourceComparer {
combined.getChildren().add(sm);
}
}
for (CodeableConcept r : right.getService()) {
if (!matchR.contains(r)) {
union.getService().add(r);
combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r));
if (right != null) {
for (CodeableConcept r : right.getService()) {
if (!matchR.contains(r)) {
union.getService().add(r);
combined.getChildren().add(new StructuralMatch<Element>(vmI(IssueSeverity.INFORMATION, "Added this concept", path), r));
}
}
}
}
@ -384,7 +388,7 @@ public class CapabilityStatementComparer extends CanonicalResourceComparer {
tgt.removeAll(toRemove);
}
private void compareRestResources(CapabilityStatementRestComponent left, CapabilityStatementRestComponent right, StructuralMatch<Element> combined, CapabilityStatementRestComponent union, CapabilityStatementRestComponent intersection, CapabilityStatement csU, CapabilityStatement csI, CapabilityStatementComparison res, String path) {
private void compareRestResources(CapabilityStatementRestComponent left, CapabilityStatementRestComponent right, StructuralMatch<Element> combined, CapabilityStatementRestComponent union, CapabilityStatementRestComponent intersection, CapabilityStatement csU, CapabilityStatement csI, CapabilityStatementComparison res, String path) throws DefinitionException, FHIRFormatError, IOException {
List<CapabilityStatementRestResourceComponent> matchR = new ArrayList<>();
for (CapabilityStatementRestResourceComponent l : left.getResource()) {
CapabilityStatementRestResourceComponent r = findInList(right.getResource(), l);
@ -410,7 +414,7 @@ public class CapabilityStatementComparer extends CanonicalResourceComparer {
}
}
private void compareRestResource(StructuralMatch<Element> sm, CapabilityStatementRestResourceComponent l, CapabilityStatementRestResourceComponent r, String path, CapabilityStatementComparison res, CapabilityStatementRestResourceComponent union, CapabilityStatementRestResourceComponent intersection) {
private void compareRestResource(StructuralMatch<Element> sm, CapabilityStatementRestResourceComponent l, CapabilityStatementRestResourceComponent r, String path, CapabilityStatementComparison res, CapabilityStatementRestResourceComponent union, CapabilityStatementRestResourceComponent intersection) throws DefinitionException, FHIRFormatError, IOException {
compareProfiles(path, sm, l.getProfileElement(), r.getProfileElement(), res, union, intersection);
// todo: supported profiles
compareStrings(path, sm.getMessages(), l.getDocumentation(), r.getDocumentation(), "documentation", IssueSeverity.INFORMATION, res);
@ -430,7 +434,7 @@ public class CapabilityStatementComparer extends CanonicalResourceComparer {
compareOperations(sm, l.getOperation(), r.getOperation(), path, res, union.getOperation(), intersection.getOperation());
}
private void compareProfiles(String path, StructuralMatch<Element> combined, CanonicalType left, CanonicalType right, CapabilityStatementComparison res, CapabilityStatementRestResourceComponent union, CapabilityStatementRestResourceComponent intersection) {
private void compareProfiles(String path, StructuralMatch<Element> combined, CanonicalType left, CanonicalType right, CapabilityStatementComparison res, CapabilityStatementRestResourceComponent union, CapabilityStatementRestResourceComponent intersection) throws DefinitionException, FHIRFormatError, IOException {
if (!left.hasValue() && !right.hasValue()) {
// nothing in this case
} else if (!left.hasValue()) {
@ -466,7 +470,9 @@ public class CapabilityStatementComparer extends CanonicalResourceComparer {
combined.getChildren().add(new StructuralMatch<Element>(left, right, vmI(IssueSeverity.WARNING, "Changed this profile to a narrower one", path)).setName("profile"));
} else {
combined.getChildren().add(new StructuralMatch<Element>(left, right, vmI(IssueSeverity.WARNING, "Different", path)).setName("profile"));
throw new Error("Not done yet");
ProfileComparison pc = (ProfileComparison) session.compare(sdLeft, sdRight);
intersection.setProfile(pc.getIntersection().getUrl());
union.setProfile(pc.getUnion().getUrl());
}
}
}

View File

@ -14,6 +14,7 @@ import java.util.Set;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.PathEngineException;
import org.hl7.fhir.r5.comparison.CapabilityStatementComparer.CapabilityStatementComparison;
import org.hl7.fhir.r5.comparison.CodeSystemComparer.CodeSystemComparison;
import org.hl7.fhir.r5.comparison.ProfileComparer.ProfileComparison;
import org.hl7.fhir.r5.comparison.ResourceComparer.PlaceHolderComparison;
@ -133,6 +134,8 @@ public class ComparisonRenderer implements IEvaluationContext {
renderValueSet(id, (ValueSetComparison) comp);
} else if (comp instanceof CodeSystemComparison) {
renderCodeSystem(id, (CodeSystemComparison) comp);
} else if (comp instanceof CapabilityStatementComparison) {
renderCapabilityStatement(id, (CapabilityStatementComparison) comp);
} else if (comp instanceof PlaceHolderComparison) {
renderPlaceHolder(id, (PlaceHolderComparison) comp);
}
@ -211,6 +214,25 @@ public class ComparisonRenderer implements IEvaluationContext {
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-union.json")), comp.getUnion());
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-intersection.json")), comp.getIntersection());
}
private void renderCapabilityStatement(String id, CapabilityStatementComparison comp) throws IOException {
String template = templates.get("CapabilityStatement");
Map<String, Base> vars = new HashMap<>();
CapabilityStatementComparer cs = new CapabilityStatementComparer(session);
vars.put("left", new StringType(comp.getLeft().present()));
vars.put("right", new StringType(comp.getRight().present()));
vars.put("leftId", new StringType(comp.getLeft().getId()));
vars.put("rightId", new StringType(comp.getRight().getId()));
vars.put("leftUrl", new StringType(comp.getLeft().getUrl()));
vars.put("rightUrl", new StringType(comp.getRight().getUrl()));
vars.put("errors", new StringType(new XhtmlComposer(true).compose(cs.renderErrors(comp))));
vars.put("metadata", new StringType(new XhtmlComposer(true).compose(cs.renderMetadata(comp, "", ""))));
vars.put("statement", new StringType(new XhtmlComposer(true).compose(cs.renderStatements(comp, "", ""))));
String cnt = processTemplate(template, "CapabilityStatement", vars);
TextFile.stringToFile(cnt, file(comp.getId()+".html"));
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-union.json")), comp.getUnion());
new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(Utilities.path(folder, comp.getId() + "-intersection.json")), comp.getIntersection());
}
private String processTemplate(String template, String name, Map<String, Base> vars) {
LiquidEngine engine = new LiquidEngine(contextRight, this);

View File

@ -8,6 +8,7 @@ import java.util.UUID;
import org.hl7.fhir.exceptions.DefinitionException;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.comparison.CapabilityStatementComparer.CapabilityStatementComparison;
import org.hl7.fhir.r5.comparison.CodeSystemComparer.CodeSystemComparison;
import org.hl7.fhir.r5.comparison.ProfileComparer.ProfileComparison;
import org.hl7.fhir.r5.comparison.ResourceComparer.ResourceComparison;
@ -16,6 +17,7 @@ import org.hl7.fhir.r5.conformance.ProfileUtilities;
import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CapabilityStatement;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.StructureDefinition;
@ -24,6 +26,7 @@ import org.hl7.fhir.utilities.Utilities;
public class ComparisonSession {
private Map<String, ResourceComparison> compares = new HashMap<>();
private IWorkerContext contextLeft;
private IWorkerContext contextRight;
@ -42,7 +45,7 @@ public class ComparisonSession {
this.title = title;
this.pkpLeft = pkpLeft;
this.pkpRight = pkpRight;
// debug = true;
debug = false;
}
public IWorkerContext getContextLeft() {
@ -94,10 +97,18 @@ public class ComparisonSession {
ProfileComparison csc = cs.compare((StructureDefinition) left, (StructureDefinition) right);
compares.put(key, csc);
return csc;
} else if (left instanceof CapabilityStatement && right instanceof CapabilityStatement) {
CapabilityStatementComparer cs = new CapabilityStatementComparer(this);
CapabilityStatementComparison csc = cs.compare((CapabilityStatement) left, (CapabilityStatement) right);
compares.put(key, csc);
return csc;
} else {
throw new FHIRException("Unable to compare resources of type "+left.fhirType()+" and "+right.fhirType());
}
} catch (Throwable e) {
if (debug) {
e.printStackTrace();
}
ResourceComparer.PlaceHolderComparison csc = new ResourceComparer.PlaceHolderComparison(left, right, e);
compares.put(key, csc);
return csc;

View File

@ -19,10 +19,12 @@ import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.DataType;
import org.hl7.fhir.r5.model.ElementDefinition;
import org.hl7.fhir.r5.model.ElementDefinition.AggregationMode;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent;
import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent;
import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.Enumeration;
import org.hl7.fhir.r5.model.Enumerations.BindingStrength;
import org.hl7.fhir.r5.model.IntegerType;
import org.hl7.fhir.r5.model.PrimitiveType;
@ -539,10 +541,13 @@ public class ProfileComparer extends CanonicalResourceComparer {
boolean pfound = false;
boolean tfound = false;
nw = nw.copy();
if (nw.hasAggregation())
throw new DefinitionException("Aggregation not supported: "+path);
for (TypeRefComponent ex : results) {
if (Utilities.equals(ex.getWorkingCode(), nw.getWorkingCode())) {
for (Enumeration<AggregationMode> a : nw.getAggregation()) {
if (!ex.hasAggregation(a.getValue())) {
ex.addAggregation(a.getValue());
}
}
if (!ex.hasProfile() && !nw.hasProfile())
pfound = true;
else if (!ex.hasProfile()) {
@ -635,14 +640,10 @@ public class ProfileComparer extends CanonicalResourceComparer {
private Collection<? extends TypeRefComponent> intersectTypes(ProfileComparison comp, StructuralMatch<ElementDefinitionNode> res, ElementDefinition ed, String path, List<TypeRefComponent> left, List<TypeRefComponent> right) throws DefinitionException, IOException, FHIRFormatError {
List<TypeRefComponent> result = new ArrayList<TypeRefComponent>();
for (TypeRefComponent l : left) {
if (l.hasAggregation())
throw new DefinitionException("Aggregation not supported: "+path);
boolean pfound = false;
boolean tfound = false;
TypeRefComponent c = l.copy();
for (TypeRefComponent r : right) {
if (r.hasAggregation())
throw new DefinitionException("Aggregation not supported: "+path);
if (!l.hasProfile() && !r.hasProfile()) {
pfound = true;
} else if (!r.hasProfile()) {
@ -697,9 +698,17 @@ public class ProfileComparer extends CanonicalResourceComparer {
}
}
}
if (pfound && tfound) {
for (Enumeration<AggregationMode> a : l.getAggregation()) {
if (!r.hasAggregation(a.getValue())) {
c.getAggregation().removeIf(n -> n.getValue() == a.getValue());
}
}
}
}
if (pfound && tfound)
if (pfound && tfound) {
result.add(c);
}
}
return result;
}

View File

@ -193,7 +193,7 @@ public class ValueSetComparer extends CanonicalResourceComparer {
if (matchCount == 1 && sourceCount == 1) {
for (ConceptSetComponent t : matches) {
if (t.getSystem().equals(item.getSystem())) {
if (t.getSystem() != null && t.getSystem().equals(item.getSystem())) {
return t;
}
}

View File

@ -56,13 +56,17 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.r5.conformance.ProfileUtilities.ProfileKnowledgeProvider.BindingResolution;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.hl7.fhir.r5.context.IWorkerContext.PackageVersion;
import org.hl7.fhir.r5.context.IWorkerContext.ValidationResult;
import org.hl7.fhir.r5.elementmodel.ObjectConverter;
import org.hl7.fhir.r5.elementmodel.Property;
import org.hl7.fhir.r5.formats.IParser;
import org.hl7.fhir.r5.formats.JsonParser;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.BooleanType;
import org.hl7.fhir.r5.model.CanonicalResource;
import org.hl7.fhir.r5.model.CanonicalType;
import org.hl7.fhir.r5.model.CodeSystem;
import org.hl7.fhir.r5.model.CodeType;
import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding;
@ -106,6 +110,7 @@ import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.UsageContext;
import org.hl7.fhir.r5.model.ValueSet;
import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionComponent;
import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.r5.renderers.TerminologyRenderer;
@ -125,6 +130,10 @@ import org.hl7.fhir.utilities.MarkDownProcessor;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.npm.BasePackageCacheManager;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.npm.NpmPackage;
import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformation;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
@ -6838,4 +6847,99 @@ public class ProfileUtilities extends TranslatingUtilities {
this.masterSourceFileNames = masterSourceFileNames;
}
}
public static int loadR5Extensions(BasePackageCacheManager pcm, IWorkerContext context) throws FHIRException, IOException {
NpmPackage npm = pcm.loadPackage("hl7.fhir.r5.core", "current");
String[] types = new String[] { "StructureDefinition", "ValueSet", "CodeSystem" };
Map<String, ValueSet> valueSets = new HashMap<>();
Map<String, CodeSystem> codeSystems = new HashMap<>();
List<StructureDefinition> extensions = new ArrayList<>();
JsonParser json = new JsonParser();
for (PackageResourceInformation pri : npm.listIndexedResources(types)) {
CanonicalResource r = (CanonicalResource) json.parse(npm.load(pri));
if (r instanceof CodeSystem) {
codeSystems.put(r.getUrl(), (CodeSystem) r);
} else if (r instanceof ValueSet) {
valueSets.put(r.getUrl(), (ValueSet) r);
} else if (r instanceof StructureDefinition) {
extensions.add((StructureDefinition) r);
}
}
PackageVersion pd = new PackageVersion(npm.name(), npm.version(), npm.dateAsDate());
int c = 0;
List<String> typeNames = context.getTypeNames();
for (StructureDefinition sd : extensions) {
if (sd.getType().equals("Extension") && sd.getDerivation() == TypeDerivationRule.CONSTRAINT &&
!context.hasResource(StructureDefinition.class, sd.getUrl())) {
if (survivesStrippingTypes(sd, context, typeNames)) {
c++;
sd.setUserData("path", Utilities.pathURL(npm.getWebLocation(), "extension-"+sd.getId()+".html"));
context.cacheResourceFromPackage(sd, pd);
registerTerminologies(sd, context, valueSets, codeSystems, pd);
}
}
}
return c;
}
private static void registerTerminologies(StructureDefinition sd, IWorkerContext context, Map<String, ValueSet> valueSets, Map<String, CodeSystem> codeSystems, PackageVersion pd) {
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.hasBinding() && ed.getBinding().hasValueSet()) {
String vs = ed.getBinding().getValueSet();
if (!context.hasResource(StructureDefinition.class, vs)) {
loadValueSet(vs, context, valueSets, codeSystems, pd);
}
}
}
}
private static void loadValueSet(String url, IWorkerContext context, Map<String, ValueSet> valueSets, Map<String, CodeSystem> codeSystems, PackageVersion pd) {
if (valueSets.containsKey(url)) {
ValueSet vs = valueSets.get(url);
context.cacheResourceFromPackage(vs, pd);
for (ConceptSetComponent inc : vs.getCompose().getInclude()) {
for (CanonicalType t : inc.getValueSet()) {
loadValueSet(t.asStringValue(), context, valueSets, codeSystems, pd);
}
if (inc.hasSystem()) {
if (!context.hasResource(CodeSystem.class, inc.getSystem()) && codeSystems.containsKey(inc.getSystem())) {
context.cacheResourceFromPackage(codeSystems.get(inc.getSystem()), pd);
}
}
}
}
}
private static boolean survivesStrippingTypes(StructureDefinition sd, IWorkerContext context, List<String> typeNames) {
for (ElementDefinition ed : sd.getDifferential().getElement()) {
stripTypes(ed, context, typeNames);
}
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
if (!stripTypes(ed, context, typeNames)) {
return false;
}
}
return true;
}
private static boolean stripTypes(ElementDefinition ed, IWorkerContext context, List<String> typeNames) {
if (!ed.getPath().contains(".") || !ed.hasType()) {
return true;
}
ed.getType().removeIf(tr -> !typeNames.contains(tr.getWorkingCode()));
if (!ed.hasType()) {
return false;
}
for (TypeRefComponent tr : ed.getType()) {
if (tr.hasTargetProfile()) {
tr.getTargetProfile().removeIf(n -> !context.hasResource(StructureDefinition.class, n.asStringValue()));
if (!tr.hasTargetProfile()) {
return false;
}
}
}
return true;
}
}

View File

@ -487,7 +487,7 @@ public class SimpleWorkerContext extends BaseWorkerContext implements IWorkerCon
t++;
} catch (Exception e) {
throw new FHIRException(formatMessage(I18nConstants.ERROR_READING__FROM_PACKAGE__, s, pi.name(), pi.version(), e.getMessage()), e);
}
}
}
} else {
if (types.length == 0) {

View File

@ -98,7 +98,7 @@ import org.slf4j.LoggerFactory;
public abstract class JsonParserBase extends ParserBase implements IParser {
static {
LoggerFactory.getLogger("org.hl7.fhir.r5.formats.XmlParserBase").debug("XML Parser is being loaded");
LoggerFactory.getLogger("org.hl7.fhir.r5.formats.JsonParserBase").debug("JSON Parser is being loaded");
ClassesLoadedFlags.ourJsonParserBaseLoaded = true;
}

View File

@ -916,7 +916,7 @@ public class DataRenderer extends Renderer {
CodeSystem cs = context.getWorker().fetchCodeSystem(system, version);
if (cs != null && cs.hasUserData("path")) {
if (!Utilities.noString(code)) {
return cs.getUserString("path")+"#"+Utilities.nmtokenize(code);
return cs.getUserString("path")+"#"+cs.getId()+"-"+Utilities.nmtokenize(code);
} else {
return cs.getUserString("path");
}
@ -932,8 +932,9 @@ public class DataRenderer extends Renderer {
if (Utilities.noString(s))
s = lookupCode(c.getSystem(), c.getVersion(), c.getCode());
CodeSystem cs = context.getWorker().fetchCodeSystem(c.getSystem());
String sn = describeSystem(c.getSystem());
String sn = cs != null ? cs.present() : describeSystem(c.getSystem());
String link = getLinkForCode(c.getSystem(), c.getVersion(), c.getCode());
if (link != null) {
x.ah(link).tx(sn);

View File

@ -680,7 +680,7 @@ public class ValueSetRenderer extends TerminologyRenderer {
if (disp == null) {
disp = getDisplayForUrl(url);
}
if (disp != null && !designations.containsKey(url)) {
if (disp != null && !designations.containsKey(url) && url != null) {
designations.put(url, disp);
}
}

View File

@ -140,7 +140,7 @@ class FHIRToolingClientTest {
private void checkHeaders(Headers argumentCaptorValue) {
getHeaders().forEach(header -> {
System.out.println("Checking header <" + header.component1().utf8() + ", " + header.component2().utf8() + ">");
// System.out.println("Checking header <" + header.component1().utf8() + ", " + header.component2().utf8() + ">");
Assertions.assertEquals(argumentCaptorValue.get(header.component1().utf8()), header.component2().utf8());
});
}

View File

@ -78,6 +78,10 @@ public class SimpleHTTPClient {
throw new IOException("Invalid HTTP response "+code+" from "+source+" ("+message+") (content in "+filename+")");
}
}
}
public String getMessage() {
return message;
}
}

View File

@ -108,7 +108,7 @@ public class PackageClient {
params.append("name="+name);
}
if (!Utilities.noString(canonical)) {
params.append("canonical="+canonical);
params.append("pkgcanonical="+canonical);
}
if (!Utilities.noString(fhirVersion)) {
params.append("fhirversion="+fhirVersion);

View File

@ -83,7 +83,8 @@ public class ValidationMessage implements Comparator<ValidationMessage>, Compara
Ontology,
ProfileComparer,
TerminologyEngine,
QuestionnaireResponseValidator
QuestionnaireResponseValidator,
IPAValidator
}
public enum IssueSeverity {

View File

@ -361,7 +361,7 @@ element__null_ = element = null: {0}
getSliceList_should_only_be_called_when_the_element_has_slicing = getSliceList should only be called when the element has slicing
Unable_to_resolve_name_reference__at_path_ = Unable to resolve name reference {0} at path {1}
Details_for__matching_against_Profile_ = Details for {0} matching against Profile {1}
Does_not_match_slice_ = Does not match slice ''{0}''
Does_not_match_slice_ = Does not match slice ''{0}'' (discriminator: {1})
Profile__does_not_match_for__because_of_the_following_profile_issues__ = Profile {0} does not match for {1} because of the following profile issues: {2}
This_element_does_not_match_any_known_slice_ = This element does not match any known slice{0}
defined_in_the_profile = defined in the profile

View File

@ -16,8 +16,8 @@ public class SimpleHTTPClientTest {
SimpleHTTPClient.HTTPResult res = http.get(url, "application/json");
System.out.println(res.getCode());
System.out.println(new String(res.getContent(), StandardCharsets.UTF_8));
// System.out.println(res.getCode());
// System.out.println(new String(res.getContent(), StandardCharsets.UTF_8));
assertTrue(res.getCode() != 400);
}
}

View File

@ -67,7 +67,7 @@ class I18nBaseTest {
BufferedReader reader = new BufferedReader(streamReader)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println("Searching for umlauts -> " + line);
// System.out.println("Searching for umlauts -> " + line);
Assertions.assertFalse(stringContainsItemFromList(line, UMLAUTS));
}
} catch (IOException e) {

View File

@ -35,9 +35,9 @@ public class CachingPackageClientTests {
public void testSearch() throws IOException {
CachingPackageClient client = new CachingPackageClient(CachingPackageClientTests.SERVER1);
List<PackageInfo> matches = client.search("core", null, null, false);
for (PackageInfo pi : matches) {
System.out.println(pi.toString());
}
// for (PackageInfo pi : matches) {
// System.out.println(pi.toString());
// }
Assertions.assertTrue(matches.size() > 0);
}
@ -52,9 +52,9 @@ public class CachingPackageClientTests {
public void testVersions() throws IOException {
CachingPackageClient client = new CachingPackageClient(CachingPackageClientTests.SERVER1);
List<PackageInfo> matches = client.getVersions("Simplifier.Core.STU3");
for (PackageInfo pi : matches) {
System.out.println(pi.toString());
}
// for (PackageInfo pi : matches) {
// System.out.println(pi.toString());
// }
Assertions.assertTrue(matches.size() > 0);
}
@ -78,9 +78,9 @@ public class CachingPackageClientTests {
public void testSearch2() throws IOException {
CachingPackageClient client = new CachingPackageClient(CachingPackageClientTests.SERVER2);
List<PackageInfo> matches = client.search("core", null, null, false);
for (PackageInfo pi : matches) {
System.out.println(pi.toString());
}
// for (PackageInfo pi : matches) {
// System.out.println(pi.toString());
// }
Assertions.assertTrue(matches.size() > 0);
}
@ -105,9 +105,9 @@ public class CachingPackageClientTests {
public void testVersions2A() throws IOException {
CachingPackageClient client = new CachingPackageClient(CachingPackageClientTests.SERVER2);
List<PackageInfo> matches = client.getVersions("hl7.fhir.us.core");
for (PackageInfo pi : matches) {
System.out.println(pi.toString());
}
// for (PackageInfo pi : matches) {
// System.out.println(pi.toString());
// }
Assertions.assertTrue(matches.size() > 0);
}

View File

@ -71,7 +71,7 @@ public class XhtmlNodeTest {
"</xhtml:div>");
String output = node.getValueAsString();
ourLog.info(output);
// ourLog.info(output);
Assertions.assertEquals("<div xmlns=\"http://www.w3.org/1999/xhtml\"><img src=\"http://pbs.twimg.com/profile_images/544507893991485440/r_vo3uj2_bigger.png\" alt=\"Twitter Avatar\"/>@fhirabend</div>", output);
}

View File

@ -192,7 +192,7 @@ public class ValidatorCli {
res.add("-version");
res.add("4.0");
res.add("-ig");
res.add("hl7.fhir.uv.ips#current$connectathon-2");
res.add("hl7.fhir.uv.ips#current");
res.add("-profile");
res.add("http://hl7.org/fhir/uv/ips/StructureDefinition/Bundle-uv-ips");
} else if (a.startsWith("-ips$")) {

View File

@ -56,6 +56,7 @@ public class ComparisonService {
// File htmlFile = cr.render(left, right);
// Desktop.getDesktop().browse(htmlFile.toURI());
// System.out.println("Done");
// cr.getTemplates().put("CapabilityStatement", new String(context.getBinaries().get("template-comparison-CapabilityStatement.html")));
}
public static void compareStructureDefinitions(String dest, ValidationEngine validator, String left, String right, StructureDefinition resLeft, StructureDefinition resRight) throws IOException, FHIRException, EOperationOutcome {
@ -70,6 +71,7 @@ public class ComparisonService {
cr.getTemplates().put("ValueSet", new String(validator.getContext().getBinaries().get("template-comparison-ValueSet.html")));
cr.getTemplates().put("Profile", new String(validator.getContext().getBinaries().get("template-comparison-Profile.html")));
cr.getTemplates().put("Index", new String(validator.getContext().getBinaries().get("template-comparison-index.html")));
cr.getTemplates().put("CapabilityStatement", new String(validator.getContext().getBinaries().get("template-comparison-CapabilityStatement.html")));
File htmlFile = cr.render(left, right);
Desktop.getDesktop().browse(htmlFile.toURI());
System.out.println("Done");

View File

@ -4,6 +4,9 @@ import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.r5.context.SimpleWorkerContext;
import org.hl7.fhir.r5.context.SystemOutLoggingService;
import org.hl7.fhir.r5.context.TerminologyCache;
import org.hl7.fhir.r5.conformance.ProfileUtilities;
import org.hl7.fhir.r5.context.IWorkerContext.PackageVersion;
import org.hl7.fhir.r5.context.SimpleWorkerContext.PackageResourceLoader;
import org.hl7.fhir.r5.elementmodel.Manager;
import org.hl7.fhir.r5.elementmodel.Manager.FhirFormat;
import org.hl7.fhir.r5.formats.IParser;
@ -12,6 +15,7 @@ import org.hl7.fhir.r5.formats.XmlParser;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind;
import org.hl7.fhir.r5.model.StructureDefinition.TypeDerivationRule;
import org.hl7.fhir.r5.renderers.spreadsheets.CodeSystemSpreadsheetGenerator;
import org.hl7.fhir.r5.renderers.spreadsheets.ConceptMapSpreadsheetGenerator;
import org.hl7.fhir.r5.renderers.spreadsheets.StructureDefinitionSpreadsheetGenerator;
@ -24,8 +28,11 @@ import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.TimeTracker;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.VersionUtilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
import org.hl7.fhir.utilities.npm.FilesystemPackageCacheManager;
import org.hl7.fhir.utilities.npm.NpmPackage;
import org.hl7.fhir.utilities.npm.ToolsVersion;
import org.hl7.fhir.utilities.npm.NpmPackage.PackageResourceInformation;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.validation.IgLoader;
import org.hl7.fhir.validation.ValidationEngine;
@ -39,6 +46,8 @@ import org.hl7.fhir.validation.cli.renderers.ValidationOutputRenderer;
import org.hl7.fhir.validation.cli.utils.EngineMode;
import org.hl7.fhir.validation.cli.utils.VersionSourceInformation;
import lombok.val;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
@ -46,7 +55,9 @@ import java.io.PrintStream;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ValidationService {
@ -329,6 +340,8 @@ public class ValidationService {
IgLoader igLoader = new IgLoader(validator.getPcm(), validator.getContext(), validator.getVersion(), validator.isDebug());
System.out.println(" - " + validator.getContext().countAllCaches() + " resources (" + tt.milestone() + ")");
igLoader.loadIg(validator.getIgs(), validator.getBinaries(), "hl7.terminology", false);
System.out.print(" Load R5 Extensions");
System.out.println(" - " + ProfileUtilities.loadR5Extensions(validator.getPcm(), validator.getContext()) + " resources (" + tt.milestone() + ")");
System.out.print(" Terminology server " + cliContext.getTxServer());
String txver = validator.setTerminologyServer(cliContext.getTxServer(), cliContext.getTxLog(), ver);
System.out.println(" - Version " + txver + " (" + tt.milestone() + ")");
@ -378,6 +391,8 @@ public class ValidationService {
}
public String determineVersion(CliContext cliContext) throws Exception {
return determineVersion(cliContext, null);
}

View File

@ -4012,10 +4012,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
throw new DefinitionException(context.formatMessage(I18nConstants.DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_MULTIPLE_TYPES_, discriminator, ed.getId(), profile.getUrl(), criteriaElement.typeSummary()));
} else
throw new DefinitionException(context.formatMessage(I18nConstants.DISCRIMINATOR__IS_BASED_ON_TYPE_BUT_SLICE__IN__HAS_NO_TYPES, discriminator, ed.getId(), profile.getUrl()));
if (discriminator.isEmpty())
if (discriminator.isEmpty()) {
expression.append(" and $this is " + type);
else
} else {
expression.append(" and " + discriminator + " is " + type);
}
} else if (s.getType() == DiscriminatorType.PROFILE) {
if (criteriaElement.getType().size() == 0) {
throw new DefinitionException(context.formatMessage(I18nConstants.PROFILE_BASED_DISCRIMINATORS_MUST_HAVE_A_TYPE__IN_PROFILE_, criteriaElement.getId(), profile.getUrl()));
@ -4036,12 +4037,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
expression.append(" and " + discriminator + ".conformsTo('" + list.get(0).getValue() + "')");
}
} else if (s.getType() == DiscriminatorType.EXISTS) {
if (criteriaElement.hasMin() && criteriaElement.getMin() >= 1)
if (criteriaElement.hasMin() && criteriaElement.getMin() >= 1) {
expression.append(" and (" + discriminator + ".exists())");
else if (criteriaElement.hasMax() && criteriaElement.getMax().equals("0"))
} else if (criteriaElement.hasMax() && criteriaElement.getMax().equals("0")) {
expression.append(" and (" + discriminator + ".exists().not())");
else
} else {
throw new FHIRException(context.formatMessage(I18nConstants.DISCRIMINATOR__IS_BASED_ON_ELEMENT_EXISTENCE_BUT_SLICE__NEITHER_SETS_MIN1_OR_MAX0, discriminator, ed.getId()));
}
} else if (criteriaElement.hasFixed()) {
buildFixedExpression(ed, expression, discriminator, criteriaElement);
} else if (criteriaElement.hasPattern()) {
@ -4072,12 +4074,13 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
timeTracker.fpe(t);
ed.setUserData("slice.expression.cache", n);
} else {
}
ValidatorHostContext shc = hostContext.forSlicing();
boolean pass = evaluateSlicingExpression(shc, element, path, profile, n);
if (!pass) {
slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, isProfile(slicer), (context.formatMessage(I18nConstants.DOES_NOT_MATCH_SLICE_, ed.getSliceName())), "discriminator = " + Utilities.escapeXml(n.toString()), null);
slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, isProfile(slicer), (context.formatMessage(I18nConstants.DOES_NOT_MATCH_SLICE_, ed.getSliceName(), n.toString().substring(8).trim())), "discriminator = " + Utilities.escapeXml(n.toString()), null);
for (String url : shc.getSliceRecords().keySet()) {
slicingHint(sliceInfo, IssueType.STRUCTURE, element.line(), element.col(), path, false, isProfile(slicer),
context.formatMessage(I18nConstants.DETAILS_FOR__MATCHING_AGAINST_PROFILE_, stack.getLiteralPath(), url),
@ -4089,6 +4092,16 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
return pass;
}
private boolean isBaseDefinition(String url) {
boolean b = url.startsWith("http://hl7.org/fhir/") && !url.substring(40).contains("/");
return b;
}
private String descSD(String url) {
StructureDefinition sd = context.fetchResource(StructureDefinition.class, url);
return sd == null ? url : sd.present();
}
private boolean isProfile(ElementDefinition slicer) {
if (slicer == null || !slicer.hasSlicing()) {
return false;

View File

@ -1,9 +1,22 @@
package org.hl7.fhir.validation.ipa;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r5.elementmodel.Element;
import org.hl7.fhir.r5.elementmodel.JsonParser;
import org.hl7.fhir.utilities.SimpleHTTPClient;
import org.hl7.fhir.utilities.SimpleHTTPClient.HTTPResult;
import org.hl7.fhir.utilities.validation.ValidationMessage;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity;
import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType;
import org.hl7.fhir.utilities.validation.ValidationMessage.Source;
import org.hl7.fhir.validation.instance.InstanceValidator;
import org.hl7.fhir.validation.ipa.IPAValidator.ValidationNode;
/**
* You give this validator three parameters:
@ -16,6 +29,24 @@ import org.hl7.fhir.validation.instance.InstanceValidator;
*/
public class IPAValidator {
public class ValidationNode {
public ValidationNode(String string) {
// TODO Auto-generated constructor stub
}
public List<ValidationMessage> getIssues() {
// TODO Auto-generated method stub
return null;
}
public String getName() {
// TODO Auto-generated method stub
return null;
}
}
private String address;
private String token;
private String urn;
@ -32,32 +63,39 @@ public class IPAValidator {
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getUrn() {
return urn;
}
public void setUrn(String urn) {
this.urn = urn;
}
public InstanceValidator getValidator() {
return validator;
}
public void setValidator(InstanceValidator validator) {
this.validator = validator;
}
public void validate() {
List<Patient> patients = searchPatients();
// check list of patients that have access to
// require that at least one of the patients matches the URL
List<Element> patients = searchPatients();
if (patients.size() > 0) {
// validate all resources and links
// validate search parameters
// check self links
@ -71,14 +109,84 @@ public class IPAValidator {
// Observation patient+category, patient+code, patient+category+date patient+category+status, patient+code+date
// Patient _id, identifier birthdate, family, gender, given, name, family+gender, birthdate+family, birthdate+name, gender+name
}
}
private List<Patient> searchPatients() {
private List<Element> searchPatients() {
ValidationNode vn = new ValidationNode("Patient Search");
log("Searching Patients");
Element bundle = makeRequest(vn, "/Patient");
List<Element> list = new ArrayList<>();
if (bundle != null) {
checkSelfLink(vn, bundle, null);
List<Element> entries = bundle.getChildren("entry");
int i = 0;
for (Element entry : entries) {
Element resource = entry.getNamedChild("resource");
if (resource != null && resource.fhirType().equals("Patient")) {
validator.validate(this, vn.getIssues(), "Bundle.entry["+i+"].resource", resource, "http://hl7.org/fhir/uv/ipa/StructureDefinition/ipa-patient");
list.add(resource);
}
}
}
if (list.size() > 1) {
vn.getIssues().add(new ValidationMessage(Source.IPAValidator, IssueType.EXCEPTION, "patient.search",
"Multiple Patients found; check that this is an expected outcome",
IssueSeverity.WARNING));
} else if (list.size() == 0) {
vn.getIssues().add(new ValidationMessage(Source.IPAValidator, IssueType.EXCEPTION, "patient.search",
"No Patients found, unable to continue",
IssueSeverity.ERROR));
}
return list;
}
private void checkSelfLink(ValidationNode vn, Element bundle, Map<String, String> params) {
// we check that there's a self link
Element sl = null;
for (Element e : bundle.getChildren("link")) {
if ("self".equals(e.getNamedChildValue("relation"))) {
sl = e.getNamedChild("url");
}
}
if (sl == null) {
vn.getIssues().add(new ValidationMessage(Source.IPAValidator, IssueType.EXCEPTION, vn.getName(),
"Self link not found in search result",
IssueSeverity.ERROR));
} else if (params != null) {
// we check that all the provided params are in the self link
}
}
private Element makeRequest(ValidationNode vn, String url) {
try {
SimpleHTTPClient http = new SimpleHTTPClient();
HTTPResult result = http.get(url, "application/fhir+json");
if (result.getCode() >= 300) {
vn.getIssues().add(new ValidationMessage(Source.IPAValidator, IssueType.EXCEPTION, "http.request",
"HTTP Return code is "+result.getCode()+" "+result.getMessage(),
IssueSeverity.FATAL));
return null;
} else if (result.getContent() == null || result.getContent().length == 0) {
vn.getIssues().add(new ValidationMessage(Source.IPAValidator, IssueType.EXCEPTION, "http.request",
"No Content Returned",
IssueSeverity.FATAL));
return null;
} else {
return new JsonParser(validator.getContext()).parse(new ByteArrayInputStream(result.getContent())).get(0).getElement();
}
} catch (Exception e) {
vn.getIssues().add(new ValidationMessage(Source.IPAValidator, IssueType.EXCEPTION, "http.request", e.getMessage(),
IssueSeverity.FATAL));
return null;
}
return null;
}
private void log(String msg) {
System.out.println(msg);
}
}

View File

@ -0,0 +1,19 @@
-------------------------------------------------------------------------------------
{"hierarchical" : false, "valueSet" :{
"resourceType" : "ValueSet",
"compose" : {
"inactive" : true,
"include" : [{
"system" : "urn:iso:std:iso:4217"
}]
}
}}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------
{"hierarchical" : false, "url": "http://hl7.org/fhir/ValueSet/currencies", "version": "4.0.1"}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------

View File

@ -0,0 +1,11 @@
-------------------------------------------------------------------------------------
{"code" : {
"system" : "urn:ietf:bcp:47",
"code" : "fr-CA"
}, "valueSet" :null, "lang":"en-US", "useServer":"true", "useClient":"true", "guessSystem":"false", "valueSetMode":"ALL_CHECKS", "versionFlexible":"true"}####
v: {
"severity" : "error",
"error" : "Attempt to use Terminology server when no Terminology server is available",
"class" : "SERVER_ERROR"
}
-------------------------------------------------------------------------------------

View File

@ -0,0 +1,90 @@
-------------------------------------------------------------------------------------
{"hierarchical" : false, "valueSet" :{
"resourceType" : "ValueSet",
"compose" : {
"inactive" : true,
"include" : [{
"system" : "http://loinc.org",
"concept" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "A."
}],
"code" : "LA20752-4",
"display" : "Within 24 hours"
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "B."
}],
"code" : "LA20753-2",
"display" : "After 24 hours but before 3 days"
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "C."
}],
"code" : "LA20754-0",
"display" : "Three days or later"
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "D."
}],
"code" : "LA4489-6",
"display" : "Unknown"
}]
}]
}
}}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------
{"hierarchical" : false, "valueSet" :{
"resourceType" : "ValueSet",
"compose" : {
"include" : [{
"system" : "http://loinc.org",
"concept" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "A."
}],
"code" : "LA20752-4",
"display" : "Within 24 hours"
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "B."
}],
"code" : "LA20753-2",
"display" : "After 24 hours but before 3 days"
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "C."
}],
"code" : "LA20754-0",
"display" : "Three days or later"
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-label",
"valueString" : "D."
}],
"code" : "LA4489-6",
"display" : "Unknown"
}]
}]
}
}}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------

View File

@ -0,0 +1,19 @@
-------------------------------------------------------------------------------------
{"hierarchical" : false, "valueSet" :{
"resourceType" : "ValueSet",
"compose" : {
"inactive" : true,
"include" : [{
"system" : "urn:ietf:bcp:13"
}]
}
}}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------
{"hierarchical" : false, "url": "http://hl7.org/fhir/ValueSet/mimetypes", "version": "4.0.1"}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------

View File

@ -0,0 +1,103 @@
-------------------------------------------------------------------------------------
{"hierarchical" : false, "valueSet" :{
"resourceType" : "ValueSet",
"compose" : {
"inactive" : true,
"include" : [{
"system" : "http://unitsofmeasure.org",
"concept" : [{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "second"
}],
"code" : "s",
"display" : "second",
"designation" : [{
"language" : "zh",
"value" : "秒"
}]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "minute"
}],
"code" : "min",
"display" : "minute",
"designation" : [{
"language" : "zh",
"value" : "分钟"
}]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "hour"
}],
"code" : "h",
"display" : "hour",
"designation" : [{
"language" : "zh",
"value" : "小时"
}]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "day"
}],
"code" : "d",
"display" : "day",
"designation" : [{
"language" : "zh",
"value" : "天"
}]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "week"
}],
"code" : "wk",
"display" : "week",
"designation" : [{
"language" : "zh",
"value" : "星期"
}]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "month"
}],
"code" : "mo",
"display" : "month",
"designation" : [{
"language" : "zh",
"value" : "月"
}]
},
{
"extension" : [{
"url" : "http://hl7.org/fhir/StructureDefinition/valueset-concept-definition",
"valueString" : "year"
}],
"code" : "a",
"display" : "year",
"designation" : [{
"language" : "zh",
"value" : "年"
}]
}]
}]
}
}}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------
{"hierarchical" : false, "url": "http://hl7.org/fhir/ValueSet/units-of-time", "version": "4.0.1"}####
e: {
"error" : "java.lang.NullPointerException"
}
-------------------------------------------------------------------------------------