use of org.hl7.fhir.utilities.xml.SchematronWriter.Rule in project beneficiary-fhir-data by CMSgov.
the class BeneficiaryTransformerV2Test method shouldIncludeMedicareExtensionIdentifierWithHistory.
@Test
public void shouldIncludeMedicareExtensionIdentifierWithHistory() {
createPatient(getRHwithIncldIdentityHdr("mbi"));
List<Identifier> patientIdentList = patient.getIdentifier();
assertEquals(2, patientIdentList.size());
ArrayList<Identifier> compareIdentList = new ArrayList<Identifier>();
Identifier ident = TransformerTestUtilsV2.createIdentifier("https://bluebutton.cms.gov/resources/variables/bene_id", "567834", "http://terminology.hl7.org/CodeSystem/v2-0203", "MB", "Member Number");
compareIdentList.add(ident);
Extension extension = new Extension("https://bluebutton.cms.gov/resources/codesystem/identifier-currency", new Coding("https://bluebutton.cms.gov/resources/codesystem/identifier-currency", "current", "Current"));
Period period = new Period();
try {
Date start = (new SimpleDateFormat("yyyy-MM-dd")).parse("2020-07-30");
period.setStart(start, TemporalPrecisionEnum.DAY);
} catch (Exception e) {
}
ident = new Identifier();
ident.setValue("3456789").setSystem("http://hl7.org/fhir/sid/us-mbi").setPeriod(period).getType().addCoding().setCode("MC").setSystem("http://terminology.hl7.org/CodeSystem/v2-0203").setDisplay("Patient's Medicare number").addExtension(extension);
compareIdentList.add(ident);
/**
* We have implemented a rule that for a valid MBI history record it has to have an end date; if
* no end date then the MBI has probably been provided by the CURRENT MBI extension. the
* following code is therefore commented out until the current Sample_A rif data provides a
* valid MedicareBeneficiaryIdHistory record.
*/
/*
extension =
new Extension(
"https://bluebutton.cms.gov/resources/codesystem/identifier-currency",
new Coding(
"https://bluebutton.cms.gov/resources/codesystem/identifier-currency",
"historic",
"Historic"));
ident = new Identifier();
ident
.setValue("9AB2WW3GR44")
.setSystem("http://hl7.org/fhir/sid/us-mbi")
.getType()
.addCoding()
.setCode("MC")
.setSystem("http://terminology.hl7.org/CodeSystem/v2-0203")
.setDisplay("Patient's Medicare number")
.addExtension(extension);
compareIdentList.add(ident);
*/
assertEquals(compareIdentList.size(), patientIdentList.size());
for (int i = 0; i < compareIdentList.size(); i++) {
assertTrue(compareIdentList.get(i).equalsDeep(patientIdentList.get(i)));
}
}
use of org.hl7.fhir.utilities.xml.SchematronWriter.Rule in project fhir-bridge by ehrbase.
the class SmartOnFhirAuthorizationInterceptor method addSmartOFPatientRules.
/**
* Allos read and write access to all Patients resources for a particular patient Id In FHIR
* terms, if the resource's Patient compartment matches the provided id, then read and write
* operations can take place.
*
* @param pSmartOnFhirPatientId The patient identifier a resource should have to be allowed for
* read or write access.
* @param rules A list of rules to add to. Rules based on the provided patient id will be added to
* this collection.
*/
private void addSmartOFPatientRules(String pSmartOnFhirPatientId, List<IAuthRule> rules) {
// no create rule -> should be done by keycloak registration at the moment
IdType sofId = new IdType(Patient.class.getSimpleName(), pSmartOnFhirPatientId);
rules.addAll(buildReadRule("rule_read_own_sof_patient_resource", Patient.class, Patient.class.getSimpleName(), sofId));
rules.addAll(buildWriteRule("rule_update_own_sof_patient_resource", Patient.class, Patient.class.getSimpleName(), sofId));
}
use of org.hl7.fhir.utilities.xml.SchematronWriter.Rule in project kindling by HL7.
the class ResourceValidator method check.
// private List<ValidationMessage> check(String n, BindingSpecification cd) throws Exception {
// List<ValidationMessage> errors = new ArrayList<ValidationMessage>();
// check(errors, n, cd);
// return errors;
// }
private void check(List<ValidationMessage> errors, String path, BindingSpecification cd, String sd, ElementDefn e) throws Exception {
// basic integrity checks
List<DefinedCode> ac = cd.getAllCodes(definitions.getCodeSystems(), definitions.getValuesets(), false);
for (DefinedCode c : ac) {
String d = c.getCode();
if (Utilities.noString(d))
d = c.getId();
if (Utilities.noString(d))
d = c.getDisplay();
if (Utilities.noString(d))
d = c.getDisplay();
if (Utilities.noString(c.getSystem()))
warning(errors, IssueType.STRUCTURE, "Binding @ " + path, !Utilities.noString(c.getDefinition()), "Code " + d + " must have a definition");
rule(errors, IssueType.STRUCTURE, "Binding @ " + path, !(Utilities.noString(c.getId()) && Utilities.noString(c.getSystem())), "Code " + d + " must have a id or a system");
}
// trigger processing into a Heirachical set if necessary
// rule(errors, IssueType.STRUCTURE, "Binding @ "+path, !cd.isHeirachical() || (cd.getChildCodes().size() < cd.getCodes().size()), "Logic error processing Hirachical code set");
// now, rules for the source
hint(errors, IssueType.STRUCTURE, "Binding @ " + path, cd.getBinding() != BindingMethod.Unbound, "Need to provide a binding");
rule(errors, IssueType.STRUCTURE, "Binding @ " + path, Utilities.noString(cd.getDefinition()) || (cd.getDefinition().charAt(0) == cd.getDefinition().toUpperCase().charAt(0)), "Definition cannot start with a lowercase letter");
if (cd.getBinding() == BindingMethod.CodeList || (cd.getBinding() == BindingMethod.ValueSet && cd.getStrength() == BindingStrength.REQUIRED && ac.size() > 0 && "code".equals(e.typeCode()))) {
if (path.toLowerCase().endsWith("status")) {
if (rule(errors, IssueType.STRUCTURE, path, definitions.getStatusCodes().containsKey(path), "Status element not registered in status-codes.xml")) {
// rule(errors, IssueType.STRUCTURE, path, e.isModifier(), "Status elements that map to status-codes should be labelled as a modifier");
ArrayList<String> list = definitions.getStatusCodes().get(path);
for (DefinedCode c : ac) {
boolean ok = false;
for (String s : list) {
String[] parts = s.split("\\,");
for (String p : parts) if (p.trim().equals(c.getCode()))
ok = true;
}
rule(errors, IssueType.STRUCTURE, path, ok, "Status element code \"" + c.getCode() + "\" not found in status-codes.xml");
}
for (String s : list) {
String[] parts = s.split("\\,");
for (String p : parts) {
List<String> cl = new ArrayList<>();
if (!Utilities.noString(p)) {
boolean ok = false;
for (DefinedCode c : ac) {
cl.add(c.getCode());
if (p.trim().equals(c.getCode()))
ok = true;
}
if (!ok)
rule(errors, IssueType.STRUCTURE, path, ok, "Status element code \"" + p + "\" found for " + path + " in status-codes.xml but has no matching code in the resource (codes = " + cl + ")");
}
}
}
}
}
StringBuilder b = new StringBuilder();
for (DefinedCode c : ac) {
if (!c.getAbstract())
b.append(" | ").append(c.getCode());
}
if (sd.equals("*")) {
e.setShortDefn(b.toString().substring(3));
sd = b.toString().substring(3);
}
if (sd.contains("|")) {
if (b.length() < 3)
throw new Error("surprise");
String esd = b.substring(3);
rule(errors, IssueType.STRUCTURE, path, sd.startsWith(esd) || (sd.endsWith("+") && b.substring(3).startsWith(sd.substring(0, sd.length() - 1))), "The short description \"" + sd + "\" does not match the expected (\"" + b.substring(3) + "\")");
} else {
rule(errors, IssueType.STRUCTURE, path, cd.getStrength() != BindingStrength.REQUIRED || ac.size() > 12 || ac.size() <= 1 || !hasGoodCode(ac) || isExemptFromCodeList(path), "The short description of an element with a code list should have the format code | code | etc (is " + sd.toString() + ") (" + ac.size() + " codes = \"" + b.toString() + "\")");
}
}
boolean isComplex = !e.typeCode().equals("code");
if (isComplex && cd.getValueSet() != null && hasInternalReference(cd.getValueSet()) && cd.getStrength() != BindingStrength.EXAMPLE) {
hint(errors, IssueType.BUSINESSRULE, path, false, "The value " + cd.getValueSet().getUrl() + " defines codes, but is used by a Coding/CodeableConcept @ " + path + ", so it should not use FHIR defined codes");
cd.getValueSet().setUserData("vs-val-warned", true);
}
if (cd.getElementType() == ElementType.Unknown) {
if (isComplex)
cd.setElementType(ElementType.Complex);
else
cd.setElementType(ElementType.Simple);
} else if (isComplex && !cd.hasMax())
rule(errors, IssueType.STRUCTURE, path, cd.getElementType() == ElementType.Complex, "Cannot use a binding from both code and Coding/CodeableConcept elements");
else
rule(errors, IssueType.STRUCTURE, path, cd.getElementType() == ElementType.Simple, "Cannot use a binding from both code and Coding/CodeableConcept elements");
if (isComplex && cd.getValueSet() != null) {
for (ConceptSetComponent inc : cd.getValueSet().getCompose().getInclude()) if (inc.hasSystem())
txurls.add(inc.getSystem());
}
rule(errors, IssueType.STRUCTURE, "Binding @ " + path, (cd.getElementType() != ElementType.Simple || cd.getStrength() == BindingStrength.REQUIRED || cd.hasMax()) || isExemptFromProperBindingRules(path), "Must be a required binding if bound to a code instead of a Coding/CodeableConcept");
rule(errors, IssueType.STRUCTURE, "Binding @ " + path, cd.getElementType() != ElementType.Simple || cd.getBinding() != BindingMethod.Unbound, "Need to provide a binding for code elements");
if (!isComplex && !externalException(path)) {
ValueSet vs = cd.getValueSet();
if (warning(errors, IssueType.REQUIRED, path, vs != null || cd.hasReference(), "Unable to resolve value set on 'code' Binding")) {
hint(errors, IssueType.REQUIRED, path, noExternals(vs), "Bindings for code data types should only use internally defined codes (" + vs.getUrl() + ")");
// don't disable this without discussion on Zulip
}
}
}
use of org.hl7.fhir.utilities.xml.SchematronWriter.Rule in project kindling by HL7.
the class ResourceValidator method check.
public void check(List<ValidationMessage> errors, String name, ResourceDefn rd) throws Exception {
for (String s : rd.getHints()) hint(errors, IssueType.INFORMATIONAL, rd.getName(), false, s);
rule(errors, IssueType.STRUCTURE, rd.getName(), !name.equals("Metadata"), "The name 'Metadata' is not a legal name for a resource");
rule(errors, IssueType.STRUCTURE, rd.getName(), !name.equals("History"), "The name 'History' is not a legal name for a resource");
rule(errors, IssueType.STRUCTURE, rd.getName(), !name.equals("Tag"), "The name 'Tag' is not a legal name for a resource");
rule(errors, IssueType.STRUCTURE, rd.getName(), !name.equals("Tags"), "The name 'Tags' is not a legal name for a resource");
rule(errors, IssueType.STRUCTURE, rd.getName(), !name.equals("MailBox"), "The name 'MailBox' is not a legal name for a resource");
rule(errors, IssueType.STRUCTURE, rd.getName(), !name.equals("Validation"), "The name 'Validation' is not a legal name for a resource");
rule(errors, IssueType.REQUIRED, rd.getName(), name.equals("Parameters") || translations.hasTranslation(name), "The name '" + name + "' is not found in the file translations.xml");
rule(errors, IssueType.STRUCTURE, rd.getName(), name.length() > 1 && Character.isUpperCase(name.charAt(0)), "Resource Name must start with an uppercase alpha character");
rule(errors, IssueType.STRUCTURE, rd.getName(), !Utilities.noString(rd.getFmmLevel()), "Resource must have a maturity level");
// too many downstream issues in the parsers, and it would only happen as a transient thing when designing the resources
rule(errors, IssueType.REQUIRED, rd.getName(), rd.getRoot().getElements().size() > 0, "A resource must have at least one element in it before the build can proceed");
// too many downstream issues in the parsers, and it would only happen as a transient thing when designing the resources
rule(errors, IssueType.REQUIRED, rd.getName(), rd.getWg() != null, "A resource must have a designated owner");
rule(errors, IssueType.REQUIRED, rd.getName(), !Utilities.noString(rd.getRoot().getW5()), "A resource must have a W5 category");
rd.getRoot().setMinCardinality(0);
rd.getRoot().setMaxCardinality(Integer.MAX_VALUE);
// pattern related rules
buildW5Mappings(rd.getRoot(), true);
if ((isWorkflowPattern(rd, "Event") || isWorkflowPattern(rd, "Request")) && hasPatient(rd)) {
rule(errors, IssueType.STRUCTURE, rd.getName(), rd.getSearchParams().containsKey("patient"), "An 'event' or 'request' resource must have a search parameter 'patient'");
}
if (suppressedwarning(errors, IssueType.REQUIRED, rd.getName(), hasW5Mappings(rd) || rd.getName().equals("Binary") || rd.getName().equals("OperationOutcome") || rd.getName().equals("Parameters"), "A resource must have w5 mappings")) {
String w5Order = listW5Elements(rd);
String w5CorrectOrder = listW5Correct(rd);
if (!w5Order.equals(w5CorrectOrder)) {
warning(errors, IssueType.REQUIRED, rd.getName(), false, "Resource elements are out of order. The correct order is '" + w5CorrectOrder + "' but the actual order is '" + w5Order + "'");
// System.out.println("Resource "+parent.getName()+": elements are out of order. The correct order is '"+w5CorrectOrder+"' but the actual order is '"+w5Order+"'");
}
if (!Utilities.noString(rd.getProposedOrder())) {
w5Order = listW5Elements(rd, rd.getProposedOrder());
if (!w5Order.equals(w5CorrectOrder)) {
rule(errors, IssueType.REQUIRED, rd.getName(), false, "Proposed Resource elements are out of order. The correct order is '" + w5CorrectOrder + "' but the proposed order is '" + w5Order + "'");
} else
System.out.println("Proposed order for " + rd.getName() + ": build order ok");
}
}
if (Utilities.noString(rd.getEnteredInErrorStatus()))
if (hasStatus(rd, "entered-in-error"))
rd.setEnteredInErrorStatus(".status = entered-in-error");
else if (hasStatus(rd, "retired"))
rd.setEnteredInErrorStatus(".status = retired");
else if (hasActivFalse(rd))
rd.setEnteredInErrorStatus(".active = false");
else
// too many downstream issues in the parsers, and it would only happen as a transient thing when designing the resources
warning(errors, IssueType.REQUIRED, rd.getName(), false, "A resource must have an 'entered in error' status");
String s = rd.getRoot().getMapping(Definitions.RIM_MAPPING);
hint(errors, IssueType.REQUIRED, rd.getName(), !Utilities.noString(s), "RIM Mapping is required");
for (Operation op : rd.getOperations()) {
warning(errors, IssueType.BUSINESSRULE, rd.getName() + ".$" + op.getName(), hasOpExample(op.getExamples(), false), "Operation must have an example request");
warning(errors, IssueType.BUSINESSRULE, rd.getName() + ".$" + op.getName(), hasOpExample(op.getExamples(), true), "Operation must have an example response");
}
List<String> vsWarns = new ArrayList<String>();
int vsWarnings = checkElement(errors, rd.getName(), rd.getRoot(), rd, null, s == null || !s.equalsIgnoreCase("n/a"), false, hasSummary(rd.getRoot()), vsWarns, true, rd.getStatus());
if (!resourceIsTechnical(name)) {
// these are exempt because identification is tightly managed
ElementDefn id = rd.getRoot().getElementByName(definitions, "identifier", true, false);
if (id == null)
warning(errors, IssueType.STRUCTURE, rd.getName(), false, "All resources should have an identifier");
else
rule(errors, IssueType.STRUCTURE, rd.getName(), id.typeCode().equals("Identifier"), "If a resource has an element named identifier, it must have a type 'Identifier'");
}
rule(errors, IssueType.STRUCTURE, rd.getName(), rd.getRoot().getElementByName(definitions, "text", true, false) == null, "Element named \"text\" not allowed");
rule(errors, IssueType.STRUCTURE, rd.getName(), rd.getRoot().getElementByName(definitions, "contained", true, false) == null, "Element named \"contained\" not allowed");
if (rd.getRoot().getElementByName(definitions, "subject", true, false) != null && rd.getRoot().getElementByName(definitions, "subject", true, false).typeCode().startsWith("Reference"))
rule(errors, IssueType.STRUCTURE, rd.getName(), rd.getSearchParams().containsKey("subject"), "A resource that contains a subject reference must have a search parameter 'subject'");
ElementDefn ped = rd.getRoot().getElementByName(definitions, "patient", true, false);
if (ped != null && ped.typeCode().startsWith("Reference")) {
SearchParameterDefn spd = rd.getSearchParams().get("patient");
if (rule(errors, IssueType.STRUCTURE, rd.getName(), spd != null, "A resource that contains a patient reference must have a search parameter 'patient'"))
rule(errors, IssueType.STRUCTURE, rd.getName(), (spd.getTargets().size() == 1 && spd.getTargets().contains("Patient")) || (ped.getTypes().get(0).getParams().size() == 1 && ped.getTypes().get(0).getParams().get(0).equals("Patient")), "A Patient search parameter must only refer to patient");
}
ElementDefn sed = rd.getRoot().getElementByName(definitions, "subject", true, false);
if (sed != null && sed.typeCode().startsWith("Reference") && sed.typeCode().contains("Patient"))
warning(errors, IssueType.STRUCTURE, rd.getName(), rd.getSearchParams().containsKey("patient"), "A resource that contains a subject that can be a patient reference must have a search parameter 'patient'");
if (rd.getRoot().getElementByName(definitions, "identifier", true, false) != null) {
warning(errors, IssueType.STRUCTURE, rd.getName(), rd.getSearchParams().containsKey("identifier"), "A resource that contains an identifier must have a search parameter 'identifier'");
}
if (rd.getRoot().getElementByName(definitions, "status", true, false) != null) {
// todo: change to a warning post STU3
hint(errors, IssueType.STRUCTURE, rd.getName(), rd.getSearchParams().containsKey("status"), "A resource that contains a status element must have a search parameter 'status'");
}
if (rd.getRoot().getElementByName(definitions, "url", true, false) != null) {
warning(errors, IssueType.STRUCTURE, rd.getName(), rd.getSearchParams().containsKey("url"), "A resource that contains a url element must have a search parameter 'url'");
}
checkSearchParams(errors, rd);
for (Operation op : rd.getOperations()) {
rule(errors, IssueType.STRUCTURE, rd.getName() + "/$" + op.getName(), !parentHasOp(rd.getRoot().typeCode(), op.getName()), "Duplicate Operation Name $" + op.getName() + " on " + rd.getName());
}
for (Compartment c : definitions.getCompartments()) {
if (rule(errors, IssueType.STRUCTURE, rd.getName(), name.equals("Parameters") || c.getResources().containsKey(rd), "Resource not entered in resource map for compartment '" + c.getTitle() + "' (compartments.xml)")) {
String param = c.getResources().get(rd);
if (!Utilities.noString(param)) {
// rule(errors, IssueType.STRUCTURE, parent.getName(), param.equals("{def}") || parent.getSearchParams().containsKey(c.getName()), "Resource "+parent.getName()+" in compartment " +c.getName()+" must have a search parameter named "+c.getName().toLowerCase()+")");
for (String p : param.split("\\|")) {
String pn = p.trim();
if (pn.contains("."))
pn = pn.substring(0, pn.indexOf("."));
rule(errors, IssueType.STRUCTURE, rd.getName(), Utilities.noString(pn) || pn.equals("{def}") || rd.getSearchParams().containsKey(pn), "Resource " + rd.getName() + " in compartment " + c.getName() + ": parameter " + param + " was not found (" + pn + ")");
}
}
}
}
// Remove suppressed messages
List<ValidationMessage> suppressed = new ArrayList<ValidationMessage>();
for (ValidationMessage em : errors) {
if (isSuppressedMessage(em.getDisplay())) {
suppressed.add(em);
}
}
errors.removeAll(suppressed);
// last check: if maturity level is
int warnings = 0;
int hints = 0;
for (ValidationMessage em : errors) {
if (em.getLevel() == IssueSeverity.WARNING)
warnings++;
else if (em.getLevel() == IssueSeverity.INFORMATION)
hints++;
}
boolean ok = warnings == 0 || "0".equals(rd.getFmmLevel());
if (rule(errors, IssueType.STRUCTURE, rd.getName(), ok, "Resource " + rd.getName() + " (FMM=" + rd.getFmmLevel() + ") cannot have an FMM level >1 (" + rd.getFmmLevel() + ") if it has warnings"))
rule(errors, IssueType.STRUCTURE, rd.getName(), vsWarnings == 0 || "0".equals(rd.getFmmLevel()), "Resource " + rd.getName() + " (FMM=" + rd.getFmmLevel() + ") cannot have an FMM level >1 (" + rd.getFmmLevel() + ") if it has linked value set warnings (" + vsWarns.toString() + ")");
ok = hints == 0 || Integer.parseInt(rd.getFmmLevel()) < 3;
rule(errors, IssueType.STRUCTURE, rd.getName(), ok, "Resource " + rd.getName() + " (FMM=" + rd.getFmmLevel() + ") cannot have an FMM level >2 (" + rd.getFmmLevel() + ") if it has informational hints");
// if (isInterface(rd.getRoot().typeCode())) {
// checkInterface(errors, rd, definitions.getBaseResources().get(rd.getRoot().typeCode()));
// }
}
use of org.hl7.fhir.utilities.xml.SchematronWriter.Rule in project kindling by HL7.
the class ValueSetValidator method checkCodeCaseDuplicates.
private void checkCodeCaseDuplicates(List<ValidationMessage> errors, String nameForErrors, CodeSystem cs, Set<String> codes, List<ConceptDefinitionComponent> concepts) {
for (ConceptDefinitionComponent c : concepts) {
if (c.hasCode()) {
String cc = c.getCode().toLowerCase();
rule(errors, IssueType.BUSINESSRULE, getWg(cs) + ":CodeSystem[" + cs.getId() + "].define", !codes.contains(cc), "Value set " + nameForErrors + " (" + cs.getName() + "): Code '" + cc + "' is defined twice, different by case - this is not allowed in a FHIR definition");
if (c.hasConcept())
checkCodeCaseDuplicates(errors, nameForErrors, cs, codes, c.getConcept());
}
}
}
Aggregations