From 3ed0b8bea26ac1287302f4bedc3d62c99c42c681 Mon Sep 17 00:00:00 2001 From: Lee Surprenant Date: Wed, 2 Feb 2022 08:27:49 -0500 Subject: [PATCH] issue #3242 - scope search-system and search-history for fhirVersion 1. introduce enums and utilities for working with resource type names and fhirVersion values ('4.0' and '4.3') 2. use those to provide a better abstraction for working with the fhir-server-config `fhirServer/resources` property group (ResourcesConfigAdapter) 3. move fhirVersion MIME-type parameter processing into a new JAX-RS RequestFilter FHIRVersionRequestFilter and update tests accordingly 4. update FHIRPersistenceUtil.parseSystemHistoryParameters to take into account the requested fhirVersion and scope the HistoryContext appropriately 5. update SearchUtil.parseQueryParameters to take into account the requested fhirVersion and scope the SearchContext appropriately Signed-off-by: Lee Surprenant --- .../com/ibm/fhir/config/FHIRConfigHelper.java | 48 +- .../java/com/ibm/fhir/config/Interaction.java | 36 + .../fhir/config/ResourcesConfigAdapter.java | 112 ++ .../test/ResourcesConfigAdapterTest.java | 53 + .../java/com/ibm/fhir/core/FHIRMediaType.java | 56 +- .../com/ibm/fhir/core/FHIRVersionParam.java | 52 + .../com/ibm/fhir/core/ResourceTypeName.java | 1312 +++++++++++++++++ .../fhir/core/util/ResourceTypeHelper.java | 122 ++ .../util/test/ResourceTypeHelperTest.java | 57 + .../persistence/util/FHIRPersistenceUtil.java | 45 +- .../util/FHIRPersistenceUtilTest.java | 78 +- .../com/ibm/fhir/search/util/SearchUtil.java | 82 +- .../test/CompartmentParseQueryParmsTest.java | 3 +- .../server/test/CapabilitiesVersionTest.java | 42 +- .../com/ibm/fhir/server/resources/Batch.java | 5 +- .../fhir/server/resources/Capabilities.java | 19 +- .../com/ibm/fhir/server/resources/Create.java | 4 +- .../com/ibm/fhir/server/resources/Delete.java | 6 +- .../fhir/server/resources/FHIRResource.java | 15 +- .../ibm/fhir/server/resources/History.java | 10 +- .../ibm/fhir/server/resources/Operation.java | 20 +- .../com/ibm/fhir/server/resources/Patch.java | 10 +- .../com/ibm/fhir/server/resources/Read.java | 4 +- .../com/ibm/fhir/server/resources/Search.java | 8 +- .../com/ibm/fhir/server/resources/Update.java | 6 +- .../com/ibm/fhir/server/resources/VRead.java | 4 +- .../filters/FHIRVersionRequestFilter.java | 16 +- .../ibm/fhir/server/util/FHIRRestHelper.java | 98 +- .../server/test/BundleValidationTest.java | 2 +- .../fhir/server/test/CapabilitiesTest.java | 34 +- .../test/InteractionValidationConfigTest.java | 5 +- .../server/test/MockHttpServletRequest.java | 19 +- .../test/ProfileValidationConfigTest.java | 7 +- .../ibm/fhir/server/test/WellKnownTest.java | 8 +- 34 files changed, 2146 insertions(+), 252 deletions(-) create mode 100644 fhir-config/src/main/java/com/ibm/fhir/config/Interaction.java create mode 100644 fhir-config/src/main/java/com/ibm/fhir/config/ResourcesConfigAdapter.java create mode 100644 fhir-config/src/test/java/com/ibm/fhir/config/test/ResourcesConfigAdapterTest.java create mode 100644 fhir-core/src/main/java/com/ibm/fhir/core/FHIRVersionParam.java create mode 100644 fhir-core/src/main/java/com/ibm/fhir/core/ResourceTypeName.java create mode 100644 fhir-core/src/main/java/com/ibm/fhir/core/util/ResourceTypeHelper.java create mode 100644 fhir-core/src/test/java/com/ibm/fhir/core/util/test/ResourceTypeHelperTest.java diff --git a/fhir-config/src/main/java/com/ibm/fhir/config/FHIRConfigHelper.java b/fhir-config/src/main/java/com/ibm/fhir/config/FHIRConfigHelper.java index f64c844dafd..8a027593e88 100644 --- a/fhir-config/src/main/java/com/ibm/fhir/config/FHIRConfigHelper.java +++ b/fhir-config/src/main/java/com/ibm/fhir/config/FHIRConfigHelper.java @@ -8,10 +8,12 @@ import java.util.ArrayList; import java.util.List; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import com.ibm.fhir.config.PropertyGroup.PropertyEntry; +import com.ibm.fhir.core.FHIRVersionParam; import com.ibm.fhir.exception.FHIRException; import jakarta.json.JsonValue; @@ -23,7 +25,7 @@ */ public class FHIRConfigHelper { private static final Logger log = Logger.getLogger(FHIRConfigHelper.class.getName()); - + //Constants public static final String SEARCH_PROPERTY_TYPE_INCLUDE = "_include"; public static final String SEARCH_PROPERTY_TYPE_REVINCLUDE = "_revinclude"; @@ -84,7 +86,7 @@ private static JsonValue getPropertyFromTenantOrDefault(String propertyName) { if (result == null && !FHIRConfiguration.DEFAULT_TENANT_ID.equals(tenantId)) { try { if (propertyName.startsWith(FHIRConfiguration.PROPERTY_DATASOURCES)) { - // Issue #639. Prevent datasource lookups from falling back to + // Issue #639. Prevent datasource lookups from falling back to // the default datasource which breaks tenant isolation. result = null; } else { @@ -160,43 +162,29 @@ private static T getTypedProperty(Class expectedDataType, String property } return (result != null ? result : defaultValue); - } - + } + /** - * This method returns the list of supported resource types - * @return a list of resource types that isn't null + * @return a non-null list of supported resource types for fhirVersion 4.3 */ public static List getSupportedResourceTypes() throws FHIRException { - List result = new ArrayList<>(); + return new ArrayList<>(getSupportedResourceTypes(FHIRVersionParam.VERSION_43)); + } + /** + * @return a non-null list of supported resource types for the given fhirVersion + */ + public static Set getSupportedResourceTypes(FHIRVersionParam fhirVersion) throws FHIRException { PropertyGroup rsrcsGroup = FHIRConfigHelper.getPropertyGroup(FHIRConfiguration.PROPERTY_RESOURCES); - List rsrcsEntries; try { - rsrcsEntries = rsrcsGroup.getProperties(); - - if (rsrcsEntries != null && !rsrcsEntries.isEmpty()) { - for (PropertyEntry rsrcsEntry : rsrcsEntries) { - String name = rsrcsEntry.getName(); - - // Ensure we skip over the special property "open" and process only the others - // and skip the abstract types Resource and DomainResource - // It would be nice to be able to verify if the resource names were valid, but - // not possible at this layer of the code. - if (!FHIRConfiguration.PROPERTY_FIELD_RESOURCES_OPEN.equals(name) && - !"Resource".equals(name) && - !"DomainResource".equals(name)) { - result.add(name); - } - } - } + ResourcesConfigAdapter configAdapter = new ResourcesConfigAdapter(rsrcsGroup, fhirVersion); + return configAdapter.getSupportedResourceTypes(); } catch (Exception e) { - log.fine("FHIRConfigHelper.getSupportedResourceTypes is configured with no " - + "resources in the server config file or is not configured properly"); + log.log(Level.SEVERE, "Unexpected exception while constructing a ResourcesConfigAdapter for fhirServer/resources", e); + throw new FHIRException(e); } - - return result; } - + /** * Retrieves the search property restrictions. * diff --git a/fhir-config/src/main/java/com/ibm/fhir/config/Interaction.java b/fhir-config/src/main/java/com/ibm/fhir/config/Interaction.java new file mode 100644 index 00000000000..9a2189a40f5 --- /dev/null +++ b/fhir-config/src/main/java/com/ibm/fhir/config/Interaction.java @@ -0,0 +1,36 @@ +/* + * (C) Copyright IBM Corp. 2022 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package com.ibm.fhir.config; + +public enum Interaction { + CREATE("create"), + DELETE("delete"), + HISTORY("history"), + PATCH("patch"), + READ("read"), + SEARCH("search"), + UPDATE("update"), + VREAD("vread"); + + private final String value; + + Interaction(String value) { + this.value = value; + } + + public String value() { + return value; + } + + public static Interaction from(String value) { + for (Interaction interaction : Interaction.values()) { + if (interaction.value.equals(value)) { + return interaction; + } + } + throw new IllegalArgumentException(value); + } +} \ No newline at end of file diff --git a/fhir-config/src/main/java/com/ibm/fhir/config/ResourcesConfigAdapter.java b/fhir-config/src/main/java/com/ibm/fhir/config/ResourcesConfigAdapter.java new file mode 100644 index 00000000000..b5e57ff6334 --- /dev/null +++ b/fhir-config/src/main/java/com/ibm/fhir/config/ResourcesConfigAdapter.java @@ -0,0 +1,112 @@ +/* + * (C) Copyright IBM Corp. 2022 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package com.ibm.fhir.config; + +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.ibm.fhir.config.PropertyGroup.PropertyEntry; +import com.ibm.fhir.core.FHIRVersionParam; +import com.ibm.fhir.core.util.ResourceTypeHelper; + +/** + * An abstraction for the ibm-fhir-server fhirServer/resources property group + */ +public class ResourcesConfigAdapter { + public static final Logger log = Logger.getLogger(ResourcesConfigAdapter.class.getName()); + + private final Set supportedTypes; + private final Map> typesByInteraction = new HashMap<>(); + + public ResourcesConfigAdapter(PropertyGroup resourcesConfig, FHIRVersionParam fhirVersion) throws Exception { + supportedTypes = computeSupportedResourceTypes(resourcesConfig, fhirVersion); + + if (resourcesConfig == null) { + for (Interaction interaction : Interaction.values()) { + typesByInteraction.put(interaction, supportedTypes); + } + return; + } + + for (String resourceType : supportedTypes) { + List interactions = resourcesConfig.getStringListProperty(resourceType + "/" + FHIRConfiguration.PROPERTY_FIELD_RESOURCES_INTERACTIONS); + if (interactions == null) { + interactions = resourcesConfig.getStringListProperty("Resource/" + FHIRConfiguration.PROPERTY_FIELD_RESOURCES_INTERACTIONS); + } + + if (interactions == null) { + for (Interaction interaction : Interaction.values()) { + typesByInteraction.computeIfAbsent(interaction, k -> new LinkedHashSet<>()).add(resourceType); + } + continue; + } + + for (String interactionString : interactions) { + Interaction interaction = Interaction.from(interactionString); + typesByInteraction.computeIfAbsent(interaction, k -> new LinkedHashSet<>()).add(resourceType); + } + } + } + + /** + * @return an immutable, non-null set of supported resource types for the given fhirVersion + * @throws Exception + */ + public Set getSupportedResourceTypes() { + return supportedTypes; + } + + /** + * @return an immutable, non-null set of resource types that are configured for the given interaction and fhirVersion + */ + public Set getSupportedResourceTypes(Interaction interaction) { + return typesByInteraction.get(interaction); + } + + /** + * Construct the list of supported resource types from the passed configuration and fhirVersion + * + * @param resourcesConfig + * @param fhirVersion + * @return + * @throws Exception + */ + private Set computeSupportedResourceTypes(PropertyGroup resourcesConfig, FHIRVersionParam fhirVersion) throws Exception { + Set applicableTypes = ResourceTypeHelper.getResourceTypesFor(fhirVersion); + + if (resourcesConfig == null || resourcesConfig.getBooleanProperty("open", true)) { + return applicableTypes; + } + + Set result = new LinkedHashSet(); + for (PropertyEntry rsrcsEntry : resourcesConfig.getProperties()) { + String name = rsrcsEntry.getName(); + + // Ensure we skip over the special property "open" + // and skip the abstract types Resource and DomainResource + if (FHIRConfiguration.PROPERTY_FIELD_RESOURCES_OPEN.equals(name) || + "Resource".equals(name) || + "DomainResource".equals(name)) { + continue; + } + + if (applicableTypes.contains(name)) { + result.add(name); + } else if (log.isLoggable(Level.FINE)) { + log.fine("Configured resource type '" + name + "' is not valid " + + "or not applicable for fhirVersion " + fhirVersion.value()); + } + } + + return Collections.unmodifiableSet(result); + } +} diff --git a/fhir-config/src/test/java/com/ibm/fhir/config/test/ResourcesConfigAdapterTest.java b/fhir-config/src/test/java/com/ibm/fhir/config/test/ResourcesConfigAdapterTest.java new file mode 100644 index 00000000000..b36e0905c9e --- /dev/null +++ b/fhir-config/src/test/java/com/ibm/fhir/config/test/ResourcesConfigAdapterTest.java @@ -0,0 +1,53 @@ +/* + * (C) Copyright IBM Corp. 2022 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.config.test; + +import static org.testng.Assert.assertEquals; + +import java.util.Set; + +import org.testng.annotations.Test; + +import com.ibm.fhir.config.Interaction; +import com.ibm.fhir.config.PropertyGroup; +import com.ibm.fhir.config.ResourcesConfigAdapter; +import com.ibm.fhir.core.FHIRVersionParam; + +import jakarta.json.Json; +import jakarta.json.JsonObject; + +public class ResourcesConfigAdapterTest { + @Test + public void testGetSupportedResourceTypes_r4() throws Exception { + JsonObject json = Json.createObjectBuilder().build(); + PropertyGroup pg = new PropertyGroup(json); + ResourcesConfigAdapter resourcesConfigAdapter = new ResourcesConfigAdapter(pg, FHIRVersionParam.VERSION_40); + + Set supportedResourceTypes = resourcesConfigAdapter.getSupportedResourceTypes(); + assertEquals(supportedResourceTypes.size(), 125); + + for (Interaction interaction : Interaction.values()) { + supportedResourceTypes = resourcesConfigAdapter.getSupportedResourceTypes(interaction); + assertEquals(supportedResourceTypes.size(), 125); + } + } + + @Test + public void testGetSupportedResourceTypes_r4b() throws Exception { + JsonObject json = Json.createObjectBuilder().build(); + PropertyGroup pg = new PropertyGroup(json); + ResourcesConfigAdapter resourcesConfigAdapter = new ResourcesConfigAdapter(pg, FHIRVersionParam.VERSION_43); + + Set supportedResourceTypes = resourcesConfigAdapter.getSupportedResourceTypes(); + assertEquals(supportedResourceTypes.size(), 141); + + for (Interaction interaction : Interaction.values()) { + supportedResourceTypes = resourcesConfigAdapter.getSupportedResourceTypes(interaction); + assertEquals(supportedResourceTypes.size(), 141); + } + } +} diff --git a/fhir-core/src/main/java/com/ibm/fhir/core/FHIRMediaType.java b/fhir-core/src/main/java/com/ibm/fhir/core/FHIRMediaType.java index 7f603d808f7..22d1f375940 100644 --- a/fhir-core/src/main/java/com/ibm/fhir/core/FHIRMediaType.java +++ b/fhir-core/src/main/java/com/ibm/fhir/core/FHIRMediaType.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2019, 2021 + * (C) Copyright IBM Corp. 2019, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -8,7 +8,8 @@ import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; import java.util.Set; import javax.ws.rs.core.MediaType; @@ -18,29 +19,42 @@ */ public class FHIRMediaType extends MediaType { - public final static String SUBTYPE_FHIR_JSON = "fhir+json"; - public final static String APPLICATION_FHIR_JSON = "application/" + SUBTYPE_FHIR_JSON; - public final static MediaType APPLICATION_FHIR_JSON_TYPE = new MediaType("application", SUBTYPE_FHIR_JSON); + // use com.ibm.fhir.core.FHIRVersionParam instead + private static final String VERSION_40 = "4.0"; + private static final String VERSION_43 = "4.3"; + private static final String VERSION_401 = "4.0.1"; + private static final String VERSION_430 = "4.3.0"; - public final static String SUBTYPE_FHIR_XML = "fhir+xml"; - public final static String APPLICATION_FHIR_XML = "application/" + SUBTYPE_FHIR_XML; - public final static MediaType APPLICATION_FHIR_XML_TYPE = new MediaType("application", SUBTYPE_FHIR_XML); + // Supported values for the MIME-type parameter fhirVersion. + // https://www.hl7.org/fhir/http.html#version-parameter + public static final String FHIR_VERSION_PARAMETER = "fhirVersion"; + public static final Set SUPPORTED_FHIR_VERSIONS = + Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(VERSION_40, VERSION_401, VERSION_43, VERSION_430))); + + private static final Map fhirVersion40ParameterMap = Collections.singletonMap(FHIR_VERSION_PARAMETER, VERSION_40); + private static final Map fhirVersion43ParameterMap = Collections.singletonMap(FHIR_VERSION_PARAMETER, VERSION_43); + + public static final String SUBTYPE_FHIR_JSON = "fhir+json"; + public static final String APPLICATION_FHIR_JSON = "application/" + SUBTYPE_FHIR_JSON; + public static final MediaType APPLICATION_FHIR_JSON_TYPE = new MediaType("application", SUBTYPE_FHIR_JSON); + public static final MediaType APPLICATION_FHIR_40_JSON_TYPE = new MediaType("application", SUBTYPE_FHIR_JSON, fhirVersion40ParameterMap); + public static final MediaType APPLICATION_FHIR_43_JSON_TYPE = new MediaType("application", SUBTYPE_FHIR_JSON, fhirVersion43ParameterMap); - public final static String SUBTYPE_JSON_PATCH = "json-patch+json"; - public final static String APPLICATION_JSON_PATCH = "application/" + SUBTYPE_JSON_PATCH; - public final static MediaType APPLICATION_JSON_PATCH_TYPE = new MediaType("application", SUBTYPE_JSON_PATCH); + public static final String SUBTYPE_FHIR_XML = "fhir+xml"; + public static final String APPLICATION_FHIR_XML = "application/" + SUBTYPE_FHIR_XML; + public static final MediaType APPLICATION_FHIR_XML_TYPE = new MediaType("application", SUBTYPE_FHIR_XML); + public static final MediaType APPLICATION_FHIR_40_XML_TYPE = new MediaType("application", SUBTYPE_FHIR_JSON, fhirVersion40ParameterMap); + public static final MediaType APPLICATION_FHIR_43_XML_TYPE = new MediaType("application", SUBTYPE_FHIR_JSON, fhirVersion43ParameterMap); - public final static String SUBTYPE_FHIR_NDJSON = "fhir+ndjson"; + public static final String SUBTYPE_JSON_PATCH = "json-patch+json"; + public static final String APPLICATION_JSON_PATCH = "application/" + SUBTYPE_JSON_PATCH; + public static final MediaType APPLICATION_JSON_PATCH_TYPE = new MediaType("application", SUBTYPE_JSON_PATCH); + + public static final String SUBTYPE_FHIR_NDJSON = "fhir+ndjson"; public static final String APPLICATION_NDJSON = "application/" + SUBTYPE_FHIR_NDJSON; - public final static MediaType APPLICATION_FHIR_NDJSON_TYPE = new MediaType("application", SUBTYPE_FHIR_NDJSON); + public static final MediaType APPLICATION_FHIR_NDJSON_TYPE = new MediaType("application", SUBTYPE_FHIR_NDJSON); - public final static String SUBTYPE_FHIR_PARQUET = "fhir+parquet"; + public static final String SUBTYPE_FHIR_PARQUET = "fhir+parquet"; public static final String APPLICATION_PARQUET = "application/" + SUBTYPE_FHIR_PARQUET; - public final static MediaType APPLICATION_FHIR_PARQUET_TYPE = new MediaType("application", SUBTYPE_FHIR_PARQUET); - - // Supported values for the MIME-type parameter fhirVersion. - // https://www.hl7.org/fhir/http.html#version-parameter - public static final String FHIR_VERSION_PARAMETER = "fhirVersion"; - public static final Set SUPPORTED_FHIR_VERSIONS = - Collections.unmodifiableSet(new HashSet<>(Arrays.asList("4.0","4.0.1","4.3","4.3.0"))); + public static final MediaType APPLICATION_FHIR_PARQUET_TYPE = new MediaType("application", SUBTYPE_FHIR_PARQUET); } diff --git a/fhir-core/src/main/java/com/ibm/fhir/core/FHIRVersionParam.java b/fhir-core/src/main/java/com/ibm/fhir/core/FHIRVersionParam.java new file mode 100644 index 00000000000..f8d4f78f74c --- /dev/null +++ b/fhir-core/src/main/java/com/ibm/fhir/core/FHIRVersionParam.java @@ -0,0 +1,52 @@ +/* + * (C) Copyright IBM Corp. 2022 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package com.ibm.fhir.core; + +/** + * Enum constants for the allowed values of the fhirVersion MIME-type parameter + */ +public enum FHIRVersionParam { + VERSION_40("4.0"), + VERSION_43("4.3"); + + private final String value; + + FHIRVersionParam(String value) { + this.value = value; + } + + /** + * @return + * The String value of the fhirVersion parameter + */ + public java.lang.String value() { + return value; + } + + /** + * Factory method for creating FHIRVersionParam values from a passed string value. + * + * @param value + * A string that matches one of the allowed FHIRVersionParam values + * @return + * The corresponding FHIRVersionParam or null if a null value was passed + * @throws IllegalArgumentException + * If the passed string is not null and cannot be parsed into an allowed FHIRVersionParam value + */ + public static FHIRVersionParam from(String value) { + if (value == null) { + return null; + } + switch (value) { + case "4.0": + return VERSION_40; + case "4.3": + return VERSION_43; + default: + throw new IllegalArgumentException(value); + } + } +} diff --git a/fhir-core/src/main/java/com/ibm/fhir/core/ResourceTypeName.java b/fhir-core/src/main/java/com/ibm/fhir/core/ResourceTypeName.java new file mode 100644 index 00000000000..986925a0089 --- /dev/null +++ b/fhir-core/src/main/java/com/ibm/fhir/core/ResourceTypeName.java @@ -0,0 +1,1312 @@ +/* + * (C) Copyright IBM Corp. 2022 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package com.ibm.fhir.core; + +/** + * Enum constants for all resource type names across all versions of HL7 FHIR + */ +public enum ResourceTypeName { + /** + * EffectEvidenceSynthesis + * + *

