From a2a62d3755ebdef936e745773f11e50a87a2f23f Mon Sep 17 00:00:00 2001 From: Mike Schroeder Date: Tue, 26 Oct 2021 15:13:19 -0500 Subject: [PATCH 1/2] Issue #2832 - enable default profile version configuration Signed-off-by: Mike Schroeder --- docs/src/pages/guides/FHIRServerUsersGuide.md | 61 ++++- .../ibm/fhir/config/FHIRConfiguration.java | 1 + .../ibm/fhir/server/util/FHIRRestHelper.java | 91 +++++-- .../test/MockRegistryResourceProvider.java | 10 +- .../test/ProfileValidationConfigTest.java | 224 +++++++++++++++--- .../fhir-server-config.json | 26 +- 6 files changed, 338 insertions(+), 75 deletions(-) diff --git a/docs/src/pages/guides/FHIRServerUsersGuide.md b/docs/src/pages/guides/FHIRServerUsersGuide.md index 32acc2eb748..e21d862a2cf 100644 --- a/docs/src/pages/guides/FHIRServerUsersGuide.md +++ b/docs/src/pages/guides/FHIRServerUsersGuide.md @@ -3,7 +3,7 @@ layout: post title: IBM FHIR Server User's Guide description: IBM FHIR Server User's Guide Copyright: years 2017, 2021 -lastupdated: "2021-10-21" +lastupdated: "2021-10-26" permalink: /FHIRServerUsersGuide/ --- @@ -803,11 +803,13 @@ The following configuration parameters can be used to specify rules relating to * `fhirServer/resources//profiles/atLeastOne` - this configuration parameter is used to specify a set of profiles, at least one of which a resource must claim conformance to and be successfully validated against in order to be persisted to the FHIR server. * `fhirServer/resources//profiles/notAllowed`- this configuration parameter is used to specify a set of profiles to which a resource is *not allowed* to claim conformance. * `fhirServer/resources//profiles/allowUnknown`- this configuration parameter is used to indicate whether a warning or an error is issued if a profile specified in a resource's `meta.profile`element is not loaded in the FHIR server. The default value is `true`, meaning unknown profiles are allowed to be specified. The profile will be ignored and just a warning will be returned. If set to `false`, this means unknown profiles are not allowed to be specified. An error will be returned and resource validation will fail. +* `fhirServer/resources//profiles/defaultVersions`- this configuration parameter is used to specify which version of a profile will be used during resource validation if the profile specified in a resource's `meta.profile`element does not contain a version. If a default profile version is not configured using this configuration parameter for an asserted profile, the FHIR server will determine the default version to use for validation. Before calling the FHIR validator to validate a resource against the set of profiles specified in its `meta.profile` element that it is claiming conformance to, the following pre-validation will be performed for that set of profiles based on the configuration parameters listed above: -1. If the `fhirServer/resources//profiles/notAllowed` configuration parameter is set to a non-empty list, an error will be returned for any specified profile that is in the list, and validation will fail. -2. If the `fhirServer/resources//profiles/allowUnknown` configuration parameter is set to `false`, an error will be returned for any specified profile that is not loaded in the FHIR server, and validation will fail. -3. If the `fhirServer/resources//profiles/atLeastOne` configuration parameter is set to a non-empty list, an error will be returned if none of the specified profiles is in the list, and validation will fail. +1. If any specified profile does not contain a version, and that profile is in the set of profiles configured to have a default version via the `fhirServer/resources//profiles/defaultVersions` configuration parameter, the default version for that profile will be appended to the profile name, and it is this new profile name containing the version which will be evaluated against in the following steps. +2. If the `fhirServer/resources//profiles/notAllowed` configuration parameter is set to a non-empty list, an error will be returned for any specified profile that is in the list, and validation will fail. +3. If the `fhirServer/resources//profiles/allowUnknown` configuration parameter is set to `false`, an error will be returned for any specified profile that is not loaded in the FHIR server, and validation will fail. +4. If the `fhirServer/resources//profiles/atLeastOne` configuration parameter is set to a non-empty list, an error will be returned if none of the specified profiles is in the list, and validation will fail. If the resource passes pre-validation successfully, it will then be passed to the FHIR validator to validate conformance to the specified set of profiles. @@ -824,7 +826,11 @@ The following example configuration shows how to define these configuration para "http://ibm.com/fhir/profile/partnerA", "http://ibm.com/fhir/profile/partnerB|1.0" ], - "allowUnknown": false + "allowUnknown": false, + "defaultVersions": { + "http://ibm.com/fhir/profile/partnerA": "1.1", + "http://ibm.com/fhir/profile/partnerE": "2.0" + } } }, "Patient": { @@ -834,6 +840,20 @@ The following example configuration shows how to define these configuration para "http://ibm.com/fhir/profile/partnerD|2.0" ] } + }, + "Resource": { + "profiles": { + "notAllowed": [ + "http://ibm.com/fhir/profile/partnerF", + "http://ibm.com/fhir/profile/partnerG|4.0.0", + "http://ibm.com/fhir/profile/partnerH" + ], + "defaultVersions": { + "http://ibm.com/fhir/profile/partnerI": "1.0", + "http://ibm.com/fhir/profile/partnerJ": "3.1.1", + "http://ibm.com/fhir/profile/partnerK": "1.1" + } + } } }, … @@ -842,17 +862,21 @@ The following example configuration shows how to define these configuration para ``` Given this configuration, in order for an `Observation` resource to be persisted to the FHIR server: -* the resource's `meta.profile` element must specify either the `http://ibm.com/fhir/profile/partnerA` profile or the `http://ibm.com/fhir/profile/partnerB|1.0` profile or both, based on the `fhirServer/resources/Observation/profiles/atLeastOne` list. Other profiles may be specified as well assuming they pass the following checks. -* the resource's `meta.profile` element must not specify any profile which is not loaded in the FHIR server, based on the `fhirServer/resources/Observation/profiles/allowUnknown` value of `false` -* the resource must successfully validate against all specified profiles +* The resource's `meta.profile` element must specify either the `http://ibm.com/fhir/profile/partnerA` profile or the `http://ibm.com/fhir/profile/partnerB|1.0` profile or both, based on the `fhirServer/resources/Observation/profiles/atLeastOne` list. Other profiles may be specified as well assuming they pass the following checks. +* The resource's `meta.profile` element must not specify any profile which is not loaded in the FHIR server, based on the `fhirServer/resources/Observation/profiles/allowUnknown` value of `false`. +* The resource must successfully validate against all specified profiles. Note that if the `http://ibm.com/fhir/profile/partnerA` profile is specified, what is actually evaluated against and eventually passed to the FHIR validator is `http://ibm.com/fhir/profile/partnerA|1.1`. This is because the `http://ibm.com/fhir/profile/partnerA` profile is specified in the `fhirServer/resources/Observation/profiles/defaultVersions` set with a default version of `1.1`. In order for a `Patient` resource to be persisted to the FHIR server: -* the resource's `meta.profile` element cannot specify either the `http://ibm.com/fhir/profile/partnerC` profile or the `http://ibm.com/fhir/profile/partnerD|2.0` profile, based on the `fhirServer/resources/Observation/profiles/notAllowed` list +* the resource's `meta.profile` element cannot specify either the `http://ibm.com/fhir/profile/partnerC` profile or the `http://ibm.com/fhir/profile/partnerD|2.0` profile, based on the `fhirServer/resources/Patient/profiles/notAllowed` list * the resource must successfully validate against all specified profiles. Since the `fhirServer/resources/Patient/profiles/allowUnknown` configuration parameter is not specified for `Patient` resources, the default value of `true` is used, meaning a profile that is not loaded in the FHIR server can be specified, and will simply be ignored. -If a profile in either the list specified by the `fhirServer/resources//profiles/atLeastOne` configuration parameter or the list specified by the `fhirServer/resources//profiles/notAllowed` configuration parameter contains a version, for example `http://ibm.com/fhir/profile/partner|1.0`, then a profile of the same name specified in the resource's `meta.profile` element will only be considered a match if it contains exactly the same version. However, if a profile in the lists specified by the configuration parameters does not contain a version, for example `http://ibm.com/fhir/profile/partner`, then a profile of the same name specified in the resource's `meta.profile` element will be considered a match whether it contains a version or not. Using the example configuration above for the `Observation` resource type, if the profile `http://ibm.com/fhir/profile/partnerA|3.2` was specified in a resource's `meta.profile` element then it would be considered a match for the `http://ibm.com/fhir/profile/partnerA` profile specified in the `fhirServer/resources/Observation/profiles/atLeastOne` list. Conversely, if the profile `http://ibm.com/fhir/profile/partnerB` was specified in the resource's `meta.profile` element then it would *not* be considered a match for the `http://ibm.com/fhir/profile/partnerB|1.0` profile specified in the `fhirServer/resources/Observation/profiles/atLeastOne` list. +If a profile in either the list specified by the `fhirServer/resources//profiles/atLeastOne` configuration parameter or the list specified by the `fhirServer/resources//profiles/notAllowed` configuration parameter contains a version, for example `http://ibm.com/fhir/profile/partner|1.0`, then a profile of the same name specified in the resource's `meta.profile` element will only be considered a match if it contains exactly the same version. However, if a profile in the lists specified by the configuration parameters does not contain a version, for example `http://ibm.com/fhir/profile/partner`, then a profile of the same name specified in the resource's `meta.profile` element will be considered a match whether it contains a version or not. + +Keep in mind that a profile specified in the resource's `meta.profile` element could be modified due to the resource's `fhirServer/resources//profiles/defaultVersions` configuration, and it is the modified profile name that is used in the matching process. + +Using the example configuration above for the `Observation` resource type, if the profile `http://ibm.com/fhir/profile/partnerA|3.2` was specified in a resource's `meta.profile` element then it would be considered a match for the `http://ibm.com/fhir/profile/partnerA` profile specified in the `fhirServer/resources/Observation/profiles/atLeastOne` list. Conversely, if the profile `http://ibm.com/fhir/profile/partnerB` was specified in the resource's `meta.profile` element then it would *not* be considered a match for the `http://ibm.com/fhir/profile/partnerB|1.0` profile specified in the `fhirServer/resources/Observation/profiles/atLeastOne` list. The IBM FHIR Server pre-packages all conformance resources from the core specification. @@ -2055,15 +2079,19 @@ This section contains reference information about each of the configuration prop |`fhirServer/resources/Resource/profiles/atLeastOne`|string list|A comma-separated list of profiles, at least one of which must be specified in a resource's `meta.profile` element and successfully validated against in order for a resource to be persisted to the FHIR server. Individual resource types may override this value via `fhirServer/resources//profiles/atLeastOne`. Omitting this property or specifying an empty list is equivalent to not requiring any profile assertions for a resource.| |`fhirServer/resources/Resource/profiles/notAllowed`|string list|A comma-separated list of profiles that are not allowed to be specified in a resource's `meta.profile` element, and thus cannot be validated against in order for a resource to be persisted to the FHIR server. Individual resource types may override this value via `fhirServer/resources//profiles/notAllowed`. Omitting this property or specifying an empty list is equivalent to allowing any profile assertions for a resource.| |`fhirServer/resources/Resource/profiles/allowUnknown`|boolean|Indicates if profiles are allowed to be specified in a resource's `meta.profile` element that are not loaded in the FHIR server. If set to `false`, specifying a profile that is not loaded in the FHIR server results in an error and a resource validation failure. If set to `true`, a warning will be issued and the profile will be ignored. The default value is `true`. Individual resource types may override this value via `fhirServer/resources//profiles/allowUnknown`.| +|`fhirServer/resources/Resource/profiles/defaultVersions`|object|A set of profiles for which default profile versions are specified. If a profile in this set is asserted for a resource, and the asserted profile URL does not include a version, the version specified for the profile in this set will be used during resource validation. In cases where asserted profiles do not include a version, and are not in this set, the FHIR server will determine the default version of the profile to be used during resource validation. Individual resource types may override this set of profiles via `fhirServer/resources//profiles/defaultVersions`.| +|`fhirServer/resources/Resource/profiles/defaultVersions/`|string|The version of the profile definition to use for the profile `` during resource validation when this profile is asserted.| |`fhirServer/resources//interactions`|string list|A list of strings that represent the RESTful interactions (create, read, vread, update, patch, delete, history, and/or search) to support for this resource type. For resources without the property, the value of `fhirServer/resources/Resource/interactions` is used.| |`fhirServer/resources//searchParameters`|object|The set of search parameters to support for this resource type. Global search parameters defined on the `Resource` resource can be overridden on a per-resourceType basis.| |`fhirServer/resources//searchParameters/`|string|The URL of the search parameter definition to use for the search parameter `` on resources of type ``.| |`fhirServer/resources//searchIncludes`|string list|A comma-separated list of \_include values supported for this resource type. An empty list, `[]`, can be used to indicate that no \_include values are supported. For resources without the property, the value of `fhirServer/resources/Resource/searchIncludes` is used.| |`fhirServer/resources//searchRevIncludes`|string list|A comma-separated list of \_revinclude values supported for this resource type. An empty list, `[]`, can be used to indicate that no \_revinclude values are supported. For resources without the property, the value of `fhirServer/resources/Resource/searchRevIncludes` is used.| |`fhirServer/resources//searchParameterCombinations`|string list|A comma-separated list of search parameter combinations supported for this resource type. Each search parameter combination is a string, where a plus sign, `+`, separates the search parameters that can be used in combination. To indicate that searching without any search parameters is allowed, an empty string must be included in the list. Including an asterisk, `*`, in the list indicates support of any search parameter combination. For resources without the property, the value of `fhirServer/resources/Resource/searchParameterCombinations` is used.| -|`fhirServer/resources//profiles/atLeastOne`|string list|A comma-separated list of profiles, at least one of which must be specified in a resource's `meta.profile` element and be successfully validated against in order for a resource of this type to be persisted to the FHIR server. If this property is not specified, or if an empty list is specified, the value of `fhirServer/resources/Resource/profiles/atLeastOne` will be used.| -|`fhirServer/resources//profiles/notAllowed`|string list|A comma-separated list of profiles that are not allowed to be specified in a resource's `meta.profile` element, and thus cannot be validated against in order for a resource to be persisted to the FHIR server. If this property is not specified, or if an empty list is specified, the value of `fhirServer/resources/Resource/profiles/notAllowed` will be used.| +|`fhirServer/resources//profiles/atLeastOne`|string list|A comma-separated list of profiles, at least one of which must be specified in a resource's `meta.profile` element and be successfully validated against in order for a resource of this type to be persisted to the FHIR server. If this property is not specified, the value of `fhirServer/resources/Resource/profiles/atLeastOne` will be used. If an empty list is specified, it is equivalent to not requiring any profile assertions for this resource type.| +|`fhirServer/resources//profiles/notAllowed`|string list|A comma-separated list of profiles that are not allowed to be specified in a resource's `meta.profile` element, and thus cannot be validated against in order for a resource to be persisted to the FHIR server. If this property is not specified, the value of `fhirServer/resources/Resource/profiles/notAllowed` will be used. If an empty list is specified, it is equivalent to allowing any profile assertions for this resource type.| |`fhirServer/resources//profiles/allowUnknown`|boolean|Indicates if profiles are allowed to be specified in a resource's `meta.profile` element that are not loaded in the FHIR server. If set to `false`, specifying a profile that is not loaded in the FHIR server results in an error and a resource validation failure. If set to `true`, a warning will be issued and the profile will be ignored. If this property is not specified, the value of `fhirServer/resources/Resource/profiles/allowUnknown` will be used.| +|`fhirServer/resources//profiles/defaultVersions`|object|A set of profiles for which default profile versions are specified for this resource type. If a profile in this set is asserted for a resource, and the asserted profile URL does not include a version, the version specified for the profile in this set will be used during resource validation. In cases where asserted profiles do not include a version, and are not in this set, the FHIR server will determine the default version of the profile to be used during resource validation. If this property is not specified, the value of `fhirServer/resources/Resource/profiles/defaultVersions` will be used.| +|`fhirServer/resources//profiles/defaultVersions/`|string|The version of the profile definition to use for the profile `` during resource validation when this profile is asserted for this resource type.| |`fhirServer/notifications/common/includeResourceTypes`|string list|A comma-separated list of resource types for which notification event messages should be published.| |`fhirServer/notifications/common/maxNotificationSizeBytes`|integer|The maximum size in bytes of the notification that should be sent| |`fhirServer/notifications/common/maxNotificationSizeBehavior`|string|The behavior of the notification framework when a notification is over the maxNotificationSizeBytes. Valid values are subset and omit| @@ -2212,12 +2240,15 @@ This section contains reference information about each of the configuration prop |`fhirServer/resources/open`|true| |`fhirServer/resources/Resource/interactions`|null (all interactions supported)| |`fhirServer/resources/Resource/searchParameters`|null (all global search parameters supported)| +|`fhirServer/resources/Resource/searchParameters/`|null| |`fhirServer/resources/Resource/searchIncludes`|null (all \_include values supported)| |`fhirServer/resources/Resource/searchRevIncludes`|null (all \_revinclude values supported)| |`fhirServer/resources/Resource/searchParameterCombinations`|null (all search parameter combinations supported)| |`fhirServer/resources/Resource/profiles/atLeastOne`|null (no resource profile assertions required)| |`fhirServer/resources/Resource/profiles/notAllowed`|null (any resource profile assertions allowed)| |`fhirServer/resources/Resource/profiles/allowUnknown`|true| +|`fhirServer/resources/Resource/profiles/defaultVersions`|null (FHIR server determines default versions)| +|`fhirServer/resources/Resource/profiles/defaultVersions/`|null| |`fhirServer/resources//interactions`|null (inherits from `fhirServer/resources/Resource/interactions`)| |`fhirServer/resources//searchParameters`|null (all type-specific search parameters supported)| |`fhirServer/resources//searchParameters/`|null| @@ -2227,6 +2258,8 @@ This section contains reference information about each of the configuration prop |`fhirServer/resources//profiles/atLeastOne`|null (inherits from `fhirServer/resources/Resource/profiles/atLeastOne`)| |`fhirServer/resources//profiles/notAllowed`|null (inherits from `fhirServer/resources/Resource/profiles/notAllowed`)| |`fhirServer/resources//profiles/allowUnknown`|null (inherits from `fhirServer/resources/Resource/profiles/allowUnknown`)| +|`fhirServer/resources//profiles/defaultVersions`|null (inherits from `fhirServer/resources/Resource/profiles/defaultVersions`)| +|`fhirServer/resources//profiles/defaultVersions/`|null| |`fhirServer/notifications/common/includeResourceTypes`|`["*"]`| |`fhirServer/notifications/common/maxNotificationSizeBytes`|1000000| |`fhirServer/notifications/common/maxNotificationSizeBehavior`|subset| @@ -2373,6 +2406,8 @@ must restart the server for that change to take effect. |`fhirServer/resources/Resource/profiles/atLeastOne`|Y|Y| |`fhirServer/resources/Resource/profiles/notAllowed`|Y|Y| |`fhirServer/resources/Resource/profiles/allowUnknown`|Y|Y| +|`fhirServer/resources/Resource/profiles/defaultVersions`|Y|Y| +|`fhirServer/resources/Resource/profiles/defaultVersions/`|Y|Y| |`fhirServer/resources//interactions`|Y|Y| |`fhirServer/resources//searchParameters`|Y|Y| |`fhirServer/resources//searchParameters/`|Y|Y| @@ -2382,6 +2417,8 @@ must restart the server for that change to take effect. |`fhirServer/resources//profiles/atLeastOne`|Y|Y| |`fhirServer/resources//profiles/notAllowed`|Y|Y| |`fhirServer/resources//profiles/allowUnknown`|Y|Y| +|`fhirServer/resources//profiles/defaultVersions`|Y|Y| +|`fhirServer/resources//profiles/defaultVersions/`|Y|Y| |`fhirServer/notifications/common/includeResourceTypes`|N|N| |`fhirServer/notifications/common/maxNotificationSizeBytes`|Y|N| |`fhirServer/notifications/common/maxNotificationSizeBehavior`|Y|N| diff --git a/fhir-config/src/main/java/com/ibm/fhir/config/FHIRConfiguration.java b/fhir-config/src/main/java/com/ibm/fhir/config/FHIRConfiguration.java index 031f7f1fa7f..1cb2ebe917f 100644 --- a/fhir-config/src/main/java/com/ibm/fhir/config/FHIRConfiguration.java +++ b/fhir-config/src/main/java/com/ibm/fhir/config/FHIRConfiguration.java @@ -64,6 +64,7 @@ public class FHIRConfiguration { public static final String PROPERTY_FIELD_RESOURCES_PROFILES_AT_LEAST_ONE = "atLeastOne"; public static final String PROPERTY_FIELD_RESOURCES_PROFILES_NOT_ALLOWED = "notAllowed"; public static final String PROPERTY_FIELD_RESOURCES_PROFILES_ALLOW_UNKNOWN = "allowUnknown"; + public static final String PROPERTY_FIELD_RESOURCES_PROFILES_DEFAULT_VERSIONS = "defaultVersions"; // Auth and security properties public static final String PROPERTY_SECURITY_CORS = "fhirServer/security/cors"; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/util/FHIRRestHelper.java b/fhir-server/src/main/java/com/ibm/fhir/server/util/FHIRRestHelper.java index 42c52ebc46d..e9352f79142 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/util/FHIRRestHelper.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/util/FHIRRestHelper.java @@ -44,6 +44,7 @@ import com.ibm.fhir.config.FHIRConfiguration; import com.ibm.fhir.config.FHIRRequestContext; import com.ibm.fhir.config.PropertyGroup; +import com.ibm.fhir.config.PropertyGroup.PropertyEntry; import com.ibm.fhir.core.FHIRConstants; import com.ibm.fhir.core.HTTPHandlingPreference; import com.ibm.fhir.core.context.FHIRPagingContext; @@ -61,11 +62,13 @@ import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.resource.SearchParameter; import com.ibm.fhir.model.resource.StructureDefinition; +import com.ibm.fhir.model.type.Canonical; import com.ibm.fhir.model.type.Code; import com.ibm.fhir.model.type.CodeableConcept; import com.ibm.fhir.model.type.DateTime; import com.ibm.fhir.model.type.Decimal; import com.ibm.fhir.model.type.Extension; +import com.ibm.fhir.model.type.Meta; import com.ibm.fhir.model.type.Reference; import com.ibm.fhir.model.type.UnsignedInt; import com.ibm.fhir.model.type.Uri; @@ -2577,6 +2580,9 @@ private List validateResource(Resource resource) throws FHIRValidationExc Set notAllowedProfiles = new HashSet<>(); Set notAllowedProfilesWithoutVersion = new HashSet<>(); boolean allowUnknown; + Map defaultVersions = new HashMap<>(); + boolean defaultVersionsSpecified = false; + Resource resourceToValidate = resource; // Retrieve the profile configuration try { @@ -2648,6 +2654,39 @@ private List validateResource(Resource resource) throws FHIRValidationExc allowUnknown = FHIRConfigHelper.getBooleanProperty(defaultProfileConfigPath.toString() + FHIRConfiguration.PROPERTY_FIELD_RESOURCES_PROFILES_ALLOW_UNKNOWN, Boolean.TRUE); } + + if (log.isLoggable(Level.FINER)) { + log.finer("Allow unknown: " + allowUnknown); + } + + // Get the 'defaultVersions' entries + PropertyGroup resourceSpecificDefaultVersionsGroup = + FHIRConfigHelper.getPropertyGroup(resourceSpecificProfileConfigPath.toString() + + FHIRConfiguration.PROPERTY_FIELD_RESOURCES_PROFILES_DEFAULT_VERSIONS); + if (resourceSpecificDefaultVersionsGroup != null) { + defaultVersionsSpecified = true; + for (PropertyEntry entry : resourceSpecificDefaultVersionsGroup.getProperties()) { + defaultVersions.put(entry.getName(), (String) entry.getValue()); + } + } else { + PropertyGroup allResourceDefaultVersionsGroup = + FHIRConfigHelper.getPropertyGroup(defaultProfileConfigPath.toString() + + FHIRConfiguration.PROPERTY_FIELD_RESOURCES_PROFILES_DEFAULT_VERSIONS); + if (allResourceDefaultVersionsGroup != null) { + defaultVersionsSpecified = true; + for (PropertyEntry entry : allResourceDefaultVersionsGroup.getProperties()) { + defaultVersions.put(entry.getName(), (String) entry.getValue()); + } + } + } + + if (log.isLoggable(Level.FINER)) { + log.finer("Default profile versions: ["); + for (String profile : defaultVersions.keySet()) { + log.finer(" " + profile + " : " + defaultVersions.get(profile)); + } + log.finer("]"); + } } catch (Exception e) { throw new FHIRValidationException("Error retrieving profile configuration.", e); } @@ -2655,10 +2694,13 @@ private List validateResource(Resource resource) throws FHIRValidationExc // Validate asserted profiles if necessary: // - if 'atLeastOne' is a non-empty list OR // - if 'notAllowed' is a non-empty list OR - // - if 'allowUnknown' is set to false - if (!notAllowedProfiles.isEmpty() || !atLeastOneProfiles.isEmpty() || !allowUnknown) { + // - if 'allowUnknown' is set to false OR + // - if 'defaultVersions' exists (empty or not) + if (!notAllowedProfiles.isEmpty() || !atLeastOneProfiles.isEmpty() || !allowUnknown || defaultVersionsSpecified) { List issues = new ArrayList<>(); boolean validProfileFound = false; + boolean defaultVersionUsed = false; + List defaultVersionAssertedProfiles = new ArrayList<>();; // Get the profiles asserted for this resource List resourceAssertedProfiles = ProfileSupport.getResourceAssertedProfiles(resource); @@ -2666,22 +2708,30 @@ private List validateResource(Resource resource) throws FHIRValidationExc log.fine("Asserted profiles: " + resourceAssertedProfiles); } - // Validate the asserted profiles. - // For 'atLeastOne' profiles, check that at least one asserted profile is in the list of 'atLeastOne' profiles. - // For 'notAllowed' profiles, check that no asserted profile is in the list of 'notAllowed' profiles. - // If an 'atLeastOne' or 'notAllowed' profile specifies a version, an asserted profile must be an exact match. - // If an 'atLeastOne' or 'notAllowed' profile does not specify a version, any asserted profile of the same name - // will be a match regardless if it specifies a version or not. + // Validate the asserted profiles for (String resourceAssertedProfile : resourceAssertedProfiles) { - if (!notAllowedProfiles.isEmpty() || !atLeastOneProfiles.isEmpty()) { - // Check if asserted profile contains a version - String strippedAssertedProfile = null; - int index = resourceAssertedProfile.indexOf("|"); - if (index != -1) { - strippedAssertedProfile = resourceAssertedProfile.substring(0, index); + // Check if asserted profile contains a version + String strippedAssertedProfile = null; + int index = resourceAssertedProfile.indexOf("|"); + if (index != -1) { + strippedAssertedProfile = resourceAssertedProfile.substring(0, index); + } else { + // Check if assertedProfile has a default version + String defaultVersion = defaultVersions.get(resourceAssertedProfile); + if (defaultVersion != null) { + defaultVersionUsed = true; + strippedAssertedProfile = resourceAssertedProfile; + resourceAssertedProfile = resourceAssertedProfile + "|" + defaultVersion; } + } + defaultVersionAssertedProfiles.add(Canonical.of(resourceAssertedProfile)); - // Look for exact match or match after stripping version from asserted profile + if (!notAllowedProfiles.isEmpty() || !atLeastOneProfiles.isEmpty()) { + // For 'atLeastOne' profiles, check that at least one asserted profile is in the list of 'atLeastOne' profiles. + // For 'notAllowed' profiles, check that no asserted profile is in the list of 'notAllowed' profiles. + // If an 'atLeastOne' or 'notAllowed' profile specifies a version, an asserted profile must be an exact match. + // If an 'atLeastOne' or 'notAllowed' profile does not specify a version, any asserted profile of the same name + // will be a match regardless if it specifies a version or not. if (notAllowedProfiles.contains(resourceAssertedProfile) || notAllowedProfilesWithoutVersion.contains(strippedAssertedProfile)) { // For 'notAllowed' profiles, a match means an invalid profile was found @@ -2724,9 +2774,16 @@ private List validateResource(Resource resource) throws FHIRValidationExc if (!issues.isEmpty()) { return issues; } - } - return validator.validate(resource); + // If any asserted profiles have a default version specified, make a copy of the + // resource with the new asserted profile values and validate against the copy. + if (defaultVersionUsed) { + Meta metaCopy = resource.getMeta().toBuilder().profile(defaultVersionAssertedProfiles).build(); + resourceToValidate = resource.toBuilder().meta(metaCopy).build(); + } + } + + return validator.validate(resourceToValidate); } @Override diff --git a/fhir-server/src/test/java/com/ibm/fhir/server/test/MockRegistryResourceProvider.java b/fhir-server/src/test/java/com/ibm/fhir/server/test/MockRegistryResourceProvider.java index 155b18796d7..db792f03b1a 100644 --- a/fhir-server/src/test/java/com/ibm/fhir/server/test/MockRegistryResourceProvider.java +++ b/fhir-server/src/test/java/com/ibm/fhir/server/test/MockRegistryResourceProvider.java @@ -154,15 +154,15 @@ public Resource getResource() { */ @Override public FHIRRegistryResource getRegistryResource(Class resourceType, String url, String version) { - if (url.equals("profile1")) { + if (url.equals("profile1") && (version == null || version.equals("1"))) { return profile1; - } else if (url.equals("profile2")) { + } else if (url.equals("profile2") && (version == null || version.equals("1"))) { return profile2; - } else if (url.equals("profile3")) { + } else if (url.equals("profile3") && (version == null || version.equals("1"))) { return profile3; - } else if (url.equals("profile4")) { + } else if (url.equals("profile4") && (version == null || version.equals("1"))) { return profile4; - } else if (url.equals("profile5")) { + } else if (url.equals("profile5") && (version == null || version.equals("1"))) { return profile5; } else { return null; diff --git a/fhir-server/src/test/java/com/ibm/fhir/server/test/ProfileValidationConfigTest.java b/fhir-server/src/test/java/com/ibm/fhir/server/test/ProfileValidationConfigTest.java index 95a7ebf6434..409263d6550 100644 --- a/fhir-server/src/test/java/com/ibm/fhir/server/test/ProfileValidationConfigTest.java +++ b/fhir-server/src/test/java/com/ibm/fhir/server/test/ProfileValidationConfigTest.java @@ -25,6 +25,7 @@ import com.ibm.fhir.model.resource.Bundle; import com.ibm.fhir.model.resource.CarePlan; import com.ibm.fhir.model.resource.Condition; +import com.ibm.fhir.model.resource.Device; import com.ibm.fhir.model.resource.Encounter; import com.ibm.fhir.model.resource.OperationOutcome; import com.ibm.fhir.model.resource.OperationOutcome.Issue; @@ -198,16 +199,18 @@ public void testCreateWithRequiredProfileSpecifiedButNoVersion() throws Exceptio } /** - * Test a create with an unsupported profile specified. + * Test a create with an unsupported profile specified and allow unknown true. */ @Test - public void testCreateWithUnsupportedProfileSpecified() throws Exception { - Patient patient = Patient.builder() + public void testCreateWithUnsupportedProfileSpecifiedAndAllowUnknownTrue() throws Exception { + CarePlan carePlan = CarePlan.builder() .meta(Meta.builder() - .profile(Canonical.of("profile1"), Canonical.of("profile9")) + .profile(Canonical.of("profile7")) .build()) - .generalPractitioner(Reference.builder() - .reference(string("Practitioner/1")) + .status(CarePlanStatus.COMPLETED) + .intent(CarePlanIntent.PLAN) + .subject(Reference.builder() + .reference(string("urn:3")) .build()) .text(Narrative.builder() .div(Xhtml.of("
Some narrative
")) @@ -219,13 +222,14 @@ public void testCreateWithUnsupportedProfileSpecified() throws Exception { FHIRRequestContext.get().setOriginalRequestUri("test"); FHIRRequestContext.get().setReturnPreference(HTTPReturnPreference.OPERATION_OUTCOME); try { - helper.doCreate("Patient", patient, null, true); + FHIRRestOperationResponse response = helper.doCreate("CarePlan", carePlan, null, true); + List issues = response.getOperationOutcome().getIssue(); + assertEquals(issues.size(), 1); + assertEquals(issues.get(0).getDetails().getText().getValue(), "Profile 'profile7' is not supported"); + assertEquals(issues.get(0).getSeverity(), IssueSeverity.WARNING); + assertEquals(issues.get(0).getCode(), IssueType.NOT_SUPPORTED); + } catch (Exception e) { fail(); - } catch (FHIRValidationException e) { - // Validate results. - // Profile assertion validation successful. - // Expected FHIRValidator error due to profile not actually being loaded. - assertEquals(e.getMessage(), "An error occurred during validation"); } } @@ -268,7 +272,7 @@ public void testCreateWithRequiredProfileSpecified() throws Exception { public void testCreateWithRequiredProfileWithVersionSpecified() throws Exception { Patient patient = Patient.builder() .meta(Meta.builder() - .profile(Canonical.of("profile1|3")) + .profile(Canonical.of("profile1|1")) .build()) .generalPractitioner(Reference.builder() .reference(string("Practitioner/1")) @@ -559,17 +563,19 @@ public void testUpdateWithNonRequiredProfileSpecified() throws Exception { } /** - * Test an update with an unsupported profile specified. + * Test an update with an unsupported profile specified and allow unknown true. */ @Test - public void testUpdateWithUnsupportedProfileSpecified() throws Exception { - Patient patient = Patient.builder() + public void testUpdateWithUnsupportedProfileSpecifiedAndAllowUnknownTrue() throws Exception { + CarePlan carePlan = CarePlan.builder() .id("1") .meta(Meta.builder() - .profile(Canonical.of("profile1"), Canonical.of("profile9")) + .profile(Canonical.of("profile7")) .build()) - .generalPractitioner(Reference.builder() - .reference(string("Practitioner/1")) + .status(CarePlanStatus.COMPLETED) + .intent(CarePlanIntent.PLAN) + .subject(Reference.builder() + .reference(string("urn:3")) .build()) .text(Narrative.builder() .div(Xhtml.of("
Some narrative
")) @@ -581,13 +587,14 @@ public void testUpdateWithUnsupportedProfileSpecified() throws Exception { FHIRRequestContext.get().setOriginalRequestUri("test"); FHIRRequestContext.get().setReturnPreference(HTTPReturnPreference.OPERATION_OUTCOME); try { - helper.doUpdate("Patient", "1", patient, null, null, false, true); + FHIRRestOperationResponse response = helper.doUpdate("CarePlan", "1", carePlan, null, null, false, true); + List issues = response.getOperationOutcome().getIssue(); + assertEquals(issues.size(), 1); + assertEquals(issues.get(0).getDetails().getText().getValue(), "Profile 'profile7' is not supported"); + assertEquals(issues.get(0).getSeverity(), IssueSeverity.WARNING); + assertEquals(issues.get(0).getCode(), IssueType.NOT_SUPPORTED); + } catch (Exception e) { fail(); - } catch (FHIRValidationException e) { - // Validate results. - // Profile assertion validation successful. - // Expected FHIRValidator error due to profile not actually being loaded. - assertEquals(e.getMessage(), "An error occurred during validation"); } } @@ -728,17 +735,19 @@ public void testBundleWithNonRequiredProfileSpecified() throws Exception { } /** - * Test bundle with an unsupported profile specified. + * Test bundle with an unsupported profile specified and allow unknown true. */ @Test - public void testBundleWithUnsupportedProfileSpecified() throws Exception { - Patient patient = Patient.builder() + public void testBundleWithUnsupportedProfileSpecifiedAndAllowUnknownTrue() throws Exception { + CarePlan carePlan = CarePlan.builder() .id("1") .meta(Meta.builder() - .profile(Canonical.of("profile1"), Canonical.of("profile9")) + .profile(Canonical.of("profile7")) .build()) - .generalPractitioner(Reference.builder() - .reference(string("Practitioner/1")) + .status(CarePlanStatus.COMPLETED) + .intent(CarePlanIntent.PLAN) + .subject(Reference.builder() + .reference(string("urn:3")) .build()) .text(Narrative.builder() .div(Xhtml.of("
Some narrative
")) @@ -748,10 +757,10 @@ public void testBundleWithUnsupportedProfileSpecified() throws Exception { Bundle.Entry.Request bundleEntryRequest = Bundle.Entry.Request.builder() .method(HTTPVerb.PUT) - .url(Uri.of("Patient/1")) + .url(Uri.of("CarePlan/1")) .build(); Bundle.Entry bundleEntry = Bundle.Entry.builder() - .resource(patient) + .resource(carePlan) .request(bundleEntryRequest) .build(); @@ -765,13 +774,15 @@ public void testBundleWithUnsupportedProfileSpecified() throws Exception { FHIRRequestContext.get().setOriginalRequestUri("test"); FHIRRequestContext.get().setReturnPreference(HTTPReturnPreference.OPERATION_OUTCOME); try { - helper.doBundle(requestBundle, false); + Bundle responseBundle = helper.doBundle(requestBundle, false); + assertEquals(responseBundle.getEntry().get(0).getResource().as(OperationOutcome.class), ALL_OK); + List issues = responseBundle.getEntry().get(0).getResponse().getOutcome().as(OperationOutcome.class).getIssue(); + assertEquals(issues.size(), 1); + assertEquals(issues.get(0).getDetails().getText().getValue(), "Profile 'profile7' is not supported"); + assertEquals(issues.get(0).getSeverity(), IssueSeverity.WARNING); + assertEquals(issues.get(0).getCode(), IssueType.NOT_SUPPORTED); + } catch (Exception e) { fail(); - } catch (FHIRValidationException e) { - // Validate results. - // Profile assertion validation successful. - // Expected FHIRValidator error due to profile not actually being loaded. - assertEquals(e.getMessage(), "An error occurred during validation"); } } @@ -1698,4 +1709,139 @@ public void testBundleWithUnsupportedProfileSpecifiedAndAllowUnknownFalse() thro } } + /** + * Test a create with a valid default version required profile specified which is unsupported. + */ + @Test + public void testCreateWithRequiredProfileAndDefaultVersionSpecifiedAndUnsupported() throws Exception { + Patient patient = Patient.builder() + .meta(Meta.builder() + .profile(Canonical.of("profile3")) + .build()) + .generalPractitioner(Reference.builder() + .reference(string("Practitioner/1")) + .build()) + .text(Narrative.builder() + .div(Xhtml.of("
Some narrative
")) + .status(NarrativeStatus.GENERATED) + .build()) + .build(); + + // Process request + FHIRRequestContext.get().setOriginalRequestUri("test"); + FHIRRequestContext.get().setReturnPreference(HTTPReturnPreference.OPERATION_OUTCOME); + try { + FHIRRestOperationResponse response = helper.doCreate("Patient", patient, null, true); + List issues = response.getOperationOutcome().getIssue(); + assertEquals(issues.size(), 1); + assertEquals(issues.get(0).getDetails().getText().getValue(), "Profile 'profile3|2' is not supported"); + assertEquals(issues.get(0).getSeverity(), IssueSeverity.WARNING); + assertEquals(issues.get(0).getCode(), IssueType.NOT_SUPPORTED); + } catch (Exception e) { + fail(); + } + } + + /** + * Test a create with a valid default version required profile specified for versioned profile. + */ + @Test + public void testCreateWithRequiredProfileAndDefaultVersionSpecifiedForVersionedProfile() throws Exception { + Device device = Device.builder() + .meta(Meta.builder() + .profile(Canonical.of("profile10")) + .build()) + .patient(Reference.builder() + .reference(string("urn:3")) + .build()) + .text(Narrative.builder() + .div(Xhtml.of("
Some narrative
")) + .status(NarrativeStatus.GENERATED) + .build()) + .build(); + + // Process request + FHIRRequestContext.get().setOriginalRequestUri("test"); + FHIRRequestContext.get().setReturnPreference(HTTPReturnPreference.OPERATION_OUTCOME); + try { + FHIRRestOperationResponse response = helper.doCreate("Device", device, null, true); + List issues = response.getOperationOutcome().getIssue(); + assertEquals(issues.size(), 1); + assertEquals(issues.get(0).getDetails().getText().getValue(), "Profile 'profile10|1' is not supported"); + assertEquals(issues.get(0).getSeverity(), IssueSeverity.WARNING); + assertEquals(issues.get(0).getCode(), IssueType.NOT_SUPPORTED); + } catch (Exception e) { + fail(); + } + } + + /** + * Test a create with a default version not allowed profile specified for versioned profile. + */ + @Test + public void testCreateWithNonAllowedProfileAndDefaultVersionSpecifiedForVersionedProfile() throws Exception { + Device device = Device.builder() + .meta(Meta.builder() + .profile(Canonical.of("profile10"), Canonical.of("profile11")) + .build()) + .patient(Reference.builder() + .reference(string("urn:3")) + .build()) + .text(Narrative.builder() + .div(Xhtml.of("
Some narrative
")) + .status(NarrativeStatus.GENERATED) + .build()) + .build(); + + // Process request + FHIRRequestContext.get().setOriginalRequestUri("test"); + FHIRRequestContext.get().setReturnPreference(HTTPReturnPreference.OPERATION_OUTCOME); + try { + helper.doCreate("Device", device, null, true); + fail(); + } catch (FHIROperationException e) { + // Validate results + List issues = e.getIssues(); + assertEquals(issues.size(), 1); + assertEquals(issues.get(0).getDetails().getText().getValue(), + "A profile was specified which is not allowed. Resources of type 'Device' are not allowed to declare conformance to any of the following profiles: [profile11|1]"); + assertEquals(issues.get(0).getSeverity(), IssueSeverity.ERROR); + assertEquals(issues.get(0).getCode(), IssueType.BUSINESS_RULE); + } + } + + /** + * Test a create with a default version unsupported profile and allow unknown false. + */ + @Test + public void testCreateWithUnsupportedProfileDefaultVersionAndAllowUnknownFalse() throws Exception { + Condition condition = Condition.builder() + .meta(Meta.builder() + .profile(Canonical.of("profile4")) + .build()) + .text(Narrative.builder() + .div(Xhtml.of("
Some narrative
")) + .status(NarrativeStatus.GENERATED) + .build()) + .subject(Reference.builder() + .reference(string("urn:3")) + .build()) + .build(); + + // Process request + FHIRRequestContext.get().setOriginalRequestUri("test"); + FHIRRequestContext.get().setReturnPreference(HTTPReturnPreference.OPERATION_OUTCOME); + try { + helper.doCreate("Condition", condition, null, true); + fail(); + } catch (FHIROperationException e) { + // Validate results + List issues = e.getIssues(); + assertEquals(issues.size(), 1); + assertEquals(issues.get(0).getDetails().getText().getValue(), "Profile 'profile4|2' is not supported"); + assertEquals(issues.get(0).getSeverity(), IssueSeverity.ERROR); + assertEquals(issues.get(0).getCode(), IssueType.NOT_SUPPORTED); + } + } + } \ No newline at end of file diff --git a/fhir-server/src/test/resources/config/profileValidationConfigTest/fhir-server-config.json b/fhir-server/src/test/resources/config/profileValidationConfigTest/fhir-server-config.json index 0fbe440eaec..f5e48a6296d 100644 --- a/fhir-server/src/test/resources/config/profileValidationConfigTest/fhir-server-config.json +++ b/fhir-server/src/test/resources/config/profileValidationConfigTest/fhir-server-config.json @@ -26,7 +26,26 @@ "atLeastOne": [ ], "notAllowed": [ - ] + ], + "allowUnknown": false, + "defaultVersions": { + "profile4":"2" + } + } + }, + "Device": { + "profiles": { + "atLeastOne": [ + "profile10|1" + ], + "notAllowed": [ + "profile11|1" + ], + "defaultVersions": { + "profile10":"1", + "profile11":"1" + }, + "allowUnknown": true } }, "Encounter": { @@ -44,7 +63,10 @@ "notAllowed": [ "profile5" ], - "allowUnknown": true + "allowUnknown": true, + "defaultVersions": { + "profile3":"2" + } } }, "Procedure": { From 59ac8d49ca144b0f815a4789188d592fe7ce1a15 Mon Sep 17 00:00:00 2001 From: Mike Schroeder Date: Wed, 27 Oct 2021 08:36:16 -0500 Subject: [PATCH 2/2] Issue #2832 - address review comments Signed-off-by: Mike Schroeder --- docs/src/pages/guides/FHIRServerUsersGuide.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/src/pages/guides/FHIRServerUsersGuide.md b/docs/src/pages/guides/FHIRServerUsersGuide.md index e21d862a2cf..6d3bee61e36 100644 --- a/docs/src/pages/guides/FHIRServerUsersGuide.md +++ b/docs/src/pages/guides/FHIRServerUsersGuide.md @@ -801,9 +801,9 @@ For example, you can configure a set of FHIRPath Constraints to run for resource The following configuration parameters can be used to specify rules relating to the set of profiles that are specified in a resource's `meta.profile` element: * `fhirServer/resources//profiles/atLeastOne` - this configuration parameter is used to specify a set of profiles, at least one of which a resource must claim conformance to and be successfully validated against in order to be persisted to the FHIR server. -* `fhirServer/resources//profiles/notAllowed`- this configuration parameter is used to specify a set of profiles to which a resource is *not allowed* to claim conformance. -* `fhirServer/resources//profiles/allowUnknown`- this configuration parameter is used to indicate whether a warning or an error is issued if a profile specified in a resource's `meta.profile`element is not loaded in the FHIR server. The default value is `true`, meaning unknown profiles are allowed to be specified. The profile will be ignored and just a warning will be returned. If set to `false`, this means unknown profiles are not allowed to be specified. An error will be returned and resource validation will fail. -* `fhirServer/resources//profiles/defaultVersions`- this configuration parameter is used to specify which version of a profile will be used during resource validation if the profile specified in a resource's `meta.profile`element does not contain a version. If a default profile version is not configured using this configuration parameter for an asserted profile, the FHIR server will determine the default version to use for validation. +* `fhirServer/resources//profiles/notAllowed` - this configuration parameter is used to specify a set of profiles to which a resource is *not allowed* to claim conformance. +* `fhirServer/resources//profiles/allowUnknown` - this configuration parameter is used to indicate whether a warning or an error is issued if a profile specified in a resource's `meta.profile` element is not loaded in the FHIR server. The default value is `true`, meaning unknown profiles are allowed to be specified. The profile will be ignored and just a warning will be returned. If set to `false`, this means unknown profiles are not allowed to be specified. An error will be returned and resource validation will fail. +* `fhirServer/resources//profiles/defaultVersions` - this configuration parameter is used to specify which version of a profile will be used during resource validation if the profile specified in a resource's `meta.profile` element does not contain a version. If a default profile version is not configured using this configuration parameter for an asserted profile, the FHIR server will determine the default version to use for validation. Before calling the FHIR validator to validate a resource against the set of profiles specified in its `meta.profile` element that it is claiming conformance to, the following pre-validation will be performed for that set of profiles based on the configuration parameters listed above: 1. If any specified profile does not contain a version, and that profile is in the set of profiles configured to have a default version via the `fhirServer/resources//profiles/defaultVersions` configuration parameter, the default version for that profile will be appended to the profile name, and it is this new profile name containing the version which will be evaluated against in the following steps. @@ -863,18 +863,19 @@ The following example configuration shows how to define these configuration para Given this configuration, in order for an `Observation` resource to be persisted to the FHIR server: * The resource's `meta.profile` element must specify either the `http://ibm.com/fhir/profile/partnerA` profile or the `http://ibm.com/fhir/profile/partnerB|1.0` profile or both, based on the `fhirServer/resources/Observation/profiles/atLeastOne` list. Other profiles may be specified as well assuming they pass the following checks. +* The resource's `meta.profile` element must not specify any profile which is specified in the `fhirServer/resources/Resource/profiles/notAllowed` list. Since the `notAllowed` property is not specified in the `fhirServer/resources/Observation/profiles` section, it is inherited from the `fhirServer/resources/Resource/profiles` section if specified there. * The resource's `meta.profile` element must not specify any profile which is not loaded in the FHIR server, based on the `fhirServer/resources/Observation/profiles/allowUnknown` value of `false`. * The resource must successfully validate against all specified profiles. Note that if the `http://ibm.com/fhir/profile/partnerA` profile is specified, what is actually evaluated against and eventually passed to the FHIR validator is `http://ibm.com/fhir/profile/partnerA|1.1`. This is because the `http://ibm.com/fhir/profile/partnerA` profile is specified in the `fhirServer/resources/Observation/profiles/defaultVersions` set with a default version of `1.1`. In order for a `Patient` resource to be persisted to the FHIR server: -* the resource's `meta.profile` element cannot specify either the `http://ibm.com/fhir/profile/partnerC` profile or the `http://ibm.com/fhir/profile/partnerD|2.0` profile, based on the `fhirServer/resources/Patient/profiles/notAllowed` list -* the resource must successfully validate against all specified profiles. - -Since the `fhirServer/resources/Patient/profiles/allowUnknown` configuration parameter is not specified for `Patient` resources, the default value of `true` is used, meaning a profile that is not loaded in the FHIR server can be specified, and will simply be ignored. +* The resource's `meta.profile` element is not required to specify any profile since the `atLeastOne` property is not specified in the `fhirServer/resources/Patient/profiles` section, nor is it specified in the `fhirServer/resources/Resource/profiles` section, where the property would be inherited from if specified. Any valid `Patient` profile may be specified assuming it passes the following checks. +* The resource's `meta.profile` element cannot specify either the `http://ibm.com/fhir/profile/partnerC` profile or the `http://ibm.com/fhir/profile/partnerD|2.0` profile, based on the `fhirServer/resources/Patient/profiles/notAllowed` list. +* The resource's `meta.profile` element may specify a profile which is not loaded in the FHIR server. Based on the absence of the `allowUnknown` property in the `fhirServer/resources/Patient/profiles` section, as well as the absence of that property in the `fhirServer/resources/Resource/profiles` section (where the property would be inherited from if specified), the default value of `true` is used. This means unknown profiles (not loaded in the FHIR server) will be allowed and will simply be ignored. +* The resource must successfully validate against all specified profiles. Note that since the `defaultVersions` property is not specified in the `fhirServer/resources/Patient/profiles` section, this property will be inherited from the `fhirServer/resources/Resource/profiles/defaultVersions` property. So if a profile is specified in the resource's `meta.profile` element that is in the set of `defaultVersions` profiles, what will actually be evaluated against and eventually passed to the FHIR validator is the original profile name with its specified default version appended to it. If a profile in either the list specified by the `fhirServer/resources//profiles/atLeastOne` configuration parameter or the list specified by the `fhirServer/resources//profiles/notAllowed` configuration parameter contains a version, for example `http://ibm.com/fhir/profile/partner|1.0`, then a profile of the same name specified in the resource's `meta.profile` element will only be considered a match if it contains exactly the same version. However, if a profile in the lists specified by the configuration parameters does not contain a version, for example `http://ibm.com/fhir/profile/partner`, then a profile of the same name specified in the resource's `meta.profile` element will be considered a match whether it contains a version or not. -Keep in mind that a profile specified in the resource's `meta.profile` element could be modified due to the resource's `fhirServer/resources//profiles/defaultVersions` configuration, and it is the modified profile name that is used in the matching process. +Keep in mind that a profile name specified in the resource's `meta.profile` element could be modified due to the resource's `fhirServer/resources//profiles/defaultVersions` configuration. It is this modified profile name that is used in the matching process and in the resource's validation, but only for those purposes. The `meta.profile` element of the original resource itself is not updated with the modified profile name. Using the example configuration above for the `Observation` resource type, if the profile `http://ibm.com/fhir/profile/partnerA|3.2` was specified in a resource's `meta.profile` element then it would be considered a match for the `http://ibm.com/fhir/profile/partnerA` profile specified in the `fhirServer/resources/Observation/profiles/atLeastOne` list. Conversely, if the profile `http://ibm.com/fhir/profile/partnerB` was specified in the resource's `meta.profile` element then it would *not* be considered a match for the `http://ibm.com/fhir/profile/partnerB|1.0` profile specified in the `fhirServer/resources/Observation/profiles/atLeastOne` list.