Credit for #1065 and forward-port fix to R4 provider

This commit is contained in:
jamesagnew 2019-01-09 07:19:55 -05:00
parent f8b232bb67
commit b3c9b32db4
9 changed files with 15576 additions and 13793 deletions

View File

@ -28,6 +28,7 @@ import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r4.model.OperationDefinition.OperationDefinitionParameterComponent; import org.hl7.fhir.r4.model.OperationDefinition.OperationDefinitionParameterComponent;
import org.hl7.fhir.r4.model.OperationDefinition.OperationKind; import org.hl7.fhir.r4.model.OperationDefinition.OperationKind;
import org.hl7.fhir.r4.model.OperationDefinition.OperationParameterUse; import org.hl7.fhir.r4.model.OperationDefinition.OperationParameterUse;
import org.hl7.fhir.r4.model.codesystems.UnknownContentCode;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -71,13 +72,15 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProvider.class); private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ServerCapabilityStatementProvider.class);
private boolean myCache = true; private boolean myCache = true;
private volatile CapabilityStatement myCapabilityStatement; private volatile CapabilityStatement myCapabilityStatement;
private IdentityHashMap<SearchMethodBinding, String> myNamedSearchMethodBindingToName;
private HashMap<String, List<SearchMethodBinding>> mySearchNameToBindings;
private IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName; private IdentityHashMap<OperationMethodBinding, String> myOperationBindingToName;
private HashMap<String, List<OperationMethodBinding>> myOperationNameToBindings; private HashMap<String, List<OperationMethodBinding>> myOperationNameToBindings;
private String myPublisher = "Not provided"; private String myPublisher = "Not provided";
private Callable<RestulfulServerConfiguration> myServerConfiguration; private Callable<RestulfulServerConfiguration> myServerConfiguration;
/** /**
* No-arg constructor and seetter so that the ServerConfirmanceProvider can be Spring-wired with the RestfulService avoiding the potential reference cycle that would happen. * No-arg constructor and setter so that the ServerConformanceProvider can be Spring-wired with the RestfulService avoiding the potential reference cycle that would happen.
*/ */
public ServerCapabilityStatementProvider() { public ServerCapabilityStatementProvider() {
super(); super();
@ -119,12 +122,12 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
} }
private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings() { private Map<String, List<BaseMethodBinding<?>>> collectMethodBindings() {
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<>(); Map<String, List<BaseMethodBinding<?>>> resourceToMethods = new TreeMap<String, List<BaseMethodBinding<?>>>();
for (ResourceBinding next : getServerConfiguration().getResourceBindings()) { for (ResourceBinding next : getServerConfiguration().getResourceBindings()) {
String resourceName = next.getResourceName(); String resourceName = next.getResourceName();
for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) { for (BaseMethodBinding<?> nextMethodBinding : next.getMethodBindings()) {
if (resourceToMethods.containsKey(resourceName) == false) { if (resourceToMethods.containsKey(resourceName) == false) {
resourceToMethods.put(resourceName, new ArrayList<>()); resourceToMethods.put(resourceName, new ArrayList<BaseMethodBinding<?>>());
} }
resourceToMethods.get(resourceName).add(nextMethodBinding); resourceToMethods.get(resourceName).add(nextMethodBinding);
} }
@ -151,6 +154,17 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
return DateTimeType.now(); return DateTimeType.now();
} }
private String createNamedQueryName(SearchMethodBinding searchMethodBinding) {
StringBuilder retVal = new StringBuilder();
if (searchMethodBinding.getResourceName() != null) {
retVal.append(searchMethodBinding.getResourceName());
}
retVal.append("-query-");
retVal.append(searchMethodBinding.getQueryName());
return retVal.toString();
}
private String createOperationName(OperationMethodBinding theMethodBinding) { private String createOperationName(OperationMethodBinding theMethodBinding) {
StringBuilder retVal = new StringBuilder(); StringBuilder retVal = new StringBuilder();
if (theMethodBinding.getResourceName() != null) { if (theMethodBinding.getResourceName() != null) {
@ -230,14 +244,15 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
Set<String> operationNames = new HashSet<>(); Set<String> operationNames = new HashSet<>();
Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings(); Map<String, List<BaseMethodBinding<?>>> resourceToMethods = collectMethodBindings();
for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) for (Entry<String, List<BaseMethodBinding<?>>> nextEntry : resourceToMethods.entrySet()) {
if (nextEntry.getKey().isEmpty() == false) { if (nextEntry.getKey().isEmpty() == false) {
Set<TypeRestfulInteraction> resourceOps = new HashSet<TypeRestfulInteraction>(); Set<TypeRestfulInteraction> resourceOps = new HashSet<>();
CapabilityStatementRestResourceComponent resource = rest.addResource(); CapabilityStatementRestResourceComponent resource = rest.addResource();
String resourceName = nextEntry.getKey(); String resourceName = nextEntry.getKey();
RuntimeResourceDefinition def = getServerConfiguration().getFhirContext().getResourceDefinition(resourceName); RuntimeResourceDefinition def = getServerConfiguration().getFhirContext().getResourceDefinition(resourceName);
resource.getTypeElement().setValue(def.getName()); resource.getTypeElement().setValue(def.getName());
resource.getProfileElement().setValue(def.getResourceProfile(serverBase)); resource.getProfileElement().setValue((def.getResourceProfile(serverBase)));
TreeSet<String> includes = new TreeSet<>(); TreeSet<String> includes = new TreeSet<>();
@ -293,13 +308,21 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
checkBindingForSystemOps(rest, systemOps, nextMethodBinding); checkBindingForSystemOps(rest, systemOps, nextMethodBinding);
if (nextMethodBinding instanceof SearchMethodBinding) { if (nextMethodBinding instanceof SearchMethodBinding) {
handleSearchMethodBinding(rest, resource, resourceName, def, includes, (SearchMethodBinding) nextMethodBinding); SearchMethodBinding methodBinding = (SearchMethodBinding) nextMethodBinding;
if (methodBinding.getQueryName() != null) {
String queryName = myNamedSearchMethodBindingToName.get(methodBinding);
if (operationNames.add(queryName)) {
rest.addOperation().setName(methodBinding.getQueryName()).setDefinition(("OperationDefinition/" + queryName));
}
} else {
handleNamelessSearchMethodBinding(rest, resource, resourceName, def, includes, (SearchMethodBinding) nextMethodBinding);
}
} else if (nextMethodBinding instanceof OperationMethodBinding) { } else if (nextMethodBinding instanceof OperationMethodBinding) {
OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding; OperationMethodBinding methodBinding = (OperationMethodBinding) nextMethodBinding;
String opName = myOperationBindingToName.get(methodBinding); String opName = myOperationBindingToName.get(methodBinding);
if (operationNames.add(opName)) { if (operationNames.add(opName)) {
// Only add each operation (by name) once // Only add each operation (by name) once
rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition("OperationDefinition/" + opName); rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition(("OperationDefinition/" + opName));
} }
} }
@ -334,19 +357,19 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
String opName = myOperationBindingToName.get(methodBinding); String opName = myOperationBindingToName.get(methodBinding);
if (operationNames.add(opName)) { if (operationNames.add(opName)) {
ourLog.debug("Found bound operation: {}", opName); ourLog.debug("Found bound operation: {}", opName);
rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition("OperationDefinition/" + opName); rest.addOperation().setName(methodBinding.getName().substring(1)).setDefinition(("OperationDefinition/" + opName));
} }
} }
} }
} }
}
myCapabilityStatement = retVal; myCapabilityStatement = retVal;
return retVal; return retVal;
} }
private void handleNamelessSearchMethodBinding(CapabilityStatementRestComponent rest, CapabilityStatementRestResourceComponent resource, String resourceName, RuntimeResourceDefinition def, TreeSet<String> includes,
private void handleSearchMethodBinding(CapabilityStatementRestComponent rest, CapabilityStatementRestResourceComponent resource, String resourceName, RuntimeResourceDefinition def, TreeSet<String> includes, SearchMethodBinding searchMethodBinding) {
SearchMethodBinding searchMethodBinding) {
includes.addAll(searchMethodBinding.getIncludes()); includes.addAll(searchMethodBinding.getIncludes());
List<IParameter> params = searchMethodBinding.getParameters(); List<IParameter> params = searchMethodBinding.getParameters();
@ -434,6 +457,8 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
@Initialize @Initialize
public void initializeOperations() { public void initializeOperations() {
myNamedSearchMethodBindingToName = new IdentityHashMap<>();
mySearchNameToBindings = new HashMap<>();
myOperationBindingToName = new IdentityHashMap<>(); myOperationBindingToName = new IdentityHashMap<>();
myOperationNameToBindings = new HashMap<>(); myOperationNameToBindings = new HashMap<>();
@ -452,9 +477,23 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
myOperationBindingToName.put(methodBinding, name); myOperationBindingToName.put(methodBinding, name);
if (myOperationNameToBindings.containsKey(name) == false) { if (myOperationNameToBindings.containsKey(name) == false) {
myOperationNameToBindings.put(name, new ArrayList<OperationMethodBinding>()); myOperationNameToBindings.put(name, new ArrayList<>());
} }
myOperationNameToBindings.get(name).add(methodBinding); myOperationNameToBindings.get(name).add(methodBinding);
} else if (nextMethodBinding instanceof SearchMethodBinding) {
SearchMethodBinding methodBinding = (SearchMethodBinding) nextMethodBinding;
if (myNamedSearchMethodBindingToName.containsKey(methodBinding)) {
continue;
}
String name = createNamedQueryName(methodBinding);
ourLog.debug("Detected named query: {}", name);
myNamedSearchMethodBindingToName.put(methodBinding, name);
if (!mySearchNameToBindings.containsKey(name)) {
mySearchNameToBindings.put(name, new ArrayList<>());
}
mySearchNameToBindings.get(name).add(methodBinding);
} }
} }
} }
@ -465,11 +504,69 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
if (theId == null || theId.hasIdPart() == false) { if (theId == null || theId.hasIdPart() == false) {
throw new ResourceNotFoundException(theId); throw new ResourceNotFoundException(theId);
} }
List<OperationMethodBinding> sharedDescriptions = myOperationNameToBindings.get(theId.getIdPart()); List<OperationMethodBinding> operationBindings = myOperationNameToBindings.get(theId.getIdPart());
if (sharedDescriptions == null || sharedDescriptions.isEmpty()) { if (operationBindings != null && !operationBindings.isEmpty()) {
throw new ResourceNotFoundException(theId); return readOperationDefinitionForOperation(operationBindings);
}
List<SearchMethodBinding> searchBindings = mySearchNameToBindings.get(theId.getIdPart());
if (searchBindings != null && !searchBindings.isEmpty()) {
return readOperationDefinitionForNamedSearch(searchBindings);
}
throw new ResourceNotFoundException(theId);
}
private OperationDefinition readOperationDefinitionForNamedSearch(List<SearchMethodBinding> bindings) {
OperationDefinition op = new OperationDefinition();
op.setStatus(PublicationStatus.ACTIVE);
op.setKind(OperationKind.QUERY);
op.setAffectsState(false);
op.setSystem(false);
op.setType(false);
op.setInstance(false);
Set<String> inParams = new HashSet<>();
for (SearchMethodBinding binding : bindings) {
if (isNotBlank(binding.getDescription())) {
op.setDescription(binding.getDescription());
}
if (isBlank(binding.getResourceProviderResourceName())) {
op.setSystem(true);
} else {
op.setType(true);
op.addResourceElement().setValue(binding.getResourceProviderResourceName());
}
op.setCode(binding.getQueryName());
for (IParameter nextParamUntyped : binding.getParameters()) {
if (nextParamUntyped instanceof SearchParameter) {
SearchParameter nextParam = (SearchParameter) nextParamUntyped;
if (!inParams.add(nextParam.getName())) {
continue;
}
OperationDefinitionParameterComponent param = op.addParameter();
param.setUse(OperationParameterUse.IN);
param.setType("string");
param.getSearchTypeElement().setValueAsString(nextParam.getParamType().getCode());
param.setMin(nextParam.isRequired() ? 1 : 0);
param.setMax("1");
param.setName(nextParam.getName());
}
}
if (isBlank(op.getName())) {
if (isNotBlank(op.getDescription())) {
op.setName(op.getDescription());
} else {
op.setName(op.getCode());
}
}
} }
return op;
}
private OperationDefinition readOperationDefinitionForOperation(List<OperationMethodBinding> bindings) {
OperationDefinition op = new OperationDefinition(); OperationDefinition op = new OperationDefinition();
op.setStatus(PublicationStatus.ACTIVE); op.setStatus(PublicationStatus.ACTIVE);
op.setKind(OperationKind.OPERATION); op.setKind(OperationKind.OPERATION);
@ -480,10 +577,10 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
op.setType(false); op.setType(false);
op.setInstance(false); op.setInstance(false);
Set<String> inParams = new HashSet<String>(); Set<String> inParams = new HashSet<>();
Set<String> outParams = new HashSet<String>(); Set<String> outParams = new HashSet<>();
for (OperationMethodBinding sharedDescription : sharedDescriptions) { for (OperationMethodBinding sharedDescription : bindings) {
if (isNotBlank(sharedDescription.getDescription())) { if (isNotBlank(sharedDescription.getDescription())) {
op.setDescription(sharedDescription.getDescription()); op.setDescription(sharedDescription.getDescription());
} }
@ -569,8 +666,9 @@ public class ServerCapabilityStatementProvider implements IServerConformanceProv
* See the class documentation for an important note if you are extending this class * See the class documentation for an important note if you are extending this class
* </p> * </p>
*/ */
public void setCache(boolean theCache) { public ServerCapabilityStatementProvider setCache(boolean theCache) {
myCache = theCache; myCache = theCache;
return this;
} }
@Override @Override

View File

@ -15,8 +15,6 @@ import org.hl7.fhir.r4.utils.FHIRPathEngine.ExpressionNodeWithOffset;
import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext; import org.hl7.fhir.r4.utils.FHIRPathEngine.IEvaluationContext;
import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.Utilities;
import javafx.scene.Parent;
public class LiquidEngine implements IEvaluationContext { public class LiquidEngine implements IEvaluationContext {
public interface ILiquidEngineIcludeResolver { public interface ILiquidEngineIcludeResolver {

View File

@ -0,0 +1,183 @@
package ca.uhn.fhir.rest.server;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.annotation.Description;
import ca.uhn.fhir.rest.annotation.*;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.SortSpec;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
import ca.uhn.fhir.rest.param.*;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Practitioner;
import java.util.Set;
// import ca.uhn.fhir.model.dstu.resource.Binary;
// import ca.uhn.fhir.model.dstu2.resource.Bundle;
// import ca.uhn.fhir.model.api.Bundle;
public class PatientResourceProvider implements IResourceProvider
{
@Override
public Class<Patient> getResourceType() {
return Patient.class;
}
@Search()
public IBundleProvider search(
javax.servlet.http.HttpServletRequest theServletRequest,
@Description(shortDefinition="The resource identity")
@OptionalParam(name="_id")
StringAndListParam theId,
@Description(shortDefinition="The resource language")
@OptionalParam(name="_language")
StringAndListParam theResourceLanguage,
@Description(shortDefinition="Search the contents of the resource's data using a fulltext search")
@OptionalParam(name=Constants.PARAM_CONTENT)
StringAndListParam theFtContent,
@Description(shortDefinition="Search the contents of the resource's narrative using a fulltext search")
@OptionalParam(name=Constants.PARAM_TEXT)
StringAndListParam theFtText,
@Description(shortDefinition="Search for resources which have the given tag")
@OptionalParam(name=Constants.PARAM_TAG)
TokenAndListParam theSearchForTag,
@Description(shortDefinition="Search for resources which have the given security labels")
@OptionalParam(name=Constants.PARAM_SECURITY)
TokenAndListParam theSearchForSecurity,
@Description(shortDefinition="Search for resources which have the given profile")
@OptionalParam(name=Constants.PARAM_PROFILE)
UriAndListParam theSearchForProfile,
@Description(shortDefinition="A patient identifier")
@OptionalParam(name="identifier")
TokenAndListParam theIdentifier,
@Description(shortDefinition="A portion of either family or given name of the patient")
@OptionalParam(name="name")
StringAndListParam theName,
@Description(shortDefinition="A portion of the family name of the patient")
@OptionalParam(name="family")
StringAndListParam theFamily,
@Description(shortDefinition="A portion of the given name of the patient")
@OptionalParam(name="given")
StringAndListParam theGiven,
@Description(shortDefinition="A portion of either family or given name using some kind of phonetic matching algorithm")
@OptionalParam(name="phonetic")
StringAndListParam thePhonetic,
@Description(shortDefinition="The value in any kind of telecom details of the patient")
@OptionalParam(name="telecom")
TokenAndListParam theTelecom,
@Description(shortDefinition="A value in a phone contact")
@OptionalParam(name="phone")
TokenAndListParam thePhone,
@Description(shortDefinition="A value in an email contact")
@OptionalParam(name="email")
TokenAndListParam theEmail,
@Description(shortDefinition="An address in any kind of address/part of the patient")
@OptionalParam(name="address")
StringAndListParam theAddress,
@Description(shortDefinition="A city specified in an address")
@OptionalParam(name="address-city")
StringAndListParam theAddress_city,
@Description(shortDefinition="A state specified in an address")
@OptionalParam(name="address-state")
StringAndListParam theAddress_state,
@Description(shortDefinition="A postalCode specified in an address")
@OptionalParam(name="address-postalcode")
StringAndListParam theAddress_postalcode,
@Description(shortDefinition="A country specified in an address")
@OptionalParam(name="address-country")
StringAndListParam theAddress_country,
@Description(shortDefinition="A use code specified in an address")
@OptionalParam(name="address-use")
TokenAndListParam theAddress_use,
@Description(shortDefinition="Gender of the patient")
@OptionalParam(name="gender")
TokenAndListParam theGender,
@Description(shortDefinition="Language code (irrespective of use value)")
@OptionalParam(name="language")
TokenAndListParam theLanguage,
@Description(shortDefinition="The patient's date of birth")
@OptionalParam(name="birthdate")
DateRangeParam theBirthdate,
@Description(shortDefinition="The organization at which this person is a patient")
@OptionalParam(name="organization", targetTypes={ Organization.class } )
ReferenceAndListParam theOrganization,
@Description(shortDefinition="Patient's nominated care provider, could be a care manager, not the organization that manages the record")
@OptionalParam(name="careprovider", targetTypes={ Organization.class , Practitioner.class } )
ReferenceAndListParam theCareprovider,
@Description(shortDefinition="Whether the patient record is active")
@OptionalParam(name="active")
TokenAndListParam theActive,
@Description(shortDefinition="The species for animal patients")
@OptionalParam(name="animal-species")
TokenAndListParam theAnimal_species,
@Description(shortDefinition="The breed for animal patients")
@OptionalParam(name="animal-breed")
TokenAndListParam theAnimal_breed,
@Description(shortDefinition="All patients linked to the given patient")
@OptionalParam(name="link", targetTypes={ Patient.class } )
ReferenceAndListParam theLink,
@Description(shortDefinition="This patient has been marked as deceased, or as a death date entered")
@OptionalParam(name="deceased")
TokenAndListParam theDeceased,
@Description(shortDefinition="The date of death has been provided and satisfies this search value")
@OptionalParam(name="deathdate")
DateRangeParam theDeathdate,
@IncludeParam(reverse=true)
Set<Include> theRevIncludes,
@Description(shortDefinition="Only return resources which were last updated as specified by the given range")
@OptionalParam(name="_lastUpdated")
DateRangeParam theLastUpdated,
@IncludeParam(allow= {
"Patient:careprovider" , "Patient:link" , "Patient:organization" , "Patient:careprovider" , "Patient:link" , "Patient:organization" , "Patient:careprovider" , "Patient:link" , "Patient:organization" , "*"
})
Set<Include> theIncludes,
@Sort
SortSpec theSort,
@Count
Integer theCount
) {
return null;
}
}

View File

@ -1,90 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2"> <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">
<sch:ns prefix="f" uri="http://hl7.org/fhir"/> <sch:ns prefix="f" uri="http://hl7.org/fhir"/>
<sch:ns prefix="h" uri="http://www.w3.org/1999/xhtml"/> <sch:ns prefix="h" uri="http://www.w3.org/1999/xhtml"/>
<!-- <!--
This file contains just the constraints for the resource CapabilityStatement This file contains just the constraints for the resource CapabilityStatement
It is provided for documentation purposes. When actually validating, It is provided for documentation purposes. When actually validating,
always use fhir-invariants.sch (because of the way containment works) always use fhir-invariants.sch (because of the way containment works)
Alternatively you can use this file to build a smaller version of Alternatively you can use this file to build a smaller version of
fhir-invariants.sch (the contents are identical; only include those fhir-invariants.sch (the contents are identical; only include those
resources relevant to your implementation). resources relevant to your implementation).
--> -->
<sch:pattern> <sch:pattern>
<sch:title>Global</sch:title> <sch:title>Global</sch:title>
<sch:rule context="f:extension"> <sch:rule context="f:extension">
<sch:assert test="exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])">ext-1: Must have either extensions or value[x], not both</sch:assert> <sch:assert test="exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])">ext-1: Must have either extensions or value[x], not both</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:modifierExtension"> <sch:rule context="f:modifierExtension">
<sch:assert test="exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])">ext-1: Must have either extensions or value[x], not both</sch:assert> <sch:assert test="exists(f:extension)!=exists(f:*[starts-with(local-name(.), 'value')])">ext-1: Must have either extensions or value[x], not both</sch:assert>
</sch:rule> </sch:rule>
</sch:pattern> </sch:pattern>
<sch:pattern> <sch:pattern>
<sch:title>Global 1</sch:title> <sch:title>Global 1</sch:title>
<sch:rule context="f:*"> <sch:rule context="f:*">
<sch:assert test="@value|f:*|h:div">global-1: All FHIR elements must have a @value or children</sch:assert> <sch:assert test="@value|f:*|h:div">global-1: All FHIR elements must have a @value or children</sch:assert>
</sch:rule> </sch:rule>
</sch:pattern> </sch:pattern>
<sch:pattern> <sch:pattern>
<sch:title>CapabilityStatement</sch:title> <sch:title>CapabilityStatement</sch:title>
<sch:rule context="f:CapabilityStatement"> <sch:rule context="f:CapabilityStatement">
<sch:assert test="not(parent::f:contained and f:contained)">dom-2: If the resource is contained in another resource, it SHALL NOT contain nested Resources</sch:assert> <sch:assert test="not(parent::f:contained and f:contained)">dom-2: If the resource is contained in another resource, it SHALL NOT contain nested Resources</sch:assert>
<sch:assert test="not(exists(f:contained/*/f:meta/f:versionId)) and not(exists(f:contained/*/f:meta/f:lastUpdated))">dom-4: If a resource is contained in another resource, it SHALL NOT have a meta.versionId or a meta.lastUpdated</sch:assert> <sch:assert test="not(exists(f:contained/*/f:meta/f:versionId)) and not(exists(f:contained/*/f:meta/f:lastUpdated))">dom-4: If a resource is contained in another resource, it SHALL NOT have a meta.versionId or a meta.lastUpdated</sch:assert>
<sch:assert test="not(exists(for $contained in f:contained return $contained[not(parent::*/descendant::f:reference/@value=concat('#', $contained/*/id/@value) or descendant::f:reference[@value='#'])]))">dom-3: If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource or SHALL refer to the containing resource</sch:assert> <sch:assert test="not(exists(for $contained in f:contained return $contained[not(parent::*/descendant::f:reference/@value=concat('#', $contained/*/id/@value) or descendant::f:reference[@value='#'])]))">dom-3: If the resource is contained in another resource, it SHALL be referred to from elsewhere in the resource or SHALL refer to the containing resource</sch:assert>
<sch:assert test="not(exists(f:contained/*/f:meta/f:security))">dom-5: If a resource is contained in another resource, it SHALL NOT have a security label</sch:assert> <sch:assert test="not(exists(f:contained/*/f:meta/f:security))">dom-5: If a resource is contained in another resource, it SHALL NOT have a security label</sch:assert>
<sch:assert test="count(f:document[f:mode/@value='producer'])=count(distinct-values(f:document[f:mode/@value='producer']/f:profile/f:reference/@value)) and count(f:document[f:mode/@value='consumer'])=count(distinct-values(f:document[f:mode/@value='consumer']/f:profile/f:reference/@value))">cpb-7: The set of documents must be unique by the combination of profile and mode.</sch:assert> <sch:assert test="count(f:document[f:mode/@value='producer'])=count(distinct-values(f:document[f:mode/@value='producer']/f:profile/f:reference/@value)) and count(f:document[f:mode/@value='consumer'])=count(distinct-values(f:document[f:mode/@value='consumer']/f:profile/f:reference/@value))">cpb-7: The set of documents must be unique by the combination of profile and mode.</sch:assert>
<sch:assert test="not(f:kind/@value='instance') or (not(exists(f:implementation)) and not(exists(f:software)))">cpb-16: If kind = requirements, implementation and software must be absent</sch:assert> <sch:assert test="not(f:kind/@value='requirements') or (not(exists(f:implementation)) and not(exists(f:software)))">cpb-16: If kind = requirements, implementation and software must be absent</sch:assert>
<sch:assert test=" not(f:kind/@value='instance') or (not(exists(f:implementation)) and exists(f:software))">cpb-15: If kind = capability, implementation must be absent, software must be present</sch:assert> <sch:assert test=" not(f:kind/@value='capability') or (not(exists(f:implementation)) and exists(f:software))">cpb-15: If kind = capability, implementation must be absent, software must be present</sch:assert>
<sch:assert test="not(exists(f:messaging/f:endpoint)) or f:kind/@value = 'instance'">cpb-3: Messaging end-point is required (and is only permitted) when a statement is for an implementation.</sch:assert> <sch:assert test="not(exists(f:messaging/f:endpoint)) or f:kind/@value = 'instance'">cpb-3: Messaging end-point is required (and is only permitted) when a statement is for an implementation.</sch:assert>
<sch:assert test="not(f:kind/@value='instance') or exists(f:implementation)">cpb-14: If kind = instance, implementation must be present and software may be present</sch:assert> <sch:assert test="not(f:kind/@value='instance') or exists(f:implementation)">cpb-14: If kind = instance, implementation must be present and software may be present</sch:assert>
<sch:assert test="count(f:software | f:implementation | f:description) &gt; 0">cpb-2: A Capability Statement SHALL have at least one of description, software, or implementation element.</sch:assert> <sch:assert test="count(f:software | f:implementation | f:description) &gt; 0">cpb-2: A Capability Statement SHALL have at least one of description, software, or implementation element.</sch:assert>
<sch:assert test="exists(f:rest) or exists(f:messaging) or exists(f:document)">cpb-1: A Capability Statement SHALL have at least one of REST, messaging or document element.</sch:assert> <sch:assert test="exists(f:rest) or exists(f:messaging) or exists(f:document)">cpb-1: A Capability Statement SHALL have at least one of REST, messaging or document element.</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:text/h:div"> <sch:rule context="f:CapabilityStatement/f:text/h:div">
<sch:assert test="not(descendant-or-self::*[not(local-name(.)=('a', 'abbr', 'acronym', 'b', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'dfn', 'div', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var'))]) and not(descendant-or-self::*/@*[not(name(.)=('abbr', 'accesskey', 'align', 'alt', 'axis', 'bgcolor', 'border', 'cellhalign', 'cellpadding', 'cellspacing', 'cellvalign', 'char', 'charoff', 'charset', 'cite', 'class', 'colspan', 'compact', 'coords', 'dir', 'frame', 'headers', 'height', 'href', 'hreflang', 'hspace', 'id', 'lang', 'longdesc', 'name', 'nowrap', 'rel', 'rev', 'rowspan', 'rules', 'scope', 'shape', 'span', 'src', 'start', 'style', 'summary', 'tabindex', 'title', 'type', 'valign', 'value', 'vspace', 'width'))])">txt-1: The narrative SHALL contain only the basic html formatting elements and attributes described in chapters 7-11 (except section 4 of chapter 9) and 15 of the HTML 4.0 standard, &lt;a&gt; elements (either name or href), images and internally contained style attributes</sch:assert> <sch:assert test="not(descendant-or-self::*[not(local-name(.)=('a', 'abbr', 'acronym', 'b', 'big', 'blockquote', 'br', 'caption', 'cite', 'code', 'col', 'colgroup', 'dd', 'dfn', 'div', 'dl', 'dt', 'em', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'i', 'img', 'li', 'ol', 'p', 'pre', 'q', 'samp', 'small', 'span', 'strong', 'sub', 'sup', 'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'tt', 'ul', 'var'))]) and not(descendant-or-self::*/@*[not(name(.)=('abbr', 'accesskey', 'align', 'alt', 'axis', 'bgcolor', 'border', 'cellhalign', 'cellpadding', 'cellspacing', 'cellvalign', 'char', 'charoff', 'charset', 'cite', 'class', 'colspan', 'compact', 'coords', 'dir', 'frame', 'headers', 'height', 'href', 'hreflang', 'hspace', 'id', 'lang', 'longdesc', 'name', 'nowrap', 'rel', 'rev', 'rowspan', 'rules', 'scope', 'shape', 'span', 'src', 'start', 'style', 'summary', 'tabindex', 'title', 'type', 'valign', 'value', 'vspace', 'width'))])">txt-1: The narrative SHALL contain only the basic html formatting elements and attributes described in chapters 7-11 (except section 4 of chapter 9) and 15 of the HTML 4.0 standard, &lt;a&gt; elements (either name or href), images and internally contained style attributes</sch:assert>
<sch:assert test="descendant::text()[normalize-space(.)!=''] or descendant::h:img[@src]">txt-2: The narrative SHALL have some non-whitespace content</sch:assert> <sch:assert test="descendant::text()[normalize-space(.)!=''] or descendant::h:img[@src]">txt-2: The narrative SHALL have some non-whitespace content</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:contact/f:telecom"> <sch:rule context="f:CapabilityStatement/f:contact/f:telecom">
<sch:assert test="not(exists(f:value)) or exists(f:system)">cpt-2: A system is required if a value is provided.</sch:assert> <sch:assert test="not(exists(f:value)) or exists(f:system)">cpt-2: A system is required if a value is provided.</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:contact/f:telecom/f:period"> <sch:rule context="f:CapabilityStatement/f:contact/f:telecom/f:period">
<sch:assert test="not(exists(f:start/@value)) or not(exists(f:end/@value)) or (xs:dateTime(f:start/@value) &lt;= xs:dateTime(f:end/@value))">per-1: If present, start SHALL have a lower value than end</sch:assert> <sch:assert test="not(exists(f:start/@value)) or not(exists(f:end/@value)) or (xs:dateTime(f:start/@value) &lt;= xs:dateTime(f:end/@value))">per-1: If present, start SHALL have a lower value than end</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:useContext/f:valueQuantity"> <sch:rule context="f:CapabilityStatement/f:useContext/f:valueQuantity">
<sch:assert test="not(exists(f:code)) or exists(f:system)">qty-3: If a code for the unit is present, the system SHALL also be present</sch:assert> <sch:assert test="not(exists(f:code)) or exists(f:system)">qty-3: If a code for the unit is present, the system SHALL also be present</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:useContext/f:valueRange"> <sch:rule context="f:CapabilityStatement/f:useContext/f:valueRange">
<sch:assert test="not(exists(f:low/f:value/@value)) or not(exists(f:high/f:value/@value)) or (number(f:low/f:value/@value) &lt;= number(f:high/f:value/@value))">rng-2: If present, low SHALL have a lower value than high</sch:assert> <sch:assert test="not(exists(f:low/f:value/@value)) or not(exists(f:high/f:value/@value)) or (number(f:low/f:value/@value) &lt;= number(f:high/f:value/@value))">rng-2: If present, low SHALL have a lower value than high</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:useContext/f:valueRange/f:low"> <sch:rule context="f:CapabilityStatement/f:useContext/f:valueRange/f:low">
<sch:assert test="not(exists(f:code)) or exists(f:system)">qty-3: If a code for the unit is present, the system SHALL also be present</sch:assert> <sch:assert test="not(exists(f:code)) or exists(f:system)">qty-3: If a code for the unit is present, the system SHALL also be present</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:useContext/f:valueRange/f:high"> <sch:rule context="f:CapabilityStatement/f:useContext/f:valueRange/f:high">
<sch:assert test="not(exists(f:code)) or exists(f:system)">qty-3: If a code for the unit is present, the system SHALL also be present</sch:assert> <sch:assert test="not(exists(f:code)) or exists(f:system)">qty-3: If a code for the unit is present, the system SHALL also be present</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:useContext/f:valueReference"> <sch:rule context="f:CapabilityStatement/f:useContext/f:valueReference">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a contained resource if a local reference is provided</sch:assert> <sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a contained resource if a local reference is provided</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:useContext/f:valueReference/f:identifier/f:period"> <sch:rule context="f:CapabilityStatement/f:useContext/f:valueReference/f:identifier/f:period">
<sch:assert test="not(exists(f:start/@value)) or not(exists(f:end/@value)) or (xs:dateTime(f:start/@value) &lt;= xs:dateTime(f:end/@value))">per-1: If present, start SHALL have a lower value than end</sch:assert> <sch:assert test="not(exists(f:start/@value)) or not(exists(f:end/@value)) or (xs:dateTime(f:start/@value) &lt;= xs:dateTime(f:end/@value))">per-1: If present, start SHALL have a lower value than end</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:useContext/f:valueReference/f:identifier/f:assigner"> <sch:rule context="f:CapabilityStatement/f:useContext/f:valueReference/f:identifier/f:assigner">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a contained resource if a local reference is provided</sch:assert> <sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a contained resource if a local reference is provided</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:implementation/f:custodian"> <sch:rule context="f:CapabilityStatement/f:implementation/f:custodian">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a contained resource if a local reference is provided</sch:assert> <sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a contained resource if a local reference is provided</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:implementation/f:custodian/f:identifier/f:period"> <sch:rule context="f:CapabilityStatement/f:implementation/f:custodian/f:identifier/f:period">
<sch:assert test="not(exists(f:start/@value)) or not(exists(f:end/@value)) or (xs:dateTime(f:start/@value) &lt;= xs:dateTime(f:end/@value))">per-1: If present, start SHALL have a lower value than end</sch:assert> <sch:assert test="not(exists(f:start/@value)) or not(exists(f:end/@value)) or (xs:dateTime(f:start/@value) &lt;= xs:dateTime(f:end/@value))">per-1: If present, start SHALL have a lower value than end</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:implementation/f:custodian/f:identifier//f:assigner"> <sch:rule context="f:CapabilityStatement/f:implementation/f:custodian/f:identifier//f:assigner">
<sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a contained resource if a local reference is provided</sch:assert> <sch:assert test="not(starts-with(f:reference/@value, '#')) or exists(ancestor::*[self::f:entry or self::f:parameter]/f:resource/f:*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')]|/*/f:contained/f:*[f:id/@value=substring-after(current()/f:reference/@value, '#')])">ref-1: SHALL have a contained resource if a local reference is provided</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:rest"> <sch:rule context="f:CapabilityStatement/f:rest">
<sch:assert test="count(f:resource)=count(distinct-values(f:resource/f:type/@value))">cpb-9: A given resource can only be described once per RESTful mode.</sch:assert> <sch:assert test="count(f:resource)=count(distinct-values(f:resource/f:type/@value))">cpb-9: A given resource can only be described once per RESTful mode.</sch:assert>
</sch:rule> </sch:rule>
<sch:rule context="f:CapabilityStatement/f:rest/f:resource"> <sch:rule context="f:CapabilityStatement/f:rest/f:resource">
<sch:assert test="count(f:searchParam)=count(distinct-values(f:searchParam/f:name/@value))">cpb-12: Search parameter names must be unique in the context of a resource.</sch:assert> <sch:assert test="count(f:searchParam)=count(distinct-values(f:searchParam/f:name/@value))">cpb-12: Search parameter names must be unique in the context of a resource.</sch:assert>
</sch:rule> </sch:rule>
</sch:pattern> </sch:pattern>
</sch:schema> </sch:schema>

View File

@ -485,6 +485,10 @@
<developer> <developer>
<id>Cory00</id> <id>Cory00</id>
</developer> </developer>
<developer>
<id>srdo</id>
<name>Stig Døssing</name>
</developer>
</developers> </developers>
<licenses> <licenses>

View File

@ -251,6 +251,10 @@
AuthorizationInterceptor is now able to authorize DELETE operations performed via a AuthorizationInterceptor is now able to authorize DELETE operations performed via a
transaction operation. Previously these were always denied. transaction operation. Previously these were always denied.
</action> </action>
<action type="add" issue="1065">
OperationDefinitions are now created for named queries in server
module. Thanks to Stig Døssing for the pull request!
</action>
</release> </release>
<release version="3.6.0" date="2018-11-12" description="Food"> <release version="3.6.0" date="2018-11-12" description="Food">
<action type="add"> <action type="add">