The EffectEvidenceSynthesis resource describes the difference in an outcome between exposures states in a + * population where the effect estimate is derived from a combination of research studies. + */ + EFFECT_EVIDENCE_SYNTHESIS("EffectEvidenceSynthesis"), + + /** + * MedicinalProduct + * + *

Detailed definition of a medicinal product, typically for uses other than direct patient care (e.g. regulatory use). + */ + MEDICINAL_PRODUCT("MedicinalProduct"), + + /** + * MedicinalProductAuthorization + * + *

The regulatory authorization of a medicinal product. + */ + MEDICINAL_PRODUCT_AUTHORIZATION("MedicinalProductAuthorization"), + + /** + * MedicinalProductContraindication + * + *

The clinical particulars - indications, contraindications etc. of a medicinal product, including for regulatory + * purposes. + */ + MEDICINAL_PRODUCT_CONTRAINDICATION("MedicinalProductContraindication"), + + /** + * MedicinalProductIndication + * + *

Indication for the Medicinal Product. + */ + MEDICINAL_PRODUCT_INDICATION("MedicinalProductIndication"), + + /** + * MedicinalProductIngredient + * + *

An ingredient of a manufactured item or pharmaceutical product. + */ + MEDICINAL_PRODUCT_INGREDIENT("MedicinalProductIngredient"), + + /** + * MedicinalProductInteraction + * + *

The interactions of the medicinal product with other medicinal products, or other forms of interactions. + */ + MEDICINAL_PRODUCT_INTERACTION("MedicinalProductInteraction"), + + /** + * MedicinalProductManufactured + * + *

The manufactured item as contained in the packaged medicinal product. + */ + MEDICINAL_PRODUCT_MANUFACTURED("MedicinalProductManufactured"), + + /** + * MedicinalProductPackaged + * + *

A medicinal product in a container or package. + */ + MEDICINAL_PRODUCT_PACKAGED("MedicinalProductPackaged"), + + /** + * MedicinalProductPharmaceutical + * + *

A pharmaceutical product described in terms of its composition and dose form. + */ + MEDICINAL_PRODUCT_PHARMACEUTICAL("MedicinalProductPharmaceutical"), + + /** + * MedicinalProductUndesirableEffect + * + *

Describe the undesirable effects of the medicinal product. + */ + MEDICINAL_PRODUCT_UNDESIRABLE_EFFECT("MedicinalProductUndesirableEffect"), + + /** + * RiskEvidenceSynthesis + * + *

The RiskEvidenceSynthesis resource describes the likelihood of an outcome in a population plus exposure state where + * the risk estimate is derived from a combination of research studies. + */ + RISK_EVIDENCE_SYNTHESIS("RiskEvidenceSynthesis"), + + /** + * SubstanceNucleicAcid + * + *

Nucleic acids are defined by three distinct elements: the base, sugar and linkage. Individual substance/moiety IDs + * will be created for each of these elements. The nucleotide sequence will be always entered in the 5’-3’ direction. + */ + SUBSTANCE_NUCLEIC_ACID("SubstanceNucleicAcid"), + + /** + * SubstancePolymer + * + *

Todo. + */ + SUBSTANCE_POLYMER("SubstancePolymer"), + + /** + * SubstanceProtein + * + *

A SubstanceProtein is defined as a single unit of a linear amino acid sequence, or a combination of subunits that + * are either covalently linked or have a defined invariant stoichiometric relationship. This includes all synthetic, + * recombinant and purified SubstanceProteins of defined sequence, whether the use is therapeutic or prophylactic. This + * set of elements will be used to describe albumins, coagulation factors, cytokines, growth factors, + * peptide/SubstanceProtein hormones, enzymes, toxins, toxoids, recombinant vaccines, and immunomodulators. + */ + SUBSTANCE_PROTEIN("SubstanceProtein"), + + /** + * SubstanceReferenceInformation + * + *

Todo. + */ + SUBSTANCE_REFERENCE_INFORMATION("SubstanceReferenceInformation"), + + /** + * SubstanceSourceMaterial + * + *

Source material shall capture information on the taxonomic and anatomical origins as well as the fraction of a + * material that can result in or can be modified to form a substance. This set of data elements shall be used to define + * polymer substances isolated from biological matrices. Taxonomic and anatomical origins shall be described using a + * controlled vocabulary as required. This information is captured for naturally derived polymers ( . starch) and + * structurally diverse substances. For Organisms belonging to the Kingdom Plantae the Substance level defines the fresh + * material of a single species or infraspecies, the Herbal Drug and the Herbal preparation. For Herbal preparations, the + * fraction information will be captured at the Substance information level and additional information for herbal + * extracts will be captured at the Specified Substance Group 1 information level. See for further explanation the + * Substance Class: Structurally Diverse and the herbal annex. + */ + SUBSTANCE_SOURCE_MATERIAL("SubstanceSourceMaterial"), + + /** + * SubstanceSpecification + * + *

The detailed description of a substance, typically at a level beyond what is used for prescribing. + */ + SUBSTANCE_SPECIFICATION("SubstanceSpecification"), + + /** + * Resource + * + *

--- Abstract Type! ---This is the base resource type for everything. + */ + RESOURCE("Resource"), + + /** + * Binary + * + *

A resource that represents the data of a single raw artifact as digital content accessible in its native format. A + * Binary resource can contain any content, whether text, image, pdf, zip archive, etc. + */ + BINARY("Binary"), + + /** + * Bundle + * + *

A container for a collection of resources. + */ + BUNDLE("Bundle"), + + /** + * DomainResource + * + *

--- Abstract Type! ---A resource that includes narrative, extensions, and contained resources. + */ + DOMAIN_RESOURCE("DomainResource"), + + /** + * Account + * + *

A financial tool for tracking value accrued for a particular purpose. In the healthcare field, used to track + * charges for a patient, cost centers, etc. + */ + ACCOUNT("Account"), + + /** + * ActivityDefinition + * + *

This resource allows for the definition of some activity to be performed, independent of a particular patient, + * practitioner, or other performance context. + */ + ACTIVITY_DEFINITION("ActivityDefinition"), + + /** + * AdministrableProductDefinition + * + *

A medicinal product in the final form which is suitable for administering to a patient (after any mixing of + * multiple components, dissolution etc. has been performed). + */ + ADMINISTRABLE_PRODUCT_DEFINITION("AdministrableProductDefinition"), + + /** + * AdverseEvent + * + *

Actual or potential/avoided event causing unintended physical injury resulting from or contributed to by medical + * care, a research study or other healthcare setting factors that requires additional monitoring, treatment, or + * hospitalization, or that results in death. + */ + ADVERSE_EVENT("AdverseEvent"), + + /** + * AllergyIntolerance + * + *

Risk of harmful or undesirable, physiological response which is unique to an individual and associated with + * exposure to a substance. + */ + ALLERGY_INTOLERANCE("AllergyIntolerance"), + + /** + * Appointment + * + *

A booking of a healthcare event among patient(s), practitioner(s), related person(s) and/or device(s) for a + * specific date/time. This may result in one or more Encounter(s). + */ + APPOINTMENT("Appointment"), + + /** + * AppointmentResponse + * + *

A reply to an appointment request for a patient and/or practitioner(s), such as a confirmation or rejection. + */ + APPOINTMENT_RESPONSE("AppointmentResponse"), + + /** + * AuditEvent + * + *

A record of an event made for purposes of maintaining a security log. Typical uses include detection of intrusion + * attempts and monitoring for inappropriate usage. + */ + AUDIT_EVENT("AuditEvent"), + + /** + * Basic + * + *

Basic is used for handling concepts not yet defined in FHIR, narrative-only resources that don't map to an existing + * resource, and custom resources not appropriate for inclusion in the FHIR specification. + */ + BASIC("Basic"), + + /** + * BiologicallyDerivedProduct + * + *

A material substance originating from a biological entity intended to be transplanted or infused + *

into another (possibly the same) biological entity. + */ + BIOLOGICALLY_DERIVED_PRODUCT("BiologicallyDerivedProduct"), + + /** + * BodyStructure + * + *

Record details about an anatomical structure. This resource may be used when a coded concept does not provide the + * necessary detail needed for the use case. + */ + BODY_STRUCTURE("BodyStructure"), + + /** + * CapabilityStatement + * + *

A Capability Statement documents a set of capabilities (behaviors) of a FHIR Server for a particular version of + * FHIR that may be used as a statement of actual server functionality or a statement of required or desired server + * implementation. + */ + CAPABILITY_STATEMENT("CapabilityStatement"), + + /** + * CarePlan + * + *

Describes the intention of how one or more practitioners intend to deliver care for a particular patient, group or + * community for a period of time, possibly limited to care for a specific condition or set of conditions. + */ + CARE_PLAN("CarePlan"), + + /** + * CareTeam + * + *

The Care Team includes all the people and organizations who plan to participate in the coordination and delivery of + * care for a patient. + */ + CARE_TEAM("CareTeam"), + + /** + * CatalogEntry + * + *

Catalog entries are wrappers that contextualize items included in a catalog. + */ + CATALOG_ENTRY("CatalogEntry"), + + /** + * ChargeItem + * + *

The resource ChargeItem describes the provision of healthcare provider products for a certain patient, therefore + * referring not only to the product, but containing in addition details of the provision, like date, time, amounts and + * participating organizations and persons. Main Usage of the ChargeItem is to enable the billing process and internal + * cost allocation. + */ + CHARGE_ITEM("ChargeItem"), + + /** + * ChargeItemDefinition + * + *

The ChargeItemDefinition resource provides the properties that apply to the (billing) codes necessary to calculate + * costs and prices. The properties may differ largely depending on type and realm, therefore this resource gives only a + * rough structure and requires profiling for each type of billing code system. + */ + CHARGE_ITEM_DEFINITION("ChargeItemDefinition"), + + /** + * Citation + * + *

The Citation Resource enables reference to any knowledge artifact for purposes of identification and attribution. + * The Citation Resource supports existing reference structures and developing publication practices such as versioning, + * expressing complex contributorship roles, and referencing computable resources. + */ + CITATION("Citation"), + + /** + * Claim + * + *

A provider issued list of professional services and products which have been provided, or are to be provided, to a + * patient which is sent to an insurer for reimbursement. + */ + CLAIM("Claim"), + + /** + * ClaimResponse + * + *

This resource provides the adjudication details from the processing of a Claim resource. + */ + CLAIM_RESPONSE("ClaimResponse"), + + /** + * ClinicalImpression + * + *

A record of a clinical assessment performed to determine what problem(s) may affect the patient and before planning + * the treatments or management strategies that are best to manage a patient's condition. Assessments are often 1:1 with + * a clinical consultation / encounter, but this varies greatly depending on the clinical workflow. This resource is + * called "ClinicalImpression" rather than "ClinicalAssessment" to avoid confusion with the recording of assessment tools + * such as Apgar score. + */ + CLINICAL_IMPRESSION("ClinicalImpression"), + + /** + * ClinicalUseDefinition + * + *

A single issue - either an indication, contraindication, interaction or an undesirable effect for a medicinal + * product, medication, device or procedure. + */ + CLINICAL_USE_DEFINITION("ClinicalUseDefinition"), + + /** + * CodeSystem + * + *

The CodeSystem resource is used to declare the existence of and describe a code system or code system supplement + * and its key properties, and optionally define a part or all of its content. + */ + CODE_SYSTEM("CodeSystem"), + + /** + * Communication + * + *

An occurrence of information being transmitted; e.g. an alert that was sent to a responsible provider, a public + * health agency that was notified about a reportable condition. + */ + COMMUNICATION("Communication"), + + /** + * CommunicationRequest + * + *

A request to convey information; e.g. the CDS system proposes that an alert be sent to a responsible provider, the + * CDS system proposes that the public health agency be notified about a reportable condition. + */ + COMMUNICATION_REQUEST("CommunicationRequest"), + + /** + * CompartmentDefinition + * + *

A compartment definition that defines how resources are accessed on a server. + */ + COMPARTMENT_DEFINITION("CompartmentDefinition"), + + /** + * Composition + * + *

A set of healthcare-related information that is assembled together into a single logical package that provides a + * single coherent statement of meaning, establishes its own context and that has clinical attestation with regard to who + * is making the statement. A Composition defines the structure and narrative content necessary for a document. However, + * a Composition alone does not constitute a document. Rather, the Composition must be the first entry in a Bundle where + * Bundle.type=document, and any other resources referenced from Composition must be included as subsequent entries in + * the Bundle (for example Patient, Practitioner, Encounter, etc.). + */ + COMPOSITION("Composition"), + + /** + * ConceptMap + * + *

A statement of relationships from one set of concepts to one or more other concepts - either concepts in code + * systems, or data element/data element concepts, or classes in class models. + */ + CONCEPT_MAP("ConceptMap"), + + /** + * Condition + * + *

A clinical condition, problem, diagnosis, or other event, situation, issue, or clinical concept that has risen to a + * level of concern. + */ + CONDITION("Condition"), + + /** + * Consent + * + *

A record of a healthcare consumer’s choices, which permits or denies identified recipient(s) or recipient role(s) + * to perform one or more actions within a given policy context, for specific purposes and periods of time. + */ + CONSENT("Consent"), + + /** + * Contract + * + *

Legally enforceable, formally recorded unilateral or bilateral directive i.e., a policy or agreement. + */ + CONTRACT("Contract"), + + /** + * Coverage + * + *

Financial instrument which may be used to reimburse or pay for health care products and services. Includes both + * insurance and self-payment. + */ + COVERAGE("Coverage"), + + /** + * CoverageEligibilityRequest + * + *

The CoverageEligibilityRequest provides patient and insurance coverage information to an insurer for them to + * respond, in the form of an CoverageEligibilityResponse, with information regarding whether the stated coverage is + * valid and in-force and optionally to provide the insurance details of the policy. + */ + COVERAGE_ELIGIBILITY_REQUEST("CoverageEligibilityRequest"), + + /** + * CoverageEligibilityResponse + * + *

This resource provides eligibility and plan details from the processing of an CoverageEligibilityRequest resource. + */ + COVERAGE_ELIGIBILITY_RESPONSE("CoverageEligibilityResponse"), + + /** + * DetectedIssue + * + *

Indicates an actual or potential clinical issue with or between one or more active or proposed clinical actions for + * a patient; e.g. Drug-drug interaction, Ineffective treatment frequency, Procedure-condition conflict, etc. + */ + DETECTED_ISSUE("DetectedIssue"), + + /** + * Device + * + *

A type of a manufactured item that is used in the provision of healthcare without being substantially changed + * through that activity. The device may be a medical or non-medical device. + */ + DEVICE("Device"), + + /** + * DeviceDefinition + * + *

The characteristics, operational status and capabilities of a medical-related component of a medical device. + */ + DEVICE_DEFINITION("DeviceDefinition"), + + /** + * DeviceMetric + * + *

Describes a measurement, calculation or setting capability of a medical device. + */ + DEVICE_METRIC("DeviceMetric"), + + /** + * DeviceRequest + * + *

Represents a request for a patient to employ a medical device. The device may be an implantable device, or an + * external assistive device, such as a walker. + */ + DEVICE_REQUEST("DeviceRequest"), + + /** + * DeviceUseStatement + * + *

A record of a device being used by a patient where the record is the result of a report from the patient or another + * clinician. + */ + DEVICE_USE_STATEMENT("DeviceUseStatement"), + + /** + * DiagnosticReport + * + *

The findings and interpretation of diagnostic tests performed on patients, groups of patients, devices, and + * locations, and/or specimens derived from these. The report includes clinical context such as requesting and provider + * information, and some mix of atomic results, images, textual and coded interpretations, and formatted representation + * of diagnostic reports. + */ + DIAGNOSTIC_REPORT("DiagnosticReport"), + + /** + * DocumentManifest + * + *

A collection of documents compiled for a purpose together with metadata that applies to the collection. + */ + DOCUMENT_MANIFEST("DocumentManifest"), + + /** + * DocumentReference + * + *

A reference to a document of any kind for any purpose. Provides metadata about the document so that the document + * can be discovered and managed. The scope of a document is any seralized object with a mime-type, so includes formal + * patient centric documents (CDA), cliical notes, scanned paper, and non-patient specific documents like policy text. + */ + DOCUMENT_REFERENCE("DocumentReference"), + + /** + * Encounter + * + *

An interaction between a patient and healthcare provider(s) for the purpose of providing healthcare service(s) or + * assessing the health status of a patient. + */ + ENCOUNTER("Encounter"), + + /** + * Endpoint + * + *

The technical details of an endpoint that can be used for electronic services, such as for web services providing + * XDS.b or a REST endpoint for another FHIR server. This may include any security context information. + */ + ENDPOINT("Endpoint"), + + /** + * EnrollmentRequest + * + *

This resource provides the insurance enrollment details to the insurer regarding a specified coverage. + */ + ENROLLMENT_REQUEST("EnrollmentRequest"), + + /** + * EnrollmentResponse + * + *

This resource provides enrollment and plan details from the processing of an EnrollmentRequest resource. + */ + ENROLLMENT_RESPONSE("EnrollmentResponse"), + + /** + * EpisodeOfCare + * + *

An association between a patient and an organization / healthcare provider(s) during which time encounters may + * occur. The managing organization assumes a level of responsibility for the patient during this time. + */ + EPISODE_OF_CARE("EpisodeOfCare"), + + /** + * EventDefinition + * + *

The EventDefinition resource provides a reusable description of when a particular event can occur. + */ + EVENT_DEFINITION("EventDefinition"), + + /** + * Evidence + * + *

The Evidence Resource provides a machine-interpretable expression of an evidence concept including the evidence + * variables (eg population, exposures/interventions, comparators, outcomes, measured variables, confounding variables), + * the statistics, and the certainty of this evidence. + */ + EVIDENCE("Evidence"), + + /** + * EvidenceReport + * + *

The EvidenceReport Resource is a specialized container for a collection of resources and codable concepts, adapted + * to support compositions of Evidence, EvidenceVariable, and Citation resources and related concepts. + */ + EVIDENCE_REPORT("EvidenceReport"), + + /** + * EvidenceVariable + * + *

The EvidenceVariable resource describes an element that knowledge (Evidence) is about. + */ + EVIDENCE_VARIABLE("EvidenceVariable"), + + /** + * ExampleScenario + * + *

Example of workflow instance. + */ + EXAMPLE_SCENARIO("ExampleScenario"), + + /** + * ExplanationOfBenefit + * + *

This resource provides: the claim details; adjudication details from the processing of a Claim; and optionally + * account balance information, for informing the subscriber of the benefits provided. + */ + EXPLANATION_OF_BENEFIT("ExplanationOfBenefit"), + + /** + * FamilyMemberHistory + * + *

Significant health conditions for a person related to the patient relevant in the context of care for the patient. + */ + FAMILY_MEMBER_HISTORY("FamilyMemberHistory"), + + /** + * Flag + * + *

Prospective warnings of potential issues when providing care to the patient. + */ + FLAG("Flag"), + + /** + * Goal + * + *

Describes the intended objective(s) for a patient, group or organization care, for example, weight loss, restoring + * an activity of daily living, obtaining herd immunity via immunization, meeting a process improvement objective, etc. + */ + GOAL("Goal"), + + /** + * GraphDefinition + * + *

A formal computable definition of a graph of resources - that is, a coherent set of resources that form a graph by + * following references. The Graph Definition resource defines a set and makes rules about the set. + */ + GRAPH_DEFINITION("GraphDefinition"), + + /** + * Group + * + *

Represents a defined collection of entities that may be discussed or acted upon collectively but which are not + * expected to act collectively, and are not formally or legally recognized; i.e. a collection of entities that isn't an + * Organization. + */ + GROUP("Group"), + + /** + * GuidanceResponse + * + *

A guidance response is the formal response to a guidance request, including any output parameters returned by the + * evaluation, as well as the description of any proposed actions to be taken. + */ + GUIDANCE_RESPONSE("GuidanceResponse"), + + /** + * HealthcareService + * + *

The details of a healthcare service available at a location. + */ + HEALTHCARE_SERVICE("HealthcareService"), + + /** + * ImagingStudy + * + *

Representation of the content produced in a DICOM imaging study. A study comprises a set of series, each of which + * includes a set of Service-Object Pair Instances (SOP Instances - images or other data) acquired or produced in a + * common context. A series is of only one modality (e.g. X-ray, CT, MR, ultrasound), but a study may have multiple + * series of different modalities. + */ + IMAGING_STUDY("ImagingStudy"), + + /** + * Immunization + * + *

Describes the event of a patient being administered a vaccine or a record of an immunization as reported by a + * patient, a clinician or another party. + */ + IMMUNIZATION("Immunization"), + + /** + * ImmunizationEvaluation + * + *

Describes a comparison of an immunization event against published recommendations to determine if the + * administration is "valid" in relation to those recommendations. + */ + IMMUNIZATION_EVALUATION("ImmunizationEvaluation"), + + /** + * ImmunizationRecommendation + * + *

A patient's point-in-time set of recommendations (i.e. forecasting) according to a published schedule with optional + * supporting justification. + */ + IMMUNIZATION_RECOMMENDATION("ImmunizationRecommendation"), + + /** + * ImplementationGuide + * + *

A set of rules of how a particular interoperability or standards problem is solved - typically through the use of + * FHIR resources. This resource is used to gather all the parts of an implementation guide into a logical whole and to + * publish a computable definition of all the parts. + */ + IMPLEMENTATION_GUIDE("ImplementationGuide"), + + /** + * Ingredient + * + *

An ingredient of a manufactured item or pharmaceutical product. + */ + INGREDIENT("Ingredient"), + + /** + * InsurancePlan + * + *

Details of a Health Insurance product/plan provided by an organization. + */ + INSURANCE_PLAN("InsurancePlan"), + + /** + * Invoice + * + *

Invoice containing collected ChargeItems from an Account with calculated individual and total price for Billing + * purpose. + */ + INVOICE("Invoice"), + + /** + * Library + * + *

The Library resource is a general-purpose container for knowledge asset definitions. It can be used to describe and + * expose existing knowledge assets such as logic libraries and information model descriptions, as well as to describe a + * collection of knowledge assets. + */ + LIBRARY("Library"), + + /** + * Linkage + * + *

Identifies two or more records (resource instances) that refer to the same real-world "occurrence". + */ + LINKAGE("Linkage"), + + /** + * List + * + *

A list is a curated collection of resources. + */ + LIST("List"), + + /** + * Location + * + *

Details and position information for a physical place where services are provided and resources and participants + * may be stored, found, contained, or accommodated. + */ + LOCATION("Location"), + + /** + * ManufacturedItemDefinition + * + *

The definition and characteristics of a medicinal manufactured item, such as a tablet or capsule, as contained in a + * packaged medicinal product. + */ + MANUFACTURED_ITEM_DEFINITION("ManufacturedItemDefinition"), + + /** + * Measure + * + *

The Measure resource provides the definition of a quality measure. + */ + MEASURE("Measure"), + + /** + * MeasureReport + * + *

The MeasureReport resource contains the results of the calculation of a measure; and optionally a reference to the + * resources involved in that calculation. + */ + MEASURE_REPORT("MeasureReport"), + + /** + * Media + * + *

A photo, video, or audio recording acquired or used in healthcare. The actual content may be inline or provided by + * direct reference. + */ + MEDIA("Media"), + + /** + * Medication + * + *

This resource is primarily used for the identification and definition of a medication for the purposes of + * prescribing, dispensing, and administering a medication as well as for making statements about medication use. + */ + MEDICATION("Medication"), + + /** + * MedicationAdministration + * + *

Describes the event of a patient consuming or otherwise being administered a medication. This may be as simple as + * swallowing a tablet or it may be a long running infusion. Related resources tie this event to the authorizing + * prescription, and the specific encounter between patient and health care practitioner. + */ + MEDICATION_ADMINISTRATION("MedicationAdministration"), + + /** + * MedicationDispense + * + *

Indicates that a medication product is to be or has been dispensed for a named person/patient. This includes a + * description of the medication product (supply) provided and the instructions for administering the medication. The + * medication dispense is the result of a pharmacy system responding to a medication order. + */ + MEDICATION_DISPENSE("MedicationDispense"), + + /** + * MedicationKnowledge + * + *

Information about a medication that is used to support knowledge. + */ + MEDICATION_KNOWLEDGE("MedicationKnowledge"), + + /** + * MedicationRequest + * + *

An order or request for both supply of the medication and the instructions for administration of the medication to + * a patient. The resource is called "MedicationRequest" rather than "MedicationPrescription" or "MedicationOrder" to + * generalize the use across inpatient and outpatient settings, including care plans, etc., and to harmonize with + * workflow patterns. + */ + MEDICATION_REQUEST("MedicationRequest"), + + /** + * MedicationStatement + * + *

A record of a medication that is being consumed by a patient. A MedicationStatement may indicate that the patient + * may be taking the medication now or has taken the medication in the past or will be taking the medication in the + * future. The source of this information can be the patient, significant other (such as a family member or spouse), or a + * clinician. A common scenario where this information is captured is during the history taking process during a patient + * visit or stay. The medication information may come from sources such as the patient's memory, from a prescription + * bottle, or from a list of medications the patient, clinician or other party maintains. + * + *

The primary difference between a medication statement and a medication administration is that the medication + * administration has complete administration information and is based on actual administration information from the + * person who administered the medication. A medication statement is often, if not always, less specific. There is no + * required date/time when the medication was administered, in fact we only know that a source has reported the patient + * is taking this medication, where details such as time, quantity, or rate or even medication product may be incomplete + * or missing or less precise. As stated earlier, the medication statement information may come from the patient's + * memory, from a prescription bottle or from a list of medications the patient, clinician or other party maintains. + * Medication administration is more formal and is not missing detailed information. + */ + MEDICATION_STATEMENT("MedicationStatement"), + + /** + * MedicinalProductDefinition + * + *

Detailed definition of a medicinal product, typically for uses other than direct patient care (e.g. regulatory use, + * drug catalogs). + */ + MEDICINAL_PRODUCT_DEFINITION("MedicinalProductDefinition"), + + /** + * MessageDefinition + * + *

Defines the characteristics of a message that can be shared between systems, including the type of event that + * initiates the message, the content to be transmitted and what response(s), if any, are permitted. + */ + MESSAGE_DEFINITION("MessageDefinition"), + + /** + * MessageHeader + * + *

The header for a message exchange that is either requesting or responding to an action. The reference(s) that are + * the subject of the action as well as other information related to the action are typically transmitted in a bundle in + * which the MessageHeader resource instance is the first resource in the bundle. + */ + MESSAGE_HEADER("MessageHeader"), + + /** + * MolecularSequence + * + *

Raw data describing a biological sequence. + */ + MOLECULAR_SEQUENCE("MolecularSequence"), + + /** + * NamingSystem + * + *

A curated namespace that issues unique symbols within that namespace for the identification of concepts, people, + * devices, etc. Represents a "System" used within the Identifier and Coding data types. + */ + NAMING_SYSTEM("NamingSystem"), + + /** + * NutritionOrder + * + *

A request to supply a diet, formula feeding (enteral) or oral nutritional supplement to a patient/resident. + */ + NUTRITION_ORDER("NutritionOrder"), + + /** + * NutritionProduct + * + *

A food or fluid product that is consumed by patients. + */ + NUTRITION_PRODUCT("NutritionProduct"), + + /** + * Observation + * + *

Measurements and simple assertions made about a patient, device or other subject. + */ + OBSERVATION("Observation"), + + /** + * ObservationDefinition + * + *

Set of definitional characteristics for a kind of observation or measurement produced or consumed by an orderable + * health care service. + */ + OBSERVATION_DEFINITION("ObservationDefinition"), + + /** + * OperationDefinition + * + *

A formal computable definition of an operation (on the RESTful interface) or a named query (using the search + * interaction). + */ + OPERATION_DEFINITION("OperationDefinition"), + + /** + * OperationOutcome + * + *

A collection of error, warning, or information messages that result from a system action. + */ + OPERATION_OUTCOME("OperationOutcome"), + + /** + * Organization + * + *

A formally or informally recognized grouping of people or organizations formed for the purpose of achieving some + * form of collective action. Includes companies, institutions, corporations, departments, community groups, healthcare + * practice groups, payer/insurer, etc. + */ + ORGANIZATION("Organization"), + + /** + * OrganizationAffiliation + * + *

Defines an affiliation/assotiation/relationship between 2 distinct oganizations, that is not a part-of + * relationship/sub-division relationship. + */ + ORGANIZATION_AFFILIATION("OrganizationAffiliation"), + + /** + * PackagedProductDefinition + * + *

A medically related item or items, in a container or package. + */ + PACKAGED_PRODUCT_DEFINITION("PackagedProductDefinition"), + + /** + * Parameters + * + *

This resource is a non-persisted resource used to pass information into and back from an [operation](operations. + * html). It has no other use, and there is no RESTful endpoint associated with it. + */ + PARAMETERS("Parameters"), + + /** + * Patient + * + *

Demographics and other administrative information about an individual or animal receiving care or other health- + * related services. + */ + PATIENT("Patient"), + + /** + * PaymentNotice + * + *

This resource provides the status of the payment for goods and services rendered, and the request and response + * resource references. + */ + PAYMENT_NOTICE("PaymentNotice"), + + /** + * PaymentReconciliation + * + *

This resource provides the details including amount of a payment and allocates the payment items being paid. + */ + PAYMENT_RECONCILIATION("PaymentReconciliation"), + + /** + * Person + * + *

Demographics and administrative information about a person independent of a specific health-related context. + */ + PERSON("Person"), + + /** + * PlanDefinition + * + *

This resource allows for the definition of various types of plans as a sharable, consumable, and executable + * artifact. The resource is general enough to support the description of a broad range of clinical and non-clinical + * artifacts such as clinical decision support rules, order sets, protocols, and drug quality specifications. + */ + PLAN_DEFINITION("PlanDefinition"), + + /** + * Practitioner + * + *

A person who is directly or indirectly involved in the provisioning of healthcare. + */ + PRACTITIONER("Practitioner"), + + /** + * PractitionerRole + * + *

A specific set of Roles/Locations/specialties/services that a practitioner may perform at an organization for a + * period of time. + */ + PRACTITIONER_ROLE("PractitionerRole"), + + /** + * Procedure + * + *

An action that is or was performed on or for a patient. This can be a physical intervention like an operation, or + * less invasive like long term services, counseling, or hypnotherapy. + */ + PROCEDURE("Procedure"), + + /** + * Provenance + * + *

Provenance of a resource is a record that describes entities and processes involved in producing and delivering or + * otherwise influencing that resource. Provenance provides a critical foundation for assessing authenticity, enabling + * trust, and allowing reproducibility. Provenance assertions are a form of contextual metadata and can themselves become + * important records with their own provenance. Provenance statement indicates clinical significance in terms of + * confidence in authenticity, reliability, and trustworthiness, integrity, and stage in lifecycle (e.g. Document + * Completion - has the artifact been legally authenticated), all of which may impact security, privacy, and trust + * policies. + */ + PROVENANCE("Provenance"), + + /** + * Questionnaire + * + *

A structured set of questions intended to guide the collection of answers from end-users. Questionnaires provide + * detailed control over order, presentation, phraseology and grouping to allow coherent, consistent data collection. + */ + QUESTIONNAIRE("Questionnaire"), + + /** + * QuestionnaireResponse + * + *

A structured set of questions and their answers. The questions are ordered and grouped into coherent subsets, + * corresponding to the structure of the grouping of the questionnaire being responded to. + */ + QUESTIONNAIRE_RESPONSE("QuestionnaireResponse"), + + /** + * RegulatedAuthorization + * + *

Regulatory approval, clearance or licencing related to a regulated product, treatment, facility or activity that is + * cited in a guidance, regulation, rule or legislative act. An example is Market Authorization relating to a Medicinal + * Product. + */ + REGULATED_AUTHORIZATION("RegulatedAuthorization"), + + /** + * RelatedPerson + * + *

Information about a person that is involved in the care for a patient, but who is not the target of healthcare, nor + * has a formal responsibility in the care process. + */ + RELATED_PERSON("RelatedPerson"), + + /** + * RequestGroup + * + *

A group of related requests that can be used to capture intended activities that have inter-dependencies such as + * "give this medication after that one". + */ + REQUEST_GROUP("RequestGroup"), + + /** + * ResearchDefinition + * + *

The ResearchDefinition resource describes the conditional state (population and any exposures being compared within + * the population) and outcome (if specified) that the knowledge (evidence, assertion, recommendation) is about. + */ + RESEARCH_DEFINITION("ResearchDefinition"), + + /** + * ResearchElementDefinition + * + *

The ResearchElementDefinition resource describes a "PICO" element that knowledge (evidence, assertion, + * recommendation) is about. + */ + RESEARCH_ELEMENT_DEFINITION("ResearchElementDefinition"), + + /** + * ResearchStudy + * + *

A process where a researcher or organization plans and then executes a series of steps intended to increase the + * field of healthcare-related knowledge. This includes studies of safety, efficacy, comparative effectiveness and other + * information about medications, devices, therapies and other interventional and investigative techniques. A + * ResearchStudy involves the gathering of information about human or animal subjects. + */ + RESEARCH_STUDY("ResearchStudy"), + + /** + * ResearchSubject + * + *

A physical entity which is the primary unit of operational and/or administrative interest in a study. + */ + RESEARCH_SUBJECT("ResearchSubject"), + + /** + * RiskAssessment + * + *

An assessment of the likely outcome(s) for a patient or other subject as well as the likelihood of each outcome. + */ + RISK_ASSESSMENT("RiskAssessment"), + + /** + * Schedule + * + *

A container for slots of time that may be available for booking appointments. + */ + SCHEDULE("Schedule"), + + /** + * SearchParameter + * + *

A search parameter that defines a named search item that can be used to search/filter on a resource. + */ + SEARCH_PARAMETER("SearchParameter"), + + /** + * ServiceRequest + * + *

A record of a request for service such as diagnostic investigations, treatments, or operations to be performed. + */ + SERVICE_REQUEST("ServiceRequest"), + + /** + * Slot + * + *

A slot of time on a schedule that may be available for booking appointments. + */ + SLOT("Slot"), + + /** + * Specimen + * + *

A sample to be used for analysis. + */ + SPECIMEN("Specimen"), + + /** + * SpecimenDefinition + * + *

A kind of specimen with associated set of requirements. + */ + SPECIMEN_DEFINITION("SpecimenDefinition"), + + /** + * StructureDefinition + * + *

A definition of a FHIR structure. This resource is used to describe the underlying resources, data types defined in + * FHIR, and also for describing extensions and constraints on resources and data types. + */ + STRUCTURE_DEFINITION("StructureDefinition"), + + /** + * StructureMap + * + *

A Map of relationships between 2 structures that can be used to transform data. + */ + STRUCTURE_MAP("StructureMap"), + + /** + * Subscription + * + *

The subscription resource is used to define a push-based subscription from a server to another system. Once a + * subscription is registered with the server, the server checks every resource that is created or updated, and if the + * resource matches the given criteria, it sends a message on the defined "channel" so that another system can take an + * appropriate action. + */ + SUBSCRIPTION("Subscription"), + + /** + * SubscriptionStatus + * + *

The SubscriptionStatus resource describes the state of a Subscription during notifications. + */ + SUBSCRIPTION_STATUS("SubscriptionStatus"), + + /** + * SubscriptionTopic + * + *

Describes a stream of resource state changes identified by trigger criteria and annotated with labels useful to + * filter projections from this topic. + */ + SUBSCRIPTION_TOPIC("SubscriptionTopic"), + + /** + * Substance + * + *

A homogeneous material with a definite composition. + */ + SUBSTANCE("Substance"), + + /** + * SubstanceDefinition + * + *

The detailed description of a substance, typically at a level beyond what is used for prescribing. + */ + SUBSTANCE_DEFINITION("SubstanceDefinition"), + + /** + * SupplyDelivery + * + *

Record of delivery of what is supplied. + */ + SUPPLY_DELIVERY("SupplyDelivery"), + + /** + * SupplyRequest + * + *

A record of a request for a medication, substance or device used in the healthcare setting. + */ + SUPPLY_REQUEST("SupplyRequest"), + + /** + * Task + * + *

A task to be performed. + */ + TASK("Task"), + + /** + * TerminologyCapabilities + * + *

A TerminologyCapabilities resource documents a set of capabilities (behaviors) of a FHIR Terminology Server that + * may be used as a statement of actual server functionality or a statement of required or desired server implementation. + */ + TERMINOLOGY_CAPABILITIES("TerminologyCapabilities"), + + /** + * TestReport + * + *

A summary of information based on the results of executing a TestScript. + */ + TEST_REPORT("TestReport"), + + /** + * TestScript + * + *

A structured set of tests against a FHIR server or client implementation to determine compliance against the FHIR + * specification. + */ + TEST_SCRIPT("TestScript"), + + /** + * ValueSet + * + *

A ValueSet resource instance specifies a set of codes drawn from one or more code systems, intended for use in a + * particular context. Value sets link between [[[CodeSystem]]] definitions and their use in [coded elements] + * (terminologies.html). + */ + VALUE_SET("ValueSet"), + + /** + * VerificationResult + * + *

Describes validation requirements, source(s), status and dates for one or more elements. + */ + VERIFICATION_RESULT("VerificationResult"), + + /** + * VisionPrescription + * + *

An authorization for the provision of glasses and/or contact lenses to a patient. + */ + VISION_PRESCRIPTION("VisionPrescription"); + + private final String value; + + ResourceTypeName(String value) { + this.value = value; + } + + /** + * @return + * The String value of the resource type name + */ + public java.lang.String value() { + return value; + } +} diff --git a/fhir-core/src/main/java/com/ibm/fhir/core/util/ResourceTypeHelper.java b/fhir-core/src/main/java/com/ibm/fhir/core/util/ResourceTypeHelper.java new file mode 100644 index 00000000000..4e75a270bde --- /dev/null +++ b/fhir-core/src/main/java/com/ibm/fhir/core/util/ResourceTypeHelper.java @@ -0,0 +1,122 @@ +/* + * (C) Copyright IBM Corp. 2022 + * + * SPDX-License-Identifier: Apache-2.0 + */ +package com.ibm.fhir.core.util; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import com.ibm.fhir.core.FHIRVersionParam; +import com.ibm.fhir.core.ResourceTypeName; + +/** + * Helper methods for working with FHIR Resource Type Strings + */ +public class ResourceTypeHelper { + private static final Set REMOVED_RESOURCE_TYPES = collectRemovedResourceTypes(); + private static final Set R4B_ONLY_RESOURCE_TYPES = collectR4bOnlyResourceTypes(); + + private static final Set ABSTRACT_TYPES = Collections.unmodifiableSet(new HashSet<>( + Arrays.asList( + ResourceTypeName.RESOURCE, + ResourceTypeName.DOMAIN_RESOURCE + ))); + + private static final Set R4_RESOURCES = Collections.unmodifiableSet(new LinkedHashSet<>( + Arrays.stream(ResourceTypeName.values()) + .filter(rtn -> !REMOVED_RESOURCE_TYPES.contains(rtn)) + .filter(rtn -> !R4B_ONLY_RESOURCE_TYPES.contains(rtn)) + .filter(rtn -> !ABSTRACT_TYPES.contains(rtn)) + .map(ResourceTypeName::value) + .collect(Collectors.toList()))); + + private static final Set R4B_RESOURCES = Collections.unmodifiableSet(new LinkedHashSet<>( + Arrays.stream(ResourceTypeName.values()) + .filter(rtn -> !REMOVED_RESOURCE_TYPES.contains(rtn)) + .filter(rtn -> !ABSTRACT_TYPES.contains(rtn)) + .map(ResourceTypeName::value) + .collect(Collectors.toList()))); + + private static final Set R4B_ONLY_RESOURCES = Collections.unmodifiableSet(new LinkedHashSet<>( + R4B_ONLY_RESOURCE_TYPES.stream() + .map(ResourceTypeName::value) + .collect(Collectors.toList()))); + + /** + * @param fhirVersion The value of the MIME-type parameter 'fhirVersion' for the current interaction + * (e.g. "4.3" not "4.3.0") + * @return a set of resource type names that corresponds to the requested fhirVersion + */ + public static Set getResourceTypesFor(FHIRVersionParam fhirVersion) { + switch (fhirVersion) { + case VERSION_43: + return R4B_RESOURCES; + case VERSION_40: + default: + return R4_RESOURCES; + } + } + + /** + * @return the set of resource type names that were either introduced in 4.3.0 (e.g. Ingredient) or changed + * in backwards-incompatible ways in the 4.3.0 release (e.g. Evidence and EvidenceVariable) + */ + public static Set getNewOrBreakingResourceTypeNames() { + return R4B_ONLY_RESOURCES; + } + + private static Set collectRemovedResourceTypes() { + Set set = new HashSet<>(); + set.add(ResourceTypeName.EFFECT_EVIDENCE_SYNTHESIS); + set.add(ResourceTypeName.MEDICINAL_PRODUCT); + set.add(ResourceTypeName.MEDICINAL_PRODUCT_AUTHORIZATION); + set.add(ResourceTypeName.MEDICINAL_PRODUCT_CONTRAINDICATION); + set.add(ResourceTypeName.MEDICINAL_PRODUCT_INDICATION); + set.add(ResourceTypeName.MEDICINAL_PRODUCT_INGREDIENT); + set.add(ResourceTypeName.MEDICINAL_PRODUCT_INTERACTION); + set.add(ResourceTypeName.MEDICINAL_PRODUCT_MANUFACTURED); + set.add(ResourceTypeName.MEDICINAL_PRODUCT_PACKAGED); + set.add(ResourceTypeName.MEDICINAL_PRODUCT_PHARMACEUTICAL); + set.add(ResourceTypeName.MEDICINAL_PRODUCT_UNDESIRABLE_EFFECT); + set.add(ResourceTypeName.RISK_EVIDENCE_SYNTHESIS); + set.add(ResourceTypeName.SUBSTANCE_NUCLEIC_ACID); + set.add(ResourceTypeName.SUBSTANCE_POLYMER); + set.add(ResourceTypeName.SUBSTANCE_PROTEIN); + set.add(ResourceTypeName.SUBSTANCE_REFERENCE_INFORMATION); + set.add(ResourceTypeName.SUBSTANCE_SOURCE_MATERIAL); + set.add(ResourceTypeName.SUBSTANCE_SPECIFICATION); + return set; + } + + private static Set collectR4bOnlyResourceTypes() { + Set set = new HashSet<>(); + set.add(ResourceTypeName.ADMINISTRABLE_PRODUCT_DEFINITION); + set.add(ResourceTypeName.CITATION); + set.add(ResourceTypeName.CLINICAL_USE_DEFINITION); + set.add(ResourceTypeName.EVIDENCE_REPORT); + set.add(ResourceTypeName.INGREDIENT); + set.add(ResourceTypeName.MANUFACTURED_ITEM_DEFINITION); + set.add(ResourceTypeName.MEDICINAL_PRODUCT_DEFINITION); + set.add(ResourceTypeName.NUTRITION_PRODUCT); + set.add(ResourceTypeName.PACKAGED_PRODUCT_DEFINITION); + set.add(ResourceTypeName.REGULATED_AUTHORIZATION); + set.add(ResourceTypeName.SUBSCRIPTION_STATUS); + set.add(ResourceTypeName.SUBSCRIPTION_TOPIC); + set.add(ResourceTypeName.SUBSTANCE_DEFINITION); + // The following resource types existed in R4, but have breaking changes in R4B. + // Because we only support the R4B version, we don't want to advertise these in our 4.0.1 statement. + set.add(ResourceTypeName.DEVICE_DEFINITION); + set.add(ResourceTypeName.EVIDENCE); + set.add(ResourceTypeName.EVIDENCE_VARIABLE); + // TODO: make final decision on whether to lump these in with the breaking resources + // R4B_ONLY_RESOURCES.add(ResourceTypeName.PLAN_DEFINITION); + // R4B_ONLY_RESOURCES.add(ResourceTypeName.ACTIVITY_DEFINITION); + return set; + } +} diff --git a/fhir-core/src/test/java/com/ibm/fhir/core/util/test/ResourceTypeHelperTest.java b/fhir-core/src/test/java/com/ibm/fhir/core/util/test/ResourceTypeHelperTest.java new file mode 100644 index 00000000000..2183ea11cd1 --- /dev/null +++ b/fhir-core/src/test/java/com/ibm/fhir/core/util/test/ResourceTypeHelperTest.java @@ -0,0 +1,57 @@ +/* + * (C) Copyright IBM Corp. 2022 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.core.util.test; + +import static org.testng.Assert.assertEquals; + +import java.util.Set; + +import org.testng.annotations.Test; + +import com.ibm.fhir.core.FHIRVersionParam; +import com.ibm.fhir.core.util.ResourceTypeHelper; + +public class ResourceTypeHelperTest { + @Test + public void testGetNewOrBreakingResourceTypeNames() { + Set newOrBreakingResourceTypeNames = ResourceTypeHelper.getNewOrBreakingResourceTypeNames(); + assertEquals(newOrBreakingResourceTypeNames.size(), 16, "number of new or breaking resource types"); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testGetNewOrBreakingResourceTypeNamesInvalidAddTo() { + Set newOrBreakingResourceTypeNames = ResourceTypeHelper.getNewOrBreakingResourceTypeNames(); + newOrBreakingResourceTypeNames.add("test"); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testGetNewOrBreakingResourceTypeNamesInvalidRemove() { + Set newOrBreakingResourceTypeNames = ResourceTypeHelper.getNewOrBreakingResourceTypeNames(); + newOrBreakingResourceTypeNames.remove("Ingredient"); + } + + @Test + public void testGetResourceTypesFor() { + Set r4Types = ResourceTypeHelper.getResourceTypesFor(FHIRVersionParam.VERSION_40); + assertEquals(r4Types.size(), 125, "number of r4 resource types"); + + Set r4bTypes = ResourceTypeHelper.getResourceTypesFor(FHIRVersionParam.VERSION_43); + assertEquals(r4bTypes.size(), 141, "number of r4b resource types"); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testInvalidAdd() { + Set newOrBreakingResourceTypeNames = ResourceTypeHelper.getResourceTypesFor(FHIRVersionParam.VERSION_40); + newOrBreakingResourceTypeNames.add("test"); + } + + @Test(expectedExceptions = UnsupportedOperationException.class) + public void testInvalidRemove() { + Set newOrBreakingResourceTypeNames = ResourceTypeHelper.getResourceTypesFor(FHIRVersionParam.VERSION_43); + newOrBreakingResourceTypeNames.remove("Ingredient"); + } +} \ No newline at end of file diff --git a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/util/FHIRPersistenceUtil.java b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/util/FHIRPersistenceUtil.java index b0085418fe8..de76093e93f 100644 --- a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/util/FHIRPersistenceUtil.java +++ b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/util/FHIRPersistenceUtil.java @@ -11,17 +11,21 @@ import java.time.format.DateTimeParseException; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.stream.Collectors; import org.owasp.encoder.Encode; import com.ibm.fhir.config.FHIRConfigHelper; import com.ibm.fhir.config.FHIRConfiguration; import com.ibm.fhir.config.FHIRRequestContext; +import com.ibm.fhir.config.Interaction; import com.ibm.fhir.config.PropertyGroup; +import com.ibm.fhir.config.ResourcesConfigAdapter; +import com.ibm.fhir.core.FHIRVersionParam; import com.ibm.fhir.core.HTTPReturnPreference; +import com.ibm.fhir.core.util.ResourceTypeHelper; import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.resource.Resource.Builder; @@ -29,6 +33,7 @@ import com.ibm.fhir.model.type.Id; import com.ibm.fhir.model.type.Instant; import com.ibm.fhir.model.type.Meta; +import com.ibm.fhir.model.type.code.FHIRVersion; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.model.util.FHIRUtil; import com.ibm.fhir.model.util.ModelSupport; @@ -43,10 +48,6 @@ public class FHIRPersistenceUtil { private static final Logger log = Logger.getLogger(FHIRPersistenceUtil.class.getName()); - private static final List ALL_RESOURCE_TYPES = ModelSupport.getResourceTypes(false).stream() - .map(r -> ModelSupport.getTypeName(r)) - .collect(Collectors.toList()); - private FHIRPersistenceUtil() { // No operation } @@ -94,14 +95,26 @@ public static FHIRHistoryContext parseHistoryParameters(Map /** - * Parse history parameters into a FHIRHistoryContext + * Parse history parameters into a FHIRHistoryContext with a fhirVersion of 4.3.0 + * + * @see #parseSystemHistoryParameters(Map, boolean, FHIRVersion) + */ + public static FHIRSystemHistoryContext parseSystemHistoryParameters(Map> queryParameters, boolean lenient) + throws FHIRPersistenceException { + return parseSystemHistoryParameters(queryParameters, lenient, FHIRVersionParam.VERSION_43); + } + + /** + * Parse history parameters into a FHIRHistoryContext for a given fhirVersion * * @param queryParameters * @param lenient + * @param fhirVersion * @return * @throws FHIRPersistenceException */ - public static FHIRSystemHistoryContext parseSystemHistoryParameters(Map> queryParameters, boolean lenient) throws FHIRPersistenceException { + public static FHIRSystemHistoryContext parseSystemHistoryParameters(Map> queryParameters, boolean lenient, + FHIRVersionParam fhirVersion) throws FHIRPersistenceException { log.entering(FHIRPersistenceUtil.class.getName(), "parseSystemHistoryParameters"); FHIRSystemHistoryContextImpl context = new FHIRSystemHistoryContextImpl(); context.setLenient(lenient); @@ -128,11 +141,17 @@ public static FHIRSystemHistoryContext parseSystemHistoryParameters(Map supportedResourceTypes = isOpen ? ALL_RESOURCE_TYPES : FHIRConfigHelper.getSupportedResourceTypes(); + PropertyGroup rsrcsGroup = FHIRConfigHelper.getPropertyGroup(FHIRConfiguration.PROPERTY_RESOURCES); + ResourcesConfigAdapter configAdapter = new ResourcesConfigAdapter(rsrcsGroup, fhirVersion); + Set supportedResourceTypes = configAdapter.getSupportedResourceTypes(Interaction.HISTORY); for (String resType : supportedResourceTypes) { - if (isHistoryEnabled(resType)) { - context.addResourceType(resType); - } + context.addResourceType(resType); } } } diff --git a/fhir-persistence/src/test/java/com/ibm/fhir/persistence/util/FHIRPersistenceUtilTest.java b/fhir-persistence/src/test/java/com/ibm/fhir/persistence/util/FHIRPersistenceUtilTest.java index 26bf02d57aa..0f44147fe3a 100644 --- a/fhir-persistence/src/test/java/com/ibm/fhir/persistence/util/FHIRPersistenceUtilTest.java +++ b/fhir-persistence/src/test/java/com/ibm/fhir/persistence/util/FHIRPersistenceUtilTest.java @@ -24,6 +24,7 @@ import com.ibm.fhir.config.FHIRConfiguration; import com.ibm.fhir.config.FHIRRequestContext; +import com.ibm.fhir.core.FHIRVersionParam; import com.ibm.fhir.exception.FHIRException; import com.ibm.fhir.model.resource.Patient; import com.ibm.fhir.model.type.HumanName; @@ -35,9 +36,8 @@ */ public class FHIRPersistenceUtilTest { - @BeforeClass(alwaysRun = true) + @BeforeClass public void setUp() throws Exception { - // Note: this assumes that the concrete test classes will be in a project that is peer to the fhir-persistence module // TODO: it would be better for our unit tests if we could load config files from the classpath FHIRConfiguration.setConfigHome("target/test-classes"); } @@ -161,6 +161,80 @@ public void testParseSystemHistoryParameters_explicitTypes() throws FHIRExceptio } } + /** + * Test the parsing of system history parameters into a HistoryContext + * with no implicit or explicit _type filter. + */ + @Test + public void testParseSystemHistoryParameters_r4() throws FHIRException { + String originalTenantId = FHIRRequestContext.get().getTenantId(); + try { + // change to a tenant (any tenant) that has `useImplicitTypeScopingForWholeSystemInteractions = false` + FHIRRequestContext.get().setTenantId("all"); + + // no explicit _type param + Map> params = new HashMap<>(); + FHIRSystemHistoryContext historyContext = FHIRPersistenceUtil.parseSystemHistoryParameters(params, false, FHIRVersionParam.VERSION_40); + + assertTrue(historyContext.getResourceTypes().isEmpty(), historyContext.getResourceTypes().toString()); + } finally { + FHIRRequestContext.get().setTenantId(originalTenantId); + } + } + + /** + * Test the parsing of system history parameters into a HistoryContext + * with an implicit _type filter. + */ + @Test + public void testParseSystemHistoryParameters_implicitTypes_r4() throws FHIRException { + String originalTenantId = FHIRRequestContext.get().getTenantId(); + try { + // change to a tenant (any tenant) that has `useImplicitTypeScopingForWholeSystemInteractions = true` + FHIRRequestContext.get().setTenantId("default"); + + // no explicit _type param + Map> params = new HashMap<>(); + FHIRSystemHistoryContext historyContext = FHIRPersistenceUtil.parseSystemHistoryParameters(params, false, FHIRVersionParam.VERSION_40); + + assertEquals(historyContext.getResourceTypes().size(), 125, "implicitly scoped to all R4 resource types"); + } finally { + FHIRRequestContext.get().setTenantId(originalTenantId); + } + } + + /** + * Test the parsing of system history parameters into a HistoryContext + * with an explicit _type filter. + */ + @Test + public void testParseSystemHistoryParameters_explicitTypes_r4() throws FHIRException { + String originalTenantId = FHIRRequestContext.get().getTenantId(); + + // add a _type parameter value with a list of resource types + List explicitTypes = Arrays.asList("Patient","Device","Observation","Condition","Medication"); + Map> params = new HashMap<>(); + params.put("_type", Collections.singletonList(String.join(",",explicitTypes))); + + try { + // change to a tenant (any tenant) that has `useImplicitTypeScopingForWholeSystemInteractions = true` + FHIRRequestContext.get().setTenantId("default"); + FHIRSystemHistoryContext historyContext = FHIRPersistenceUtil.parseSystemHistoryParameters(params, false, FHIRVersionParam.VERSION_40); + assertEquals(historyContext.getResourceTypes(), explicitTypes); + + // change to a tenant (any tenant) that has `useImplicitTypeScopingForWholeSystemInteractions = false` + FHIRRequestContext.get().setTenantId("all"); + historyContext = FHIRPersistenceUtil.parseSystemHistoryParameters(params, false, FHIRVersionParam.VERSION_40); + assertEquals(historyContext.getResourceTypes(), explicitTypes); + } finally { + FHIRRequestContext.get().setTenantId(originalTenantId); + } + } + + /** + * Test the parsing of system history parameters into a HistoryContext + * when multiple _type parameters are specified. + */ @Test public void testParseSystemHistoryParameters_multipleTypeParam() throws FHIRException { String originalTenantId = FHIRRequestContext.get().getTenantId(); diff --git a/fhir-search/src/main/java/com/ibm/fhir/search/util/SearchUtil.java b/fhir-search/src/main/java/com/ibm/fhir/search/util/SearchUtil.java index 147f6546925..c5fbe9f95db 100644 --- a/fhir-search/src/main/java/com/ibm/fhir/search/util/SearchUtil.java +++ b/fhir-search/src/main/java/com/ibm/fhir/search/util/SearchUtil.java @@ -34,9 +34,13 @@ import com.ibm.fhir.config.FHIRConfigHelper; import com.ibm.fhir.config.FHIRConfiguration; import com.ibm.fhir.config.FHIRRequestContext; +import com.ibm.fhir.config.Interaction; import com.ibm.fhir.config.PropertyGroup; import com.ibm.fhir.config.PropertyGroup.PropertyEntry; +import com.ibm.fhir.config.ResourcesConfigAdapter; import com.ibm.fhir.core.FHIRConstants; +import com.ibm.fhir.core.FHIRVersionParam; +import com.ibm.fhir.core.util.ResourceTypeHelper; import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.resource.CodeSystem; import com.ibm.fhir.model.resource.CodeSystem.Concept; @@ -136,15 +140,9 @@ public class SearchUtil { private static final String IBM_COMPOSITE_PREFIX = "ibm_composite_"; - private static final List ALL_RESOURCE_TYPES = ModelSupport.getResourceTypes(false).stream() - .map(r -> ModelSupport.getTypeName(r)) - .collect(Collectors.toList()); - // The functionality is split into a new class. private static final Sort sort = new Sort(); - - private SearchUtil() { // No Operation // Hides the Initialization @@ -374,11 +372,27 @@ public static Map> extractParameterValues(Re public static FHIRSearchContext parseQueryParameters(Class resourceType, Map> queryParameters) throws Exception { - return parseQueryParameters(resourceType, queryParameters, false, true); + return parseQueryParameters(resourceType, queryParameters, false, true, FHIRVersionParam.VERSION_43); } public static FHIRSearchContext parseQueryParameters(Class resourceType, Map> queryParameters, boolean lenient, boolean includeResource) throws Exception { + return parseQueryParameters(resourceType, queryParameters, lenient, includeResource, FHIRVersionParam.VERSION_43); + } + + /** + * Parse the passed query parameters into a FHIRSeachContext according to the given options + * + * @param resourceType + * @param queryParameters + * @param lenient + * @param includeResource + * @param fhirVersion + * @return + * @throws Exception + */ + public static FHIRSearchContext parseQueryParameters(Class resourceType, Map> queryParameters, + boolean lenient, boolean includeResource, FHIRVersionParam fhirVersion) throws Exception { FHIRSearchContext context = FHIRSearchContextFactory.createSearchContext(); context.setLenient(lenient); @@ -419,6 +433,11 @@ public static FHIRSearchContext parseQueryParameters(Class resourceType, if (!isSearchEnabled(resType)) { manageException("search interaction is not supported for _type parameter value: " + Encode.forHtml(resType), IssueType.NOT_SUPPORTED, context, false); + } else if (fhirVersion == FHIRVersionParam.VERSION_40 && + ResourceTypeHelper.getNewOrBreakingResourceTypeNames().contains(resType)) { + String msg = "fhirVersion 4.0 interaction for _type parameter value: '" + resType + + "' is not supported"; + manageException(msg, IssueType.NOT_SUPPORTED, context, false); } else { resourceTypes.add(resType); } @@ -429,14 +448,9 @@ public static FHIRSearchContext parseQueryParameters(Class resourceType, if (resourceTypes.isEmpty()) { Boolean implicitTypeScoping = FHIRConfigHelper.getBooleanProperty(FHIRConfiguration.PROPERTY_WHOLE_SYSTEM_TYPE_SCOPING, true); if (implicitTypeScoping) { - Boolean isOpen = FHIRConfigHelper.getBooleanProperty(FHIRConfiguration.PROPERTY_RESOURCES + "/" - + FHIRConfiguration.PROPERTY_FIELD_RESOURCES_OPEN, true); - List supportedResourceTypes = isOpen ? ALL_RESOURCE_TYPES : FHIRConfigHelper.getSupportedResourceTypes(); - for (String resType : supportedResourceTypes) { - if (isSearchEnabled(resType)) { - resourceTypes.add(resType); - } - } + PropertyGroup rsrcsGroup = FHIRConfigHelper.getPropertyGroup(FHIRConfiguration.PROPERTY_RESOURCES); + ResourcesConfigAdapter configAdapter = new ResourcesConfigAdapter(rsrcsGroup, fhirVersion); + resourceTypes.addAll(configAdapter.getSupportedResourceTypes(Interaction.SEARCH)); } } } @@ -1234,11 +1248,12 @@ public static Map getSearchParameters(String resourceTy * @param queryParameters the query parameters * @param interaction read or vread * @param lenient true if lenient, false if strict + * @param fhirVersion * @return the FHIR search context * @throws Exception an exception */ public static FHIRSearchContext parseReadQueryParameters(Class resourceType, - Map> queryParameters, String interaction, boolean lenient) throws Exception { + Map> queryParameters, String interaction, boolean lenient, FHIRVersionParam fhirVersion) throws Exception { String resourceTypeName = resourceType.getSimpleName(); // Read and vRead only allow general search parameters @@ -1252,25 +1267,18 @@ public static FHIRSearchContext parseReadQueryParameters(Class resourceType, log.log(Level.FINE, "Error while parsing search parameter '" + nonGeneralParam + "' for resource type " + resourceTypeName, se); } - return parseCompartmentQueryParameters(null, null, resourceType, queryParameters, lenient, true); + return parseCompartmentQueryParameters(null, null, resourceType, queryParameters, lenient, true, fhirVersion); } public static FHIRSearchContext parseCompartmentQueryParameters(String compartmentName, String compartmentLogicalId, Class resourceType, Map> queryParameters) throws Exception { - return parseCompartmentQueryParameters(compartmentName, compartmentLogicalId, resourceType, queryParameters, true, true); + return parseCompartmentQueryParameters(compartmentName, compartmentLogicalId, resourceType, queryParameters, true, true, FHIRVersionParam.VERSION_43); } - /** - * Check the configuration to see if the flag enabling the compartment search - * optimization. Defaults to false so the behavior won't change unless it - * is explicitly enabled in fhir-server-config. This is important, because - * existing data must be reindexed (see $reindex custom operation) to - * generate values for the ibm-internal compartment relationship params. - * @return - */ - public static boolean useStoredCompartmentParam() { - return FHIRConfigHelper.getBooleanProperty(FHIRConfiguration.PROPERTY_USE_STORED_COMPARTMENT_PARAM, true); + public static FHIRSearchContext parseCompartmentQueryParameters(String compartmentName, String compartmentLogicalId, + Class resourceType, Map> queryParameters, FHIRVersionParam fhirVersion) throws Exception { + return parseCompartmentQueryParameters(compartmentName, compartmentLogicalId, resourceType, queryParameters, true, true, fhirVersion); } /** @@ -1282,15 +1290,17 @@ public static boolean useStoredCompartmentParam() { * Whether to ignore unknown or unsupported parameter * @param includeResource * Whether to include the resource from the result (return handling prefer != minimal) + * @param fhirVersion * @return * @throws Exception */ public static FHIRSearchContext parseCompartmentQueryParameters(String compartmentName, String compartmentLogicalId, - Class resourceType, Map> queryParameters, boolean lenient, boolean includeResource) throws Exception { + Class resourceType, Map> queryParameters, boolean lenient, boolean includeResource, + FHIRVersionParam fhirVersion) throws Exception { Set compartmentLogicalIds = Collections.singleton(compartmentLogicalId); QueryParameter inclusionCriteria = buildInclusionCriteria(compartmentName, compartmentLogicalIds, resourceType.getSimpleName()); - FHIRSearchContext context = parseQueryParameters(resourceType, queryParameters, lenient, includeResource); + FHIRSearchContext context = parseQueryParameters(resourceType, queryParameters, lenient, includeResource, fhirVersion); // Add the inclusion criteria to the front of the search parameter list if (inclusionCriteria != null) { @@ -1352,6 +1362,18 @@ public static QueryParameter buildInclusionCriteria(String compartmentName, Set< return rootParameter; } + /** + * Check the configuration to see if the flag enabling the compartment search + * optimization. Defaults to false so the behavior won't change unless it + * is explicitly enabled in fhir-server-config. This is important, because + * existing data must be reindexed (see $reindex custom operation) to + * generate values for the ibm-internal compartment relationship params. + * @return + */ + public static boolean useStoredCompartmentParam() { + return FHIRConfigHelper.getBooleanProperty(FHIRConfiguration.PROPERTY_USE_STORED_COMPARTMENT_PARAM, true); + } + private static SearchConstants.Prefix getPrefix(String s) throws FHIRSearchException { SearchConstants.Prefix returnPrefix = null; diff --git a/fhir-search/src/test/java/com/ibm/fhir/search/test/CompartmentParseQueryParmsTest.java b/fhir-search/src/test/java/com/ibm/fhir/search/test/CompartmentParseQueryParmsTest.java index ee6ac6d3275..6f5f0ed713e 100644 --- a/fhir-search/src/test/java/com/ibm/fhir/search/test/CompartmentParseQueryParmsTest.java +++ b/fhir-search/src/test/java/com/ibm/fhir/search/test/CompartmentParseQueryParmsTest.java @@ -24,6 +24,7 @@ import org.testng.annotations.Test; import com.ibm.fhir.config.FHIRRequestContext; +import com.ibm.fhir.core.FHIRVersionParam; import com.ibm.fhir.model.resource.CommunicationRequest; import com.ibm.fhir.model.resource.Condition; import com.ibm.fhir.model.resource.Device; @@ -230,7 +231,7 @@ public void testCompartmentWithFakeQueryParm(boolean useStoredCompartmentParam) assertFalse(selfUri.contains(queryString), selfUri + " contain unexpectedExceptions query parameter 'fakeParameter'"); try { - SearchUtil.parseCompartmentQueryParameters(compartmentName, compartmentLogicalId, resourceType, queryParameters, false, true); + SearchUtil.parseCompartmentQueryParameters(compartmentName, compartmentLogicalId, resourceType, queryParameters, false, true, FHIRVersionParam.VERSION_43); fail("expectedExceptions parseQueryParameters to throw due to strict mode but it didn't."); } catch (Exception e) { assertTrue(e instanceof FHIRSearchException); diff --git a/fhir-server-test/src/test/java/com/ibm/fhir/server/test/CapabilitiesVersionTest.java b/fhir-server-test/src/test/java/com/ibm/fhir/server/test/CapabilitiesVersionTest.java index f320e55874e..49ce7da4dd0 100644 --- a/fhir-server-test/src/test/java/com/ibm/fhir/server/test/CapabilitiesVersionTest.java +++ b/fhir-server-test/src/test/java/com/ibm/fhir/server/test/CapabilitiesVersionTest.java @@ -7,8 +7,8 @@ package com.ibm.fhir.server.test; import static com.ibm.fhir.core.FHIRMediaType.FHIR_VERSION_PARAMETER; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; -import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import static org.testng.AssertJUnit.assertNotNull; @@ -28,6 +28,7 @@ import com.ibm.fhir.model.resource.CapabilityStatement; import com.ibm.fhir.model.resource.CapabilityStatement.Rest; import com.ibm.fhir.model.resource.CapabilityStatement.Rest.Resource; +import com.ibm.fhir.model.type.code.FHIRVersion; import com.ibm.fhir.model.type.code.ResourceType; import com.ibm.fhir.path.exception.FHIRPathException; import com.ibm.fhir.validation.exception.FHIRValidationException; @@ -62,9 +63,9 @@ public class CapabilitiesVersionTest extends FHIRServerTestBase { * Verify the 'metadata' API. */ @Test(dataProvider = "dataMethod") - public void testWithTenantAndFHIRVersion(String tenant, String fhirVersion) throws FHIRPathException, FHIRValidationException { + public void testWithTenantAndFHIRVersion(String tenant, String requestFhirVersion, FHIRVersion expectedVersion) throws FHIRPathException, FHIRValidationException { WebTarget target = getWebTarget(); - Map fhirVersionParameterMap = (fhirVersion == null) ? null : Collections.singletonMap(FHIR_VERSION_PARAMETER, fhirVersion); + Map fhirVersionParameterMap = (requestFhirVersion == null) ? null : Collections.singletonMap(FHIR_VERSION_PARAMETER, requestFhirVersion); MediaType mediaType = new MediaType("application", FHIRMediaType.SUBTYPE_FHIR_JSON, fhirVersionParameterMap); Response response = target.path("metadata") @@ -75,10 +76,7 @@ public void testWithTenantAndFHIRVersion(String tenant, String fhirVersion) thro CapabilityStatement conf = response.readEntity(CapabilityStatement.class); assertNotNull(conf); - assertNotNull(conf.getFhirVersion()); - if (fhirVersion != null) { - assertTrue(conf.getFhirVersion().getValue().startsWith(fhirVersion)); - } + assertEquals(conf.getFhirVersion(), expectedVersion); switch (conf.getFhirVersion().getValueAsEnum()) { case VERSION_4_0_1: @@ -99,26 +97,20 @@ public void testWithTenantAndFHIRVersion(String tenant, String fhirVersion) thro } /** - * tenant, fhirVersion + * tenant, requestedVersion, expectedVersion */ @DataProvider public static Object[][] dataMethod() { - String[] tenants = new String[] { - "default", // defaultFhirVersion=4.0 - "tenant1", // defaultFhirVersion=4.3 - "tenant2" // no defaultFhirVersion configured - }; - String[] versions = new String[] {null, "4.0", "4.3"}; - - // compute the cartesian product - Object[][] inputs = new Object[tenants.length * versions.length][2]; - int i = 0; - for (String tenant : tenants) { - for (String version : versions) { - inputs[i++] = new Object[] {tenant, version}; - } - } - - return inputs; + return new Object[][] { + { "default", null, FHIRVersion.VERSION_4_0_1 }, + { "default", "4.0", FHIRVersion.VERSION_4_0_1 }, + { "default", "4.3", FHIRVersion.VERSION_4_3_0_CIBUILD }, + { "tenant1", null, FHIRVersion.VERSION_4_3_0_CIBUILD }, + { "tenant1", "4.0", FHIRVersion.VERSION_4_0_1 }, + { "tenant1", "4.3", FHIRVersion.VERSION_4_3_0_CIBUILD }, + { "tenant2", null, FHIRVersion.VERSION_4_0_1 }, + { "tenant2", "4.0", FHIRVersion.VERSION_4_0_1 }, + { "tenant2", "4.3", FHIRVersion.VERSION_4_3_0_CIBUILD } + }; } } diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Batch.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Batch.java index 7009f6b491c..0bedc290930 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Batch.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Batch.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2016, 2021 + * (C) Copyright IBM Corp. 2016, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -28,7 +28,6 @@ import com.ibm.fhir.config.FHIRRequestContext; import com.ibm.fhir.core.FHIRConstants; - import com.ibm.fhir.core.FHIRMediaType; import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.resource.Bundle; @@ -86,7 +85,7 @@ public Response bundle(Resource resource, @HeaderParam(FHIRConstants.UPDATE_IF_M throw buildRestException(msg, IssueType.INVALID); } - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); responseBundle = helper.doBundle(inputBundle, updateOnlyIfModified); status = Status.OK; return Response.ok(responseBundle).build(); diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Capabilities.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Capabilities.java index 24c8d4d520a..05f9777bb07 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Capabilities.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Capabilities.java @@ -41,7 +41,6 @@ import javax.ws.rs.Consumes; import javax.ws.rs.DefaultValue; import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; @@ -55,6 +54,7 @@ import com.ibm.fhir.config.FHIRConfiguration; import com.ibm.fhir.config.PropertyGroup; import com.ibm.fhir.core.FHIRMediaType; +import com.ibm.fhir.core.FHIRVersionParam; import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.format.Format; import com.ibm.fhir.model.resource.CapabilityStatement; @@ -160,7 +160,7 @@ public Capabilities() throws Exception { @GET @Path("metadata") - public Response capabilities(@QueryParam("mode") @DefaultValue("full") String mode, @HeaderParam("accept") String accept) { + public Response capabilities(@QueryParam("mode") @DefaultValue("full") String mode) { log.entering(this.getClass().getName(), "capabilities()"); try { Date startTime = new Date(); @@ -177,8 +177,8 @@ public Response capabilities(@QueryParam("mode") @DefaultValue("full") String mo Map cacheAsMap = CacheManager.getCacheAsMap(CAPABILITY_STATEMENT_CACHE_NAME, configuration); CacheManager.reportCacheStats(log, CAPABILITY_STATEMENT_CACHE_NAME); - FHIRVersion fhirVersion = getFhirVersion(); - String cacheKey = mode + "-" + fhirVersion.getValue(); + FHIRVersionParam fhirVersion = getFhirVersion(); + String cacheKey = mode + "-" + fhirVersion.value(); Resource capabilityStatement = cacheAsMap.computeIfAbsent(cacheKey, k -> computeCapabilityStatement(mode, fhirVersion)); RestAuditLogger.logMetadata(httpServletRequest, startTime, new Date(), Response.Status.OK); @@ -209,7 +209,7 @@ private boolean isValidMode(String mode) { return "full".equals(mode) || "normative".equals(mode) || "terminology".equals(mode); } - private Resource computeCapabilityStatement(String mode, FHIRVersion fhirVersion) { + private Resource computeCapabilityStatement(String mode, FHIRVersionParam fhirVersion) { try { switch (mode) { case "terminology": @@ -315,7 +315,7 @@ private List buildCodeSystem() { * * @throws Exception */ - private CapabilityStatement buildCapabilityStatement(FHIRVersion fhirVersion) throws Exception { + private CapabilityStatement buildCapabilityStatement(FHIRVersionParam fhirVersion) throws Exception { // Retrieve the "resources" config property group. PropertyGroup rsrcsGroup = FHIRConfigHelper.getPropertyGroup(FHIRConfiguration.PROPERTY_RESOURCES); @@ -551,7 +551,7 @@ private CapabilityStatement buildCapabilityStatement(FHIRVersion fhirVersion) th .status(PublicationStatus.ACTIVE) .date(DateTime.now(ZoneOffset.UTC)) .kind(CapabilityStatementKind.INSTANCE) - .fhirVersion(fhirVersion) + .fhirVersion(fhirVersion == FHIRVersionParam.VERSION_43 ? FHIRVersion.VERSION_4_3_0_CIBUILD : FHIRVersion.VERSION_4_0_1) .format(format) .patchFormat(Code.of(FHIRMediaType.APPLICATION_JSON_PATCH), Code.of(FHIRMediaType.APPLICATION_FHIR_JSON), @@ -653,11 +653,12 @@ private List convertStringList(List stri } /** + * TODO: replace this with the new ResourcesConfigAdapter * @param rsrcsGroup the "resources" propertyGroup from the server configuration * @return a list of resource types to support * @throws Exception */ - private List getSupportedResourceTypes(PropertyGroup rsrcsGroup, FHIRVersion fhirVersion) throws Exception { + private List getSupportedResourceTypes(PropertyGroup rsrcsGroup, FHIRVersionParam fhirVersion) throws Exception { final List resourceTypes = new ArrayList<>(); if (rsrcsGroup == null) { @@ -672,7 +673,7 @@ private List getSupportedResourceTypes(PropertyGroup rsrcsGr } } - if (fhirVersion.getValueAsEnum() == FHIRVersion.Value.VERSION_4_0_1) { + if (fhirVersion == FHIRVersionParam.VERSION_40) { resourceTypes.removeAll(R4B_ONLY_RESOURCES); } return resourceTypes; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Create.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Create.java index d3a0926f3f5..9e395fb32af 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Create.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Create.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2016, 2021 + * (C) Copyright IBM Corp. 2016, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -71,7 +71,7 @@ public Response create(@PathParam("type") String type, Resource resource, @Heade checkInitComplete(); checkType(type); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); ior = helper.doCreate(type, resource, ifNoneExist); ResponseBuilder response = diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Delete.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Delete.java index 3ce027bb9ef..37abda71559 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Delete.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Delete.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2016, 2021 + * (C) Copyright IBM Corp. 2016, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -58,7 +58,7 @@ public Response delete(@PathParam("type") String type, @PathParam("id") String i checkInitComplete(); checkType(type); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); ior = helper.doDelete(type, id, null); status = ior.getStatus(); return buildResponse(ior); @@ -103,7 +103,7 @@ public Response conditionalDelete(@PathParam("type") String type) throws Excepti throw buildRestException(msg, IssueType.INVALID); } - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); ior = helper.doDelete(type, null, searchQueryString); status = ior.getStatus(); return buildResponse(ior); diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/FHIRResource.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/FHIRResource.java index 4b5baf5864e..290fb32f338 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/FHIRResource.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/FHIRResource.java @@ -42,7 +42,7 @@ import com.ibm.fhir.config.FHIRRequestContext; import com.ibm.fhir.config.PropertyGroup; import com.ibm.fhir.core.FHIRConstants; -import com.ibm.fhir.core.FHIRMediaType; +import com.ibm.fhir.core.FHIRVersionParam; import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.format.Format; import com.ibm.fhir.model.generator.FHIRGenerator; @@ -53,7 +53,6 @@ import com.ibm.fhir.model.type.Code; import com.ibm.fhir.model.type.CodeableConcept; import com.ibm.fhir.model.type.Extension; -import com.ibm.fhir.model.type.code.FHIRVersion; import com.ibm.fhir.model.type.code.IssueSeverity; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.model.util.FHIRUtil; @@ -508,12 +507,12 @@ protected FHIROperationException buildUnsupportedResourceTypeException(String re * * @return the corresponding FHIRVersion for the com.ibm.fhir.server.fhirVersion request context attribute */ - protected FHIRVersion getFhirVersion() { - String fhirVersionString = (String) httpServletRequest.getAttribute(FHIRVersionRequestFilter.FHIR_VERSION_PROP); - if (FHIRMediaType.VERSION_43.equals(fhirVersionString)) { - return FHIRVersion.VERSION_4_3_0_CIBUILD; - } else { - return FHIRVersion.VERSION_4_0_1; + protected FHIRVersionParam getFhirVersion() { + FHIRVersionParam fhirVersion = (FHIRVersionParam) httpServletRequest.getAttribute(FHIRVersionRequestFilter.FHIR_VERSION_PROP); + if (fhirVersion == null) { + log.warning("Missing request context attribute " + FHIRVersionRequestFilter.FHIR_VERSION_PROP + "; using 4.0"); + fhirVersion = FHIRVersionParam.VERSION_40; } + return fhirVersion; } } diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/History.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/History.java index 4324e7861e1..752687c8193 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/History.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/History.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2016, 2021 + * (C) Copyright IBM Corp. 2016, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -55,7 +55,7 @@ public Response history(@PathParam("type") String type, @PathParam("id") String checkInitComplete(); checkType(type); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); bundle = helper.doHistory(type, id, uriInfo.getQueryParameters(), getRequestUri()); status = Status.OK; return Response.status(status).entity(bundle).build(); @@ -88,7 +88,7 @@ public Response systemHistory() { try { checkInitComplete(); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); bundle = helper.doHistory(uriInfo.getQueryParameters(), getRequestUri()); status = Status.OK; return Response.status(status).entity(bundle).build(); @@ -112,7 +112,7 @@ public Response systemHistory() { @GET @Path("{type}/_history") - public Response systemHistory(@PathParam("type") String type) { + public Response typeHistory(@PathParam("type") String type) { log.entering(this.getClass().getName(), "systemHistory(String)"); Date startTime = new Date(); Response.Status status = null; @@ -122,7 +122,7 @@ public Response systemHistory(@PathParam("type") String type) { checkInitComplete(); checkType(type); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); bundle = helper.doHistory(uriInfo.getQueryParameters(), getRequestUri(), type); status = Status.OK; return Response.status(status).entity(bundle).build(); diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Operation.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Operation.java index 014946b910d..0f9c8cfe9e4 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Operation.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Operation.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2016, 2021 + * (C) Copyright IBM Corp. 2016, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -79,7 +79,7 @@ public Response invoke(@PathParam("operationName") String operationName) { operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_REQUEST, httpServletRequest); operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.GET); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, null, null, null, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, null, result); @@ -122,7 +122,7 @@ public Response invoke(@PathParam("operationName") String operationName, Resourc operationContext.setProperty(FHIROperationContext.PROPNAME_SECURITY_CONTEXT, securityContext); operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_REQUEST, httpServletRequest); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, null, null, null, resource, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, null, result); @@ -166,7 +166,7 @@ public Response invokeDelete(@PathParam("operationName") String operationName) { operationContext.setProperty(FHIROperationContext.PROPNAME_SECURITY_CONTEXT, securityContext); operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_REQUEST, httpServletRequest); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, null, null, null, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, null, result); @@ -211,7 +211,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_REQUEST, httpServletRequest); operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.GET); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, resourceTypeName, null, null, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); @@ -256,7 +256,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_REQUEST, httpServletRequest); operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.POST); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, resourceTypeName, null, null, resource, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); @@ -315,7 +315,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_REQUEST, httpServletRequest); operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.GET); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, null, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); @@ -361,7 +361,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_REQUEST, httpServletRequest); operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.POST); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, null, resource, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); @@ -408,7 +408,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_REQUEST, httpServletRequest); operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.GET); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, versionId, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); @@ -455,7 +455,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_REQUEST, httpServletRequest); operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.POST); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, versionId, resource, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Patch.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Patch.java index 753231f1e67..c6763d61873 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Patch.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Patch.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2016, 2021 + * (C) Copyright IBM Corp. 2016, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -78,7 +78,7 @@ public Response patch(@PathParam("type") String type, @PathParam("id") String id FHIRPatch patch = createPatch(array); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); ior = helper.doPatch(type, id, patch, ifMatch, null, onlyIfModified); status = ior.getStatus(); @@ -136,7 +136,7 @@ public Response patch(@PathParam("type") String type, @PathParam("id") String id throw buildRestException(e.getMessage(), IssueType.INVALID); } - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); ior = helper.doPatch(type, id, patch, ifMatch, null, onlyIfModified); ResponseBuilder response = @@ -199,7 +199,7 @@ public Response conditionalPatch(@PathParam("type") String type, JsonArray array throw buildRestException(msg, IssueType.INVALID); } - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); ior = helper.doPatch(type, null, patch, ifMatch, searchQueryString, onlyIfModified); ResponseBuilder response = @@ -267,7 +267,7 @@ public Response conditionalPatch(@PathParam("type") String type, Parameters para throw buildRestException(msg, IssueType.INVALID); } - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); ior = helper.doPatch(type, null, patch, ifMatch, searchQueryString, onlyIfModified); status = ior.getStatus(); diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Read.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Read.java index aeb72202e5b..668f3a673e5 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Read.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Read.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2016, 2021 + * (C) Copyright IBM Corp. 2016, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -65,7 +65,7 @@ public Response read(@PathParam("type") String type, @PathParam("id") String id, MultivaluedMap queryParameters = uriInfo.getQueryParameters(); long modifiedSince = parseIfModifiedSince(); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); Resource resource = helper.doRead(type, id, true, false, null, queryParameters).getResource(); int version2Match = -1; // Support ETag value with or without " (and W/) diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Search.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Search.java index b21c3538fd8..80d88ef962c 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Search.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Search.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2016, 2021 + * (C) Copyright IBM Corp. 2016, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -68,7 +68,7 @@ private Response doSearch(String type) { checkType(type); queryParameters = uriInfo.getQueryParameters(); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); bundle = helper.doSearch(type, null, null, queryParameters, getRequestUri(), null); status = Status.OK; return Response.status(status).entity(bundle).build(); @@ -117,7 +117,7 @@ private Response doSearchCompartment(String compartment, String compartmentId, S checkType(type); queryParameters = uriInfo.getQueryParameters(); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); bundle = helper.doSearch(type, compartment, compartmentId, queryParameters, getRequestUri(), null); status = Status.OK; return Response.status(status).entity(bundle).build(); @@ -162,7 +162,7 @@ private Response doSearchAll() { checkInitComplete(); queryParameters = uriInfo.getQueryParameters(); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); bundle = helper.doSearch("Resource", null, null, queryParameters, getRequestUri(), null); status = Status.OK; return Response.status(status).entity(bundle).build(); diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Update.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Update.java index 456dd515dfa..da792aff6f6 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Update.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Update.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2016, 2021 + * (C) Copyright IBM Corp. 2016, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -71,7 +71,7 @@ public Response update(@PathParam("type") String type, @PathParam("id") String i checkType(type); Integer ifNoneMatch = encodeIfNoneMatch(ifNoneMatchHdr); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); ior = helper.doUpdate(type, id, resource, ifMatch, null, onlyIfModified, ifNoneMatch); ResponseBuilder response = Response.ok() @@ -143,7 +143,7 @@ public Response conditionalUpdate(@PathParam("type") String type, Resource resou throw buildRestException(msg, IssueType.INVALID); } - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); ior = helper.doUpdate(type, null, resource, ifMatch, searchQueryString, onlyIfModified, IF_NONE_MATCH_NULL); ResponseBuilder response = diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/VRead.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/VRead.java index 9ddd123344c..a49fc283bf6 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/VRead.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/VRead.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2016, 2021 + * (C) Copyright IBM Corp. 2016, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -60,7 +60,7 @@ public Response vread(@PathParam("type") String type, @PathParam("id") String id MultivaluedMap queryParameters = uriInfo.getQueryParameters(); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getFhirVersion()); Resource resource = helper.doVRead(type, id, vid, queryParameters); status = Status.OK; ResponseBuilder response = Response.ok().entity(resource); diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/filters/FHIRVersionRequestFilter.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/filters/FHIRVersionRequestFilter.java index 1df71b920e7..45e09bb1435 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/filters/FHIRVersionRequestFilter.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/filters/FHIRVersionRequestFilter.java @@ -6,8 +6,8 @@ package com.ibm.fhir.server.resources.filters; -import static com.ibm.fhir.core.FHIRMediaType.VERSION_40; -import static com.ibm.fhir.core.FHIRMediaType.VERSION_43; +import static com.ibm.fhir.core.FHIRVersionParam.VERSION_40; +import static com.ibm.fhir.core.FHIRVersionParam.VERSION_43; import java.io.IOException; @@ -18,6 +18,7 @@ import com.ibm.fhir.config.FHIRConfigHelper; import com.ibm.fhir.config.FHIRConfiguration; import com.ibm.fhir.core.FHIRMediaType; +import com.ibm.fhir.core.FHIRVersionParam; public class FHIRVersionRequestFilter implements ContainerRequestFilter { public static final String FHIR_VERSION_PROP = "com.ibm.fhir.server.fhirVersion"; @@ -34,25 +35,26 @@ public void filter(ContainerRequestContext requestContext) throws IOException { * 3. whatever is configured in the fhirServer/core/defaultFhirVersion config property * 4. "4.0" */ - String fhirVersion = null; + FHIRVersionParam fhirVersion = null; for (MediaType mediaType : requestContext.getAcceptableMediaTypes()) { if (mediaType.getParameters() != null) { String fhirVersionParam = mediaType.getParameters().get(FHIRMediaType.FHIR_VERSION_PARAMETER); if (fhirVersionParam != null) { // "startsWith" to cover the x.y.x cases which are technically invalid, but close enough - if (fhirVersionParam.startsWith(VERSION_43)) { + if (fhirVersionParam.startsWith(VERSION_43.value())) { // one of the acceptable media types was our "actual" fhir version, so use that and stop looking fhirVersion = VERSION_43; break; - } else if (fhirVersionParam.startsWith(VERSION_40)) { + } else if (fhirVersionParam.startsWith(VERSION_40.value())) { // set the fhirVersion parameter but keep looking in case our "actual" version is also acceptable - fhirVersion = fhirVersionParam; + fhirVersion = VERSION_40; } } } } if (fhirVersion == null) { - fhirVersion = FHIRConfigHelper.getStringProperty(FHIRConfiguration.PROPERTY_DEFAULT_FHIR_VERSION, FHIRMediaType.VERSION_40); + String fhirVersionString = FHIRConfigHelper.getStringProperty(FHIRConfiguration.PROPERTY_DEFAULT_FHIR_VERSION, VERSION_40.value()); + fhirVersion = FHIRVersionParam.from(fhirVersionString); } requestContext.setProperty(FHIR_VERSION_PROP, fhirVersion); } 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 3e6dab497b1..fb86fecf111 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 @@ -6,6 +6,7 @@ package com.ibm.fhir.server.util; +import static com.ibm.fhir.config.FHIRConfiguration.PROPERTY_VALIDATION_FAIL_FAST; import static com.ibm.fhir.core.FHIRConstants.EXT_BASE; import static com.ibm.fhir.model.type.String.string; import static com.ibm.fhir.model.util.ModelSupport.getResourceType; @@ -45,9 +46,11 @@ import com.ibm.fhir.config.PropertyGroup; import com.ibm.fhir.config.PropertyGroup.PropertyEntry; import com.ibm.fhir.core.FHIRConstants; +import com.ibm.fhir.core.FHIRVersionParam; import com.ibm.fhir.core.HTTPHandlingPreference; import com.ibm.fhir.core.HTTPReturnPreference; import com.ibm.fhir.core.context.FHIRPagingContext; +import com.ibm.fhir.core.util.ResourceTypeHelper; import com.ibm.fhir.database.utils.api.LockException; import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.patch.FHIRPatch; @@ -74,6 +77,7 @@ import com.ibm.fhir.model.type.Uri; import com.ibm.fhir.model.type.Url; import com.ibm.fhir.model.type.code.BundleType; +import com.ibm.fhir.model.type.code.FHIRVersion; import com.ibm.fhir.model.type.code.HTTPVerb; import com.ibm.fhir.model.type.code.IssueSeverity; import com.ibm.fhir.model.type.code.IssueType; @@ -152,7 +156,7 @@ public class FHIRRestHelper implements FHIRResourceHelpers { private static final com.ibm.fhir.model.type.String SC_BAD_REQUEST_STRING = string(Integer.toString(SC_BAD_REQUEST)); private static final com.ibm.fhir.model.type.String SC_ACCEPTED_STRING = string(Integer.toString(SC_ACCEPTED)); private static final ZoneId UTC = ZoneId.of("UTC"); - + // Convenience constants to make call parameters more readable private static final boolean THROW_EXC_ON_NULL = true; private static final boolean INCLUDE_DELETED = true; @@ -176,21 +180,37 @@ public class FHIRRestHelper implements FHIRResourceHelpers { .optionalEnd().toFormatter(); private FHIRPersistence persistence = null; + private FHIRVersionParam fhirVersion = null; // Used for correlating requests within a bundle. private String bundleRequestCorrelationId = null; - private final FHIRValidator validator = FHIRValidator.validator(FHIRConfigHelper.getBooleanProperty(FHIRConfiguration.PROPERTY_VALIDATION_FAIL_FAST, Boolean.FALSE)); + private final FHIRValidator validator = FHIRValidator.validator(FHIRConfigHelper.getBooleanProperty(PROPERTY_VALIDATION_FAIL_FAST, Boolean.FALSE)); + /** + * Construct an instance with the passed FHIRPersistence and a FHIRVersion of 4.3.0 + * @see #FHIRRestHelper(FHIRPersistence, FHIRVersion) + */ public FHIRRestHelper(FHIRPersistence persistence) { + this(persistence, FHIRVersionParam.VERSION_43); + } + + /** + * @param persistence a FHIRPersistence instance to use for the interactions + * @param fhirVersion the fhirVersion to use for the interactions + * @implNote fhirVersion is used to validate that the interactions are only + * performed against resource types compatible with the target version + */ + public FHIRRestHelper(FHIRPersistence persistence, FHIRVersionParam fhirVersion) { this.persistence = persistence; + this.fhirVersion = fhirVersion; } @Override public FHIRRestOperationResponse doCreate(String type, Resource resource, String ifNoneExist, - boolean doValidation) throws Exception { + boolean doValidation) throws Exception { - // Validate that interaction is allowed for given resource type + // Validate that the interaction is allowed for the given resource type validateInteraction(Interaction.CREATE, type); // Validate the input and, if valid, start collecting supplemental warnings @@ -237,7 +257,7 @@ public FHIRRestOperationResponse doCreate(String type, Resource resource, String @Override public FHIRRestOperationResponse doCreateMeta(FHIRPersistenceEvent event, List warnings, String type, Resource resource, - String ifNoneExist) throws Exception { + String ifNoneExist) throws Exception { log.entering(this.getClass().getName(), "doCreateMeta"); // Save the current request context. @@ -336,7 +356,8 @@ public FHIRRestOperationResponse doCreateMeta(FHIRPersistenceEvent event, List warnings, Resource resource, PayloadPersistenceResponse offloadResponse) throws Exception { + public FHIRRestOperationResponse doCreatePersist(FHIRPersistenceEvent event, List warnings, Resource resource, + PayloadPersistenceResponse offloadResponse) throws Exception { log.entering(this.getClass().getName(), "doCreatePersist"); FHIRRestOperationResponse ior = new FHIRRestOperationResponse(); @@ -353,7 +374,7 @@ public FHIRRestOperationResponse doCreatePersist(FHIRPersistenceEvent event, Lis checkIdAndMeta(resource); // create the resource and return the location header. - final FHIRPersistenceContext persistenceContext = + final FHIRPersistenceContext persistenceContext = FHIRPersistenceContextImpl.builder(event) .withOffloadResponse(offloadResponse) .build(); @@ -757,7 +778,7 @@ public FHIRRestOperationResponse doPatchOrUpdatePersist(FHIRPersistenceEvent eve .withIfNoneMatch(ifNoneMatch) .withOffloadResponse(offloadResponse) .build(); - + boolean createOnUpdate = (prevResource == null); final SingleResourceResult result; if (createOnUpdate) { @@ -898,14 +919,10 @@ public FHIRRestOperationResponse doDelete(String type, String id, String searchQ List warnings = new ArrayList<>(); try { - String resourceTypeName = type; if (!ModelSupport.isResourceType(type)) { throw buildUnsupportedResourceTypeException(type); } - Class resourceType = - getResourceType(resourceTypeName); - // Next, if a conditional delete was invoked then use the search criteria to find the // resource to be deleted. Otherwise, we'll use the id value to identify the resource // to be deleted. @@ -989,7 +1006,7 @@ public FHIRRestOperationResponse doDelete(String type, String id, String searchQ new FHIRPersistenceEvent(null, buildPersistenceEventProperties(type, id, null, null)); event.setFhirResource(resourceToDelete); getInterceptorMgr().fireBeforeDeleteEvent(event); - + // For soft-delete we store a new version of the resource with the deleted // flag set. Update the resource meta so that it has the correct version id final String resourcePayloadKey = UUID.randomUUID().toString(); @@ -1116,7 +1133,7 @@ private SingleResourceResult doRead(String type, String id, FHIRSearchContext searchContext = null; if (queryParameters != null) { searchContext = SearchUtil.parseReadQueryParameters(resourceType, queryParameters, Interaction.READ.value(), - HTTPHandlingPreference.LENIENT.equals(requestContext.getHandlingPreference())); + HTTPHandlingPreference.LENIENT.equals(requestContext.getHandlingPreference()), fhirVersion); } // First, invoke the 'beforeRead' interceptor methods. @@ -1183,7 +1200,7 @@ public Resource doVRead(String type, String id, String versionId, MultivaluedMap FHIRSearchContext searchContext = null; if (queryParameters != null) { searchContext = SearchUtil.parseReadQueryParameters(resourceType, queryParameters, Interaction.VREAD.value(), - HTTPHandlingPreference.LENIENT.equals(requestContext.getHandlingPreference())); + HTTPHandlingPreference.LENIENT.equals(requestContext.getHandlingPreference()), fhirVersion); } // First, invoke the 'beforeVread' interceptor methods. @@ -1342,7 +1359,7 @@ public Bundle doSearch(String type, String compartment, String compartmentId, log.info("Not including resources"); } FHIRSearchContext searchContext = SearchUtil.parseCompartmentQueryParameters(compartment, compartmentId, resourceType, queryParameters, - isLenientHandling, includeResources); + isLenientHandling, includeResources, fhirVersion); // First, invoke the 'beforeSearch' interceptor methods. FHIRPersistenceEvent event = @@ -2145,10 +2162,10 @@ Bundle createSearchResponseBundle(List> resou issues.add(FHIRUtil.buildOperationOutcomeIssue(IssueSeverity.WARNING, IssueType.NOT_SUPPORTED, msg)); } else { // Off-spec - simply provide the url without the resource body. But in order - // to satisfy "Rule: must be a resource unless there's a request or response" + // to satisfy "Rule: must be a resource unless there's a request or response" // we also add a minimal response element final Uri uri = Uri.of(getRequestBaseUri(type) + "/" + resourceResult.getResourceTypeName() + "/" + resourceResult.getLogicalId()); - com.ibm.fhir.model.resource.Bundle.Entry.Response response = + com.ibm.fhir.model.resource.Bundle.Entry.Response response = com.ibm.fhir.model.resource.Bundle.Entry.Response.builder() .status("200") .build(); @@ -2978,10 +2995,18 @@ public void validateInteraction(Interaction interaction, String resourceType) th // Perform validation of specified interaction against specified resourceType if (interactions != null && !interactions.contains(interaction.value())) { throw buildRestException("The requested interaction of type '" + interaction.value() + "' is not allowed for resource type '" + resourceType + "'", - IssueType.BUSINESS_RULE, IssueSeverity.ERROR); + IssueType.BUSINESS_RULE, IssueSeverity.ERROR); } else if (!resourceValid) { throw buildRestException("The requested resource type '" + resourceType + "' is not found", - IssueType.NOT_FOUND, IssueSeverity.ERROR); + IssueType.NOT_FOUND, IssueSeverity.ERROR); + } + + if (fhirVersion == FHIRVersionParam.VERSION_40) { + // ensure that the version of this resource type in 4.0.1 is compatible with the fhirVersion of the server + if (ResourceTypeHelper.getNewOrBreakingResourceTypeNames().contains(resourceType)) { + throw buildRestException("The requested resource type '" + resourceType + "' is not supported for fhirVersion 4.0", + IssueType.NOT_SUPPORTED, IssueSeverity.ERROR); + } } } @@ -2998,14 +3023,14 @@ public Bundle doHistory(MultivaluedMap queryParameters, String r // extract the query parameters FHIRRequestContext requestContext = FHIRRequestContext.get(); - + if (requestContext.isReturnPreferenceDefault()) { // For _history, override the default preference to be REPRESENTATION so resources // are returned in the response bundle per the R4 spec. requestContext.setReturnPreference(HTTPReturnPreference.REPRESENTATION); } - FHIRSystemHistoryContext historyContext = - FHIRPersistenceUtil.parseSystemHistoryParameters(queryParameters, HTTPHandlingPreference.LENIENT.equals(requestContext.getHandlingPreference())); + FHIRSystemHistoryContext historyContext = FHIRPersistenceUtil.parseSystemHistoryParameters(queryParameters, + HTTPHandlingPreference.LENIENT.equals(requestContext.getHandlingPreference()), fhirVersion); // If HTTPReturnPreference is REPRESENTATION, we fetch the resources and include them // in the response bundle. To make it simple, we make the records and resources the same @@ -3025,26 +3050,26 @@ public Bundle doHistory(MultivaluedMap queryParameters, String r } else if (count > MAX_HISTORY_ENTRIES) { count = MAX_HISTORY_ENTRIES; } - + if (resourceType != null) { // Use the resource type on the path, ignoring any _type parameter - records = persistence.changes(count, since, before, historyContext.getChangeIdMarker(), Collections.singletonList(resourceType), historyContext.isExcludeTransactionTimeoutWindow(), - historyContext.getHistorySortOrder()); + records = persistence.changes(count, since, before, historyContext.getChangeIdMarker(), Collections.singletonList(resourceType), + historyContext.isExcludeTransactionTimeoutWindow(), historyContext.getHistorySortOrder()); } else if (historyContext.getResourceTypes().size() > 0) { // New API allows us to filter using multiple resource type names, but first we // have to check the interaction is allowed for each one for (String rt: historyContext.getResourceTypes()) { validateInteraction(Interaction.HISTORY, rt); } - records = persistence.changes(count, since, before, historyContext.getChangeIdMarker(), historyContext.getResourceTypes(), + records = persistence.changes(count, since, before, historyContext.getChangeIdMarker(), historyContext.getResourceTypes(), historyContext.isExcludeTransactionTimeoutWindow(), historyContext.getHistorySortOrder()); } else { // no resource type filter final List NULL_RESOURCE_TYPE_NAMES = null; - records = persistence.changes(count, since, before, historyContext.getChangeIdMarker(), NULL_RESOURCE_TYPE_NAMES, historyContext.isExcludeTransactionTimeoutWindow(), - historyContext.getHistorySortOrder()); + records = persistence.changes(count, since, before, historyContext.getChangeIdMarker(), NULL_RESOURCE_TYPE_NAMES, + historyContext.isExcludeTransactionTimeoutWindow(), historyContext.getHistorySortOrder()); } - + if (historyContext.getReturnPreference() == HTTPReturnPreference.REPRESENTATION || historyContext.getReturnPreference() == HTTPReturnPreference.OPERATION_OUTCOME) { // vread the resources so that we can include them in the response bundle @@ -3052,15 +3077,14 @@ public Bundle doHistory(MultivaluedMap queryParameters, String r log.fine("Fetching resources for history response bundle, count=" + records.size()); } resources = persistence.readResourcesForRecords(records); - + if (resources.size() != records.size()) { // very unlikely...unless we overlap with a patient erase operation throw new FHIRPersistenceException("Could not read all required resource records"); } } } catch (FHIRPersistenceDataAccessException x) { - log.log(Level.SEVERE, "Error reading history; params = {" + historyContext + "}", - x); + log.log(Level.SEVERE, "Error reading history; params = {" + historyContext + "}", x); throw x; } finally { txn.end(); @@ -3153,12 +3177,12 @@ public Bundle doHistory(MultivaluedMap queryParameters, String r } nextRequest.append("?"); nextRequest.append("_count=").append(count); - + if (resourceType == null && historyContext.getResourceTypes().size() > 0) { nextRequest.append("&_type="); nextRequest.append(String.join(",", historyContext.getResourceTypes())); } - + if (historyContext.isExcludeTransactionTimeoutWindow()) { nextRequest.append("&_excludeTransactionTimeoutWindow=true"); } @@ -3171,7 +3195,7 @@ public Bundle doHistory(MultivaluedMap queryParameters, String r // make sure we keep including the before filter specified in the original request nextRequest.append("&_before=").append(historyContext.getBefore().getValue().format(DateTime.PARSER_FORMATTER)); } - + if (changeIdMarker != null) { // good way to exclude this resource on the next call nextRequest.append("&_changeIdMarker=").append(changeIdMarker); @@ -3224,7 +3248,7 @@ public Bundle doHistory(MultivaluedMap queryParameters, String r if (historyContext.getChangeIdMarker() != null) { selfRequest.append("&_changeIdMarker=").append(historyContext.getChangeIdMarker()); } - + if (historyContext.getSince() != null && historyContext.getSince().getValue() != null) { selfRequest.append("&_since=").append(historyContext.getSince().getValue().format(DateTime.PARSER_FORMATTER)); } diff --git a/fhir-server/src/test/java/com/ibm/fhir/server/test/BundleValidationTest.java b/fhir-server/src/test/java/com/ibm/fhir/server/test/BundleValidationTest.java index e0143e39ea5..a5ebbddb2b4 100644 --- a/fhir-server/src/test/java/com/ibm/fhir/server/test/BundleValidationTest.java +++ b/fhir-server/src/test/java/com/ibm/fhir/server/test/BundleValidationTest.java @@ -664,7 +664,7 @@ public void testValidBundle() throws Exception { .build()) .build()) .build(); - + // Process request FHIRRequestContext.get().setOriginalRequestUri("test"); FHIRRequestContext.get().setReturnPreference(HTTPReturnPreference.OPERATION_OUTCOME); diff --git a/fhir-server/src/test/java/com/ibm/fhir/server/test/CapabilitiesTest.java b/fhir-server/src/test/java/com/ibm/fhir/server/test/CapabilitiesTest.java index 6f6cd7474bb..781d430eabc 100644 --- a/fhir-server/src/test/java/com/ibm/fhir/server/test/CapabilitiesTest.java +++ b/fhir-server/src/test/java/com/ibm/fhir/server/test/CapabilitiesTest.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2020, 2021 + * (C) Copyright IBM Corp. 2020, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -20,6 +20,7 @@ import com.ibm.fhir.config.FHIRConfiguration; import com.ibm.fhir.config.FHIRRequestContext; +import com.ibm.fhir.core.FHIRVersionParam; import com.ibm.fhir.exception.FHIRException; import com.ibm.fhir.model.resource.CapabilityStatement; import com.ibm.fhir.model.resource.CapabilityStatement.Rest.Resource.Interaction; @@ -42,9 +43,9 @@ void tearDown() throws FHIRException { void testBuildCapabilityStatement_resources_omitted() throws Exception { FHIRRequestContext.get().setTenantId("omitted"); FHIRRequestContext.get().setOriginalRequestUri("http://example.com/metadata"); - CapabilitiesChild c = new CapabilitiesChild(); + CapabilitiesChild c = new CapabilitiesChild(FHIRVersionParam.VERSION_40); - Response capabilities = c.capabilities("full", null); + Response capabilities = c.capabilities("full"); CapabilityStatement capabilityStatement = capabilities.readEntity(CapabilityStatement.class); assertEquals(capabilityStatement.getRest().size(), 1, "Number of REST Elements"); @@ -57,9 +58,9 @@ void testBuildCapabilityStatement_resources_omitted() throws Exception { void testBuildCapabilityStatement_resources_empty_r4() throws Exception { FHIRRequestContext.get().setTenantId("empty"); FHIRRequestContext.get().setOriginalRequestUri("http://example.com/metadata"); - CapabilitiesChild c = new CapabilitiesChild(); + CapabilitiesChild c = new CapabilitiesChild(FHIRVersionParam.VERSION_40); - Response capabilities = c.capabilities("full", "application/fhir+json;fhirVersion=4.0"); + Response capabilities = c.capabilities("full"); CapabilityStatement capabilityStatement = capabilities.readEntity(CapabilityStatement.class); assertEquals(capabilityStatement.getRest().size(), 1, "Number of REST Elements"); @@ -72,9 +73,9 @@ void testBuildCapabilityStatement_resources_empty_r4() throws Exception { void testBuildCapabilityStatement_resources_empty_r4b() throws Exception { FHIRRequestContext.get().setTenantId("empty"); FHIRRequestContext.get().setOriginalRequestUri("http://example.com/metadata"); - CapabilitiesChild c = new CapabilitiesChild(); + CapabilitiesChild c = new CapabilitiesChild(FHIRVersionParam.VERSION_43); - Response capabilities = c.capabilities("full", "application/fhir+json;fhirVersion=4.3"); + Response capabilities = c.capabilities("full"); CapabilityStatement capabilityStatement = capabilities.readEntity(CapabilityStatement.class); assertEquals(capabilityStatement.getRest().size(), 1, "Number of REST Elements"); @@ -87,9 +88,9 @@ void testBuildCapabilityStatement_resources_empty_r4b() throws Exception { void testBuildCapabilityStatement_resources_filtered() throws Exception { FHIRRequestContext.get().setTenantId("smart-enabled"); FHIRRequestContext.get().setOriginalRequestUri("http://example.com/metadata"); - CapabilitiesChild c = new CapabilitiesChild(); + CapabilitiesChild c = new CapabilitiesChild(FHIRVersionParam.VERSION_43); - Response capabilities = c.capabilities("full", ""); + Response capabilities = c.capabilities("full"); CapabilityStatement capabilityStatement = capabilities.readEntity(CapabilityStatement.class); assertEquals(capabilityStatement.getRest().size(), 1, "Number of REST Elements"); @@ -129,15 +130,14 @@ private void assertResourceDefinition(CapabilityStatement.Rest restDefinition, R * that are normally injected by JAX-RS and so this is the only way to set them. */ private static class CapabilitiesChild extends Capabilities { - public CapabilitiesChild() throws Exception { - super(); + /** + * @implNote Under "normal" operation, the FHIRVersionParam is set via the + * FHIRVersionRequestFilter. To simulate that, use a different + * CapabilitesChild for each request with a new fhirVersion value + */ + public CapabilitiesChild(FHIRVersionParam fhirVersion) throws Exception { this.context = new MockServletContext(); - } - - @Override - public Response capabilities(String mode, String acceptHeaderValue) { - httpServletRequest = new MockHttpServletRequest(); - return super.capabilities(mode, acceptHeaderValue); + this.httpServletRequest = new MockHttpServletRequest(fhirVersion); } } } diff --git a/fhir-server/src/test/java/com/ibm/fhir/server/test/InteractionValidationConfigTest.java b/fhir-server/src/test/java/com/ibm/fhir/server/test/InteractionValidationConfigTest.java index 2f215a5eec7..45b62d7146a 100644 --- a/fhir-server/src/test/java/com/ibm/fhir/server/test/InteractionValidationConfigTest.java +++ b/fhir-server/src/test/java/com/ibm/fhir/server/test/InteractionValidationConfigTest.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2021 + * (C) Copyright IBM Corp. 2021, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -20,6 +20,7 @@ import com.ibm.fhir.config.FHIRConfiguration; import com.ibm.fhir.config.FHIRRequestContext; +import com.ibm.fhir.core.FHIRVersionParam; import com.ibm.fhir.core.HTTPReturnPreference; import com.ibm.fhir.exception.FHIRException; import com.ibm.fhir.exception.FHIROperationException; @@ -71,7 +72,7 @@ void setup() throws FHIRException { FHIRConfiguration.setConfigHome("src/test/resources"); FHIRRegistry.getInstance().addProvider(new MockRegistryResourceProvider()); persistence = new MockPersistenceImpl(); - helper = new FHIRRestHelper(persistence); + helper = new FHIRRestHelper(persistence, FHIRVersionParam.VERSION_43); } @AfterClass diff --git a/fhir-server/src/test/java/com/ibm/fhir/server/test/MockHttpServletRequest.java b/fhir-server/src/test/java/com/ibm/fhir/server/test/MockHttpServletRequest.java index 4931ff44c82..5c5aba8610e 100644 --- a/fhir-server/src/test/java/com/ibm/fhir/server/test/MockHttpServletRequest.java +++ b/fhir-server/src/test/java/com/ibm/fhir/server/test/MockHttpServletRequest.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2020 + * (C) Copyright IBM Corp. 2020, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -29,17 +29,30 @@ import javax.servlet.http.HttpUpgradeHandler; import javax.servlet.http.Part; +import com.ibm.fhir.core.FHIRVersionParam; +import com.ibm.fhir.server.resources.filters.FHIRVersionRequestFilter; + public class MockHttpServletRequest implements HttpServletRequest { + FHIRVersionParam fhirVersion; + + public MockHttpServletRequest(FHIRVersionParam fhirVersion) { + this.fhirVersion = fhirVersion; + } @Override public StringBuffer getRequestURL() { return new StringBuffer("http://example.com"); } - // All below methods are auto-generated stubs @Override - public Object getAttribute(String name) { return null; } + public Object getAttribute(String name) { + if (FHIRVersionRequestFilter.FHIR_VERSION_PROP.equals(name)) { + return fhirVersion; + } + return null; + } + // All below methods are auto-generated stubs @Override public Enumeration getAttributeNames() { 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 6faf2bffc44..54a264c5a36 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 @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2020, 2021 + * (C) Copyright IBM Corp. 2020, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -18,6 +18,7 @@ import com.ibm.fhir.config.FHIRConfiguration; import com.ibm.fhir.config.FHIRRequestContext; +import com.ibm.fhir.core.FHIRVersionParam; import com.ibm.fhir.core.HTTPReturnPreference; import com.ibm.fhir.exception.FHIRException; import com.ibm.fhir.exception.FHIROperationException; @@ -86,7 +87,7 @@ void setup() throws FHIRException { FHIRRequestContext.get().setTenantId("profileValidationConfigTest"); FHIRRegistry.getInstance().addProvider(new MockRegistryResourceProvider()); persistence = new MockPersistenceImpl(); - helper = new FHIRRestHelper(persistence); + helper = new FHIRRestHelper(persistence, FHIRVersionParam.VERSION_43); } @AfterClass @@ -1215,7 +1216,7 @@ public void testBundleWithNonAllowedProfileSpecified() throws Exception { assertEquals(issues.get(0).getDetails().getText().getValue(), "One or more errors were encountered while validating a 'transaction' request bundle."); assertEquals(issues.get(0).getSeverity(), IssueSeverity.FATAL); assertEquals(issues.get(0).getCode(), IssueType.INVALID); - assertTrue(issues.get(1).getDetails().getText().getValue().startsWith( + assertTrue(issues.get(1).getDetails().getText().getValue().startsWith( "A profile was specified which is not allowed. Resources of type 'CarePlan' are not allowed to declare conformance to any of the following profiles: [")); assertEquals(issues.get(1).getSeverity(), IssueSeverity.ERROR); assertEquals(issues.get(1).getCode(), IssueType.BUSINESS_RULE); diff --git a/fhir-server/src/test/java/com/ibm/fhir/server/test/WellKnownTest.java b/fhir-server/src/test/java/com/ibm/fhir/server/test/WellKnownTest.java index 551cb009d1e..7fde8f9d4f4 100644 --- a/fhir-server/src/test/java/com/ibm/fhir/server/test/WellKnownTest.java +++ b/fhir-server/src/test/java/com/ibm/fhir/server/test/WellKnownTest.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2020, 2021 + * (C) Copyright IBM Corp. 2020, 2022 * * SPDX-License-Identifier: Apache-2.0 */ @@ -9,7 +9,6 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; -import jakarta.json.JsonObject; import javax.ws.rs.core.Response; import org.testng.annotations.AfterClass; @@ -18,9 +17,12 @@ import com.ibm.fhir.config.FHIRConfiguration; import com.ibm.fhir.config.FHIRRequestContext; +import com.ibm.fhir.core.FHIRVersionParam; import com.ibm.fhir.exception.FHIRException; import com.ibm.fhir.server.resources.WellKnown; +import jakarta.json.JsonObject; + public class WellKnownTest { @BeforeClass @@ -89,7 +91,7 @@ public WellKnownChild() throws Exception { @Override public Response smartConfig() throws ClassNotFoundException { - httpServletRequest = new MockHttpServletRequest(); + httpServletRequest = new MockHttpServletRequest(FHIRVersionParam.VERSION_43); return super.smartConfig(); } }