From 58d6eb92658d1e820f76cbdc99adde1721ead550 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 | 36 +- .../fhir/config/ResourcesConfigAdapter.java | 24 +- .../test/ResourcesConfigAdapterTest.java | 22 +- .../java/com/ibm/fhir/core/FHIRMediaType.java | 56 ++- .../com/ibm/fhir/core/FHIRVersionParam.java | 52 +++ .../com/ibm/fhir/core/ResourceTypeName.java | 427 +++++++++++------- .../fhir/core/util/ResourceTypeHelper.java | 122 +++++ .../util/test/ResourceTypeHelperTest.java | 57 +++ .../fhir/model/test/ResourceTypeNameTest.java | 27 -- .../persistence/util/FHIRPersistenceUtil.java | 96 +--- .../util/FHIRPersistenceUtilTest.java | 82 +++- .../ibm/fhir/search/util/SearchHelper.java | 93 ++-- .../test/CompartmentParseQueryParmsTest.java | 3 +- .../server/test/CapabilitiesVersionTest.java | 42 +- .../com/ibm/fhir/server/resources/Batch.java | 2 +- .../fhir/server/resources/Capabilities.java | 19 +- .../com/ibm/fhir/server/resources/Create.java | 2 +- .../com/ibm/fhir/server/resources/Delete.java | 4 +- .../fhir/server/resources/FHIRResource.java | 15 +- .../ibm/fhir/server/resources/History.java | 10 +- .../ibm/fhir/server/resources/Operation.java | 18 +- .../com/ibm/fhir/server/resources/Patch.java | 8 +- .../com/ibm/fhir/server/resources/Read.java | 2 +- .../com/ibm/fhir/server/resources/Search.java | 6 +- .../com/ibm/fhir/server/resources/Update.java | 6 +- .../com/ibm/fhir/server/resources/VRead.java | 2 +- .../filters/FHIRVersionRequestFilter.java | 16 +- .../ibm/fhir/server/util/FHIRRestHelper.java | 63 ++- .../fhir/server/test/CapabilitiesTest.java | 36 +- .../test/InteractionValidationConfigTest.java | 2 + .../server/test/MockHttpServletRequest.java | 19 +- .../test/ProfileValidationConfigTest.java | 5 +- .../ibm/fhir/server/test/WellKnownTest.java | 8 +- 33 files changed, 885 insertions(+), 497 deletions(-) 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/util/ResourceTypeHelper.java create mode 100644 fhir-core/src/test/java/com/ibm/fhir/core/util/test/ResourceTypeHelperTest.java delete mode 100644 fhir-model/src/test/java/com/ibm/fhir/model/test/ResourceTypeNameTest.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 eb79e4006ff..df4b6a70907 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 @@ -12,6 +12,7 @@ import java.util.logging.Logger; import com.ibm.fhir.config.PropertyGroup.PropertyEntry; +import com.ibm.fhir.core.FHIRVersionParam; import jakarta.json.JsonValue; @@ -22,7 +23,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 +85,7 @@ private static JsonValue getPropertyFromTenantOrDefault(String propertyName) { try { if (propertyName.startsWith(FHIRConfiguration.PROPERTY_DATASOURCES) || propertyName.startsWith(FHIRConfiguration.PROPERTY_PERSISTENCE_PAYLOAD)) { - // Issue #639 and #3416. Prevent datasource/payload lookups from falling back to + // Issue #639 and #3416. Prevent datasource/payload lookups from falling back to // the default configuration which breaks tenant isolation. result = null; } else { @@ -160,24 +161,31 @@ private static T getTypedProperty(Class expectedDataType, String property } return (result != null ? result : defaultValue); - } - + } + /** - * Get the set of supported resource types for tenantId in the FHIRRequestContext - * @return an immutable set of resource type names that isn't null + * Get the set of supported resource types for the latest supported FHIRVersion + * and the tenantId in the FHIRRequestContext + * @return an immutable non-null set of resource type names that isn't null * @throws IllegalStateException if there is an unexpected issue while processing the config */ public static Set getSupportedResourceTypes() { + return getSupportedResourceTypes(FHIRVersionParam.VERSION_43); + } + + /** + * Get the set of supported resource types for the passed fhirVersion + * and the tenantId in the FHIRRequestContext + * @param fhirVersion + * @return an immutable non-null set of resource type names for the passed fhirVersion + * @throws IllegalStateException if there is an unexpected issue while processing the config + */ + public static Set getSupportedResourceTypes(FHIRVersionParam fhirVersion) { PropertyGroup rsrcsGroup = FHIRConfigHelper.getPropertyGroup(FHIRConfiguration.PROPERTY_RESOURCES); - try { - ResourcesConfigAdapter configAdapter = new ResourcesConfigAdapter(rsrcsGroup); - return configAdapter.getSupportedResourceTypes(); - } catch (Exception e) { - log.throwing(FHIRConfigHelper.class.getName(), "getSupportedResourceTypes", e); - throw new IllegalStateException(e); - } + ResourcesConfigAdapter configAdapter = new ResourcesConfigAdapter(rsrcsGroup, fhirVersion); + return configAdapter.getSupportedResourceTypes(); } - + /** * Retrieves the search property restrictions. * 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 index e6eb6611baa..03d785499c2 100644 --- a/fhir-config/src/main/java/com/ibm/fhir/config/ResourcesConfigAdapter.java +++ b/fhir-config/src/main/java/com/ibm/fhir/config/ResourcesConfigAdapter.java @@ -20,7 +20,10 @@ import java.util.stream.Collectors; import com.ibm.fhir.config.PropertyGroup.PropertyEntry; +import com.ibm.fhir.core.FHIRVersionParam; import com.ibm.fhir.core.ResourceTypeName; +import com.ibm.fhir.core.util.ResourceTypeHelper; + /** * An abstraction for the ibm-fhir-server fhirServer/resources property group @@ -38,8 +41,8 @@ public class ResourcesConfigAdapter { private boolean isWholeSystemSearchSupported = true; private boolean isWholeSystemHistorySupported = true; - public ResourcesConfigAdapter(PropertyGroup resourcesConfig) { - supportedTypes = computeSupportedResourceTypes(resourcesConfig); + public ResourcesConfigAdapter(PropertyGroup resourcesConfig, FHIRVersionParam fhirVersion) { + supportedTypes = computeSupportedResourceTypes(resourcesConfig, fhirVersion); if (resourcesConfig == null) { for (Interaction interaction : Interaction.values()) { @@ -91,7 +94,7 @@ public boolean isHistoryRestricted() { } /** - * @return an immutable, non-null set of concrete supported resource types + * @return an immutable, non-null set of supported resource types for the given fhirVersion * @throws Exception */ public Set getSupportedResourceTypes() { @@ -100,6 +103,7 @@ public Set getSupportedResourceTypes() { /** * @return an immutable, non-null set of concrete resource types that are configured for the given interaction + * and fhirVersion */ public Set getSupportedResourceTypes(Interaction interaction) { Set result = typesByInteraction.get(interaction); @@ -110,14 +114,17 @@ public Set getSupportedResourceTypes(Interaction interaction) { } /** - * Construct the list of concrete supported resource types from the passed configuration + * Construct the list of supported resource types from the passed configuration and fhirVersion * * @param resourcesConfig + * @param fhirVersion * @return */ - private Set computeSupportedResourceTypes(PropertyGroup resourcesConfig) { + private Set computeSupportedResourceTypes(PropertyGroup resourcesConfig, FHIRVersionParam fhirVersion) { + Set applicableTypes = ResourceTypeHelper.getResourceTypesFor(fhirVersion); + if (resourcesConfig == null || resourcesConfig.getBooleanProperty("open", true)) { - return ALL_CONCRETE_TYPES; + return applicableTypes; } Set result = new LinkedHashSet(); @@ -132,10 +139,11 @@ private Set computeSupportedResourceTypes(PropertyGroup resourcesConfig) continue; } - if (ALL_CONCRETE_TYPES.contains(name)) { + if (applicableTypes.contains(name)) { result.add(name); } else if (log.isLoggable(Level.FINE)) { - log.fine("Configured resource type '" + name + "' is not valid."); + log.fine("Configured resource type '" + name + "' is not valid " + + "or not applicable for fhirVersion " + fhirVersion.value()); } } 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 index b334ac66f13..fa79fe459ed 100644 --- 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 @@ -15,6 +15,7 @@ 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; @@ -24,16 +25,31 @@ public class ResourcesConfigAdapterTest { public void testGetSupportedResourceTypes() throws Exception { JsonObject json = Json.createObjectBuilder().build(); PropertyGroup pg = new PropertyGroup(json); - ResourcesConfigAdapter resourcesConfigAdapter = new ResourcesConfigAdapter(pg); + ResourcesConfigAdapter resourcesConfigAdapter = new ResourcesConfigAdapter(pg, FHIRVersionParam.VERSION_40); Set supportedResourceTypes = resourcesConfigAdapter.getSupportedResourceTypes(); - assertEquals(supportedResourceTypes.size(), 146); + assertEquals(supportedResourceTypes.size(), 125); System.out.println(supportedResourceTypes); for (Interaction interaction : Interaction.values()) { supportedResourceTypes = resourcesConfigAdapter.getSupportedResourceTypes(interaction); - assertEquals(supportedResourceTypes.size(), 146); + 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 index 19837ccf123..986925a0089 100644 --- a/fhir-core/src/main/java/com/ibm/fhir/core/ResourceTypeName.java +++ b/fhir-core/src/main/java/com/ibm/fhir/core/ResourceTypeName.java @@ -9,6 +9,177 @@ * 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 * @@ -25,6 +196,14 @@ public enum ResourceTypeName { */ 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 * @@ -73,14 +252,6 @@ public enum ResourceTypeName { */ BASIC("Basic"), - /** - * 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"), - /** * BiologicallyDerivedProduct * @@ -97,13 +268,6 @@ public enum ResourceTypeName { */ BODY_STRUCTURE("BodyStructure"), - /** - * Bundle - * - *

A container for a collection of resources. - */ - BUNDLE("Bundle"), - /** * CapabilityStatement * @@ -155,6 +319,15 @@ public enum ResourceTypeName { */ 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 * @@ -181,6 +354,14 @@ public enum ResourceTypeName { */ 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 * @@ -351,21 +532,6 @@ public enum ResourceTypeName { */ DOCUMENT_REFERENCE("DocumentReference"), - /** - * DomainResource - * - *

A resource that includes narrative, extensions, and contained resources. - */ - DOMAIN_RESOURCE("DomainResource"), - - /** - * 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"), - /** * Encounter * @@ -414,16 +580,24 @@ public enum ResourceTypeName { /** * Evidence * - *

The Evidence 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. + *

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 a "PICO" element that knowledge (evidence, assertion, recommendation) is - * about. + *

The EvidenceVariable resource describes an element that knowledge (Evidence) is about. */ EVIDENCE_VARIABLE("EvidenceVariable"), @@ -539,6 +713,13 @@ public enum ResourceTypeName { */ IMPLEMENTATION_GUIDE("ImplementationGuide"), + /** + * Ingredient + * + *

An ingredient of a manufactured item or pharmaceutical product. + */ + INGREDIENT("Ingredient"), + /** * InsurancePlan * @@ -585,6 +766,14 @@ public enum ResourceTypeName { */ 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 * @@ -673,75 +862,12 @@ public enum ResourceTypeName { MEDICATION_STATEMENT("MedicationStatement"), /** - * 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 + * MedicinalProductDefinition * - *

A pharmaceutical product described in terms of its composition and dose form. + *

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

Describe the undesirable effects of the medicinal product. - */ - MEDICINAL_PRODUCT_UNDESIRABLE_EFFECT("MedicinalProductUndesirableEffect"), + MEDICINAL_PRODUCT_DEFINITION("MedicinalProductDefinition"), /** * MessageDefinition @@ -782,6 +908,13 @@ public enum ResourceTypeName { */ NUTRITION_ORDER("NutritionOrder"), + /** + * NutritionProduct + * + *

A food or fluid product that is consumed by patients. + */ + NUTRITION_PRODUCT("NutritionProduct"), + /** * Observation * @@ -829,6 +962,13 @@ public enum ResourceTypeName { */ ORGANIZATION_AFFILIATION("OrganizationAffiliation"), + /** + * PackagedProductDefinition + * + *

A medically related item or items, in a container or package. + */ + PACKAGED_PRODUCT_DEFINITION("PackagedProductDefinition"), + /** * Parameters * @@ -871,8 +1011,8 @@ public enum ResourceTypeName { * 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 artifacts such as - * clinical decision support rules, order sets and protocols. + * 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"), @@ -928,6 +1068,15 @@ public enum ResourceTypeName { */ 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 * @@ -977,13 +1126,6 @@ public enum ResourceTypeName { */ RESEARCH_SUBJECT("ResearchSubject"), - /** - * Resource - * - *

This is the base resource type for everything. - */ - RESOURCE("Resource"), - /** * RiskAssessment * @@ -991,14 +1133,6 @@ public enum ResourceTypeName { */ RISK_ASSESSMENT("RiskAssessment"), - /** - * 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"), - /** * Schedule * @@ -1067,66 +1201,33 @@ public enum ResourceTypeName { SUBSCRIPTION("Subscription"), /** - * Substance + * SubscriptionStatus * - *

A homogeneous material with a definite composition. + *

The SubscriptionStatus resource describes the state of a Subscription during notifications. */ - SUBSTANCE("Substance"), + SUBSCRIPTION_STATUS("SubscriptionStatus"), /** - * 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 + * SubscriptionTopic * - *

Todo. + *

Describes a stream of resource state changes identified by trigger criteria and annotated with labels useful to + * filter projections from this topic. */ - SUBSTANCE_POLYMER("SubstancePolymer"), + SUBSCRIPTION_TOPIC("SubscriptionTopic"), /** - * 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 + * Substance * - *

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. + *

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

The detailed description of a substance, typically at a level beyond what is used for prescribing. */ - SUBSTANCE_SPECIFICATION("SubstanceSpecification"), + SUBSTANCE_DEFINITION("SubstanceDefinition"), /** * SupplyDelivery 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-model/src/test/java/com/ibm/fhir/model/test/ResourceTypeNameTest.java b/fhir-model/src/test/java/com/ibm/fhir/model/test/ResourceTypeNameTest.java deleted file mode 100644 index 0579017dd87..00000000000 --- a/fhir-model/src/test/java/com/ibm/fhir/model/test/ResourceTypeNameTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.ibm.fhir.model.test; - -import static org.testng.Assert.assertEquals; - -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import org.testng.annotations.Test; - -import com.ibm.fhir.core.ResourceTypeName; -import com.ibm.fhir.model.type.code.ResourceType; - -public class ResourceTypeNameTest { - @Test - void testResourceTypeNameEnum() { - List typename = Arrays.stream(ResourceTypeName.values()) - .map(t -> t.value()) - .collect(Collectors.toList()); - - List types = Arrays.stream(ResourceType.Value.values()) - .map(t -> t.value()) - .collect(Collectors.toList()); - - assertEquals(typename, types); - } -} 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 a14c0098162..c8e4715cd6d 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 @@ -13,27 +13,26 @@ 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.FHIRRequestContext; 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.exception.FHIROperationException; +import com.ibm.fhir.core.util.ResourceTypeHelper; import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.resource.Resource.Builder; import com.ibm.fhir.model.type.DateTime; 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; @@ -48,10 +47,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 } @@ -99,16 +94,27 @@ 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, + ResourcesConfigAdapter resourcesConfig) throws FHIRPersistenceException { + return parseSystemHistoryParameters(queryParameters, lenient, resourcesConfig, FHIRVersionParam.VERSION_43); + } + + /** + * Parse history parameters into a FHIRHistoryContext for a given fhirVersion * * @param queryParameters * @param lenient * @param resourcesConfig + * @param fhirVersion * @return * @throws FHIRPersistenceException */ public static FHIRSystemHistoryContext parseSystemHistoryParameters(Map> queryParameters, boolean lenient, - ResourcesConfigAdapter resourcesConfig) throws FHIRPersistenceException { + ResourcesConfigAdapter resourcesConfig, FHIRVersionParam fhirVersion) throws FHIRPersistenceException { log.entering(FHIRPersistenceUtil.class.getName(), "parseSystemHistoryParameters"); FHIRSystemHistoryContextImpl context = new FHIRSystemHistoryContextImpl(); context.setLenient(lenient); @@ -139,15 +145,12 @@ 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); } } } @@ -246,53 +247,6 @@ public static FHIRSystemHistoryContext parseSystemHistoryParameters(Map interactions = null; - - // Retrieve the interaction configuration - try { - StringBuilder defaultInteractionsConfigPath = new StringBuilder(FHIRConfiguration.PROPERTY_RESOURCES).append("/Resource/") - .append(FHIRConfiguration.PROPERTY_FIELD_RESOURCES_INTERACTIONS); - StringBuilder resourceSpecificInteractionsConfigPath = new StringBuilder(FHIRConfiguration.PROPERTY_RESOURCES).append("/") - .append(resourceType).append("/").append(FHIRConfiguration.PROPERTY_FIELD_RESOURCES_INTERACTIONS); - - // Get the 'interactions' property - List resourceSpecificInteractions = FHIRConfigHelper.getStringListProperty(resourceSpecificInteractionsConfigPath.toString()); - if (resourceSpecificInteractions != null) { - interactions = resourceSpecificInteractions; - } else { - // Check the 'open' property, and if that's false, check if resource was specified - if (!FHIRConfigHelper.getBooleanProperty(FHIRConfiguration.PROPERTY_RESOURCES + "/" + FHIRConfiguration.PROPERTY_FIELD_RESOURCES_OPEN, true)) { - PropertyGroup resourceGroup = FHIRConfigHelper.getPropertyGroup(FHIRConfiguration.PROPERTY_RESOURCES + "/" + resourceType); - if (resourceGroup == null) { - resourceValid = false; - } - } - if (resourceValid) { - // Get the 'Resource' interaction property - List defaultInteractions = FHIRConfigHelper.getStringListProperty(defaultInteractionsConfigPath.toString()); - if (defaultInteractions != null) { - interactions = defaultInteractions; - } - } - } - - if (log.isLoggable(Level.FINE)) { - log.fine("Allowed interactions: " + interactions); - } - } catch (Exception e) { - String msg = "Error retrieving interactions configuration."; - throw new FHIROperationException(msg).withIssue(FHIRUtil.buildOperationOutcomeIssue(msg, IssueType.EXCEPTION)); - } - - // Perform validation of specified interaction against specified resourceType - if (interactions != null && !interactions.contains("history")) { - return false; - } - return resourceValid; - } - /** * Create a new {@link ResourceResult} instance to represent a deleted or partially * erased resource 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 9dd7fac18ae..1a1441a2609 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 @@ -27,6 +27,7 @@ import com.ibm.fhir.config.FHIRRequestContext; import com.ibm.fhir.config.PropertyGroup; import com.ibm.fhir.config.ResourcesConfigAdapter; +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; @@ -38,9 +39,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"); } @@ -128,7 +128,7 @@ public void testParseSystemHistoryParameters_implicitTypes() throws FHIRExceptio // no explicit _type param Map> params = new HashMap<>(); - FHIRSystemHistoryContext historyContext = FHIRPersistenceUtil.parseSystemHistoryParameters(params, false); + FHIRSystemHistoryContext historyContext = FHIRPersistenceUtil.parseSystemHistoryParameters(params, false, newResourcesConfigAdapter()); assertEquals(historyContext.getResourceTypes().size(), 141, "implicitly scoped to all R4B resource types"); } finally { @@ -164,6 +164,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, newResourcesConfigAdapter(), 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, newResourcesConfigAdapter(), 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, newResourcesConfigAdapter(), 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, newResourcesConfigAdapter(), 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(); @@ -213,6 +287,6 @@ public void testParseSystemHistoryParameters_emptyTypeParam() throws FHIRExcepti private ResourcesConfigAdapter newResourcesConfigAdapter() { PropertyGroup resourcesPG = FHIRConfigHelper.getPropertyGroup(FHIRConfiguration.PROPERTY_RESOURCES); - return new ResourcesConfigAdapter(resourcesPG); + return new ResourcesConfigAdapter(resourcesPG, FHIRVersionParam.VERSION_43); } } diff --git a/fhir-search/src/main/java/com/ibm/fhir/search/util/SearchHelper.java b/fhir-search/src/main/java/com/ibm/fhir/search/util/SearchHelper.java index 089fdbfe1a5..f95b6b00d23 100644 --- a/fhir-search/src/main/java/com/ibm/fhir/search/util/SearchHelper.java +++ b/fhir-search/src/main/java/com/ibm/fhir/search/util/SearchHelper.java @@ -37,7 +37,8 @@ import com.ibm.fhir.config.PropertyGroup.PropertyEntry; import com.ibm.fhir.config.ResourcesConfigAdapter; import com.ibm.fhir.core.FHIRConstants; -import com.ibm.fhir.exception.FHIROperationException; +import com.ibm.fhir.core.FHIRVersionParam; +import com.ibm.fhir.core.util.ResourceTypeHelper; import com.ibm.fhir.model.resource.CodeSystem; import com.ibm.fhir.model.resource.CodeSystem.Concept; import com.ibm.fhir.model.resource.Resource; @@ -133,10 +134,6 @@ public class SearchHelper { 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(); @@ -383,11 +380,27 @@ public Map> extractParameterValues(Resource public FHIRSearchContext parseQueryParameters(Class resourceType, Map> queryParameters) throws Exception { - return parseQueryParameters(resourceType, queryParameters, false, true); + return parseQueryParameters(resourceType, queryParameters, false, true, FHIRVersionParam.VERSION_43); } public 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 FHIRSearchContext parseQueryParameters(Class resourceType, Map> queryParameters, + boolean lenient, boolean includeResource, FHIRVersionParam fhirVersion) throws Exception { FHIRSearchContext context = FHIRSearchContextFactory.createSearchContext(); context.setLenient(lenient); @@ -417,9 +430,7 @@ public FHIRSearchContext parseQueryParameters(Class resourceType, Set resourceTypes = new LinkedHashSet<>(); if (isSystemSearch) { PropertyGroup pg = FHIRConfigHelper.getPropertyGroup(FHIRConfiguration.PROPERTY_RESOURCES); - ResourcesConfigAdapter config = new ResourcesConfigAdapter(pg); - // TODO reconcile getSupportedResourceTypes(SEARCH) with this with the new config property: - // Boolean implicitTypeScoping = FHIRConfigHelper.getBooleanProperty(FHIRConfiguration.PROPERTY_WHOLE_SYSTEM_TYPE_SCOPING, true); + ResourcesConfigAdapter config = new ResourcesConfigAdapter(pg, fhirVersion); Set searchableTypes = config.getSupportedResourceTypes(Interaction.SEARCH); if (queryParameters.containsKey(SearchConstants.RESOURCE_TYPE)) { @@ -433,6 +444,11 @@ public FHIRSearchContext parseQueryParameters(Class resourceType, } else if (!searchableTypes.contains(tmpResourceType)) { String msg = "Search interaction is not supported for _type parameter value: " + Encode.forHtml(tmpResourceType); manageException(msg, IssueType.NOT_SUPPORTED, context, false); + } else if (fhirVersion == FHIRVersionParam.VERSION_40 && + ResourceTypeHelper.getNewOrBreakingResourceTypeNames().contains(tmpResourceType)) { + String msg = "fhirVersion 4.0 interaction for _type parameter value: '" + Encode.forHtml(tmpResourceType) + + "' is not supported"; + manageException(msg, IssueType.NOT_SUPPORTED, context, false); } else { resourceTypes.add(tmpResourceType); } @@ -601,53 +617,6 @@ public FHIRSearchContext parseQueryParameters(Class resourceType, return context; } - private static boolean isSearchEnabled(String resourceType) throws FHIROperationException { - boolean resourceValid = true; - List interactions = null; - - // Retrieve the interaction configuration - try { - StringBuilder defaultInteractionsConfigPath = new StringBuilder(FHIRConfiguration.PROPERTY_RESOURCES).append("/Resource/") - .append(FHIRConfiguration.PROPERTY_FIELD_RESOURCES_INTERACTIONS); - StringBuilder resourceSpecificInteractionsConfigPath = new StringBuilder(FHIRConfiguration.PROPERTY_RESOURCES).append("/") - .append(resourceType).append("/").append(FHIRConfiguration.PROPERTY_FIELD_RESOURCES_INTERACTIONS); - - // Get the 'interactions' property - List resourceSpecificInteractions = FHIRConfigHelper.getStringListProperty(resourceSpecificInteractionsConfigPath.toString()); - if (resourceSpecificInteractions != null) { - interactions = resourceSpecificInteractions; - } else { - // Check the 'open' property, and if that's false, check if resource was specified - if (!FHIRConfigHelper.getBooleanProperty(FHIRConfiguration.PROPERTY_RESOURCES + "/" + FHIRConfiguration.PROPERTY_FIELD_RESOURCES_OPEN, true)) { - PropertyGroup resourceGroup = FHIRConfigHelper.getPropertyGroup(FHIRConfiguration.PROPERTY_RESOURCES + "/" + resourceType); - if (resourceGroup == null) { - resourceValid = false; - } - } - if (resourceValid) { - // Get the 'Resource' interaction property - List defaultInteractions = FHIRConfigHelper.getStringListProperty(defaultInteractionsConfigPath.toString()); - if (defaultInteractions != null) { - interactions = defaultInteractions; - } - } - } - - if (log.isLoggable(Level.FINE)) { - log.fine("Allowed interactions: " + interactions); - } - } catch (Exception e) { - String msg = "Error retrieving interactions configuration."; - throw new FHIROperationException(msg).withIssue(FHIRUtil.buildOperationOutcomeIssue(msg, IssueType.EXCEPTION)); - } - - // Perform validation of specified interaction against specified resourceType - if (interactions != null && !interactions.contains("search")) { - return false; - } - return resourceValid; - } - /** * Checks the query parameters (with the same parameter code) against any search restrictions specified * in the SearchParameter resource for that parameter code. @@ -1243,11 +1212,12 @@ public Map getSearchParameters(String resourceType) thr * @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 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 @@ -1261,13 +1231,13 @@ public 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 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); } /** @@ -1279,15 +1249,16 @@ public FHIRSearchContext parseCompartmentQueryParameters(String compartmentName, * 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 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) { 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 7872d548d21..b2dfdfc9572 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 @@ -23,6 +23,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; @@ -213,7 +214,7 @@ public void testCompartmentWithFakeQueryParm() throws Exception { try { searchHelper.parseCompartmentQueryParameters(compartmentName, compartmentLogicalId, - resourceType, queryParameters, false, true); + 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 f6bab502554..15025c5321a 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 @@ -85,7 +85,7 @@ public Response bundle(Resource resource, @HeaderParam(FHIRConstants.UPDATE_IF_M throw buildRestException(msg, IssueType.INVALID); } - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), 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 bf2023bdb1b..94b52581ef6 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; @@ -159,7 +159,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(); @@ -176,8 +176,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); @@ -208,7 +208,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": @@ -314,7 +314,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); @@ -571,7 +571,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), @@ -673,11 +673,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) { @@ -692,7 +693,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 e9b302b9a8c..ea6b1526732 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 @@ -72,7 +72,7 @@ public Response create(@PathParam("type") String type, Resource resource, @Heade checkInitComplete(); checkType(type); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), 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 93adc3ccdde..d0a6f1a8b3e 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 @@ -58,7 +58,7 @@ public Response delete(@PathParam("type") String type, @PathParam("id") String i checkInitComplete(); checkType(type); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), 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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), 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 8084abf1a27..31b89bf502d 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 @@ -44,7 +44,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; @@ -56,7 +56,6 @@ import com.ibm.fhir.model.type.CodeableConcept; import com.ibm.fhir.model.type.Extension; import com.ibm.fhir.model.type.Meta; -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; @@ -492,12 +491,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 705acd627b7..fd5686dd842 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 @@ -55,7 +55,7 @@ public Response history(@PathParam("type") String type, @PathParam("id") String checkInitComplete(); checkType(type); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), 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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), 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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); bundle = helper.doHistory(uriInfo.getQueryParameters(), getRequestUri(), type); status = Status.OK; return Response.status(status).entity(bundle).build(); @@ -143,4 +143,4 @@ public Response systemHistory(@PathParam("type") String type) { log.exiting(this.getClass().getName(), "systemHistory(String)"); } } -} \ No newline at end of file +} 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 4931f58367d..dde05fd83d1 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 @@ -80,7 +80,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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, null, null, null, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, null, result); @@ -123,7 +123,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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, null, null, null, resource, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, null, result); @@ -167,7 +167,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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, null, null, null, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, null, result); @@ -212,7 +212,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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, resourceTypeName, null, null, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); @@ -257,7 +257,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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, resourceTypeName, null, null, resource, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); @@ -316,7 +316,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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, null, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); @@ -362,7 +362,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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, null, resource, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); @@ -409,7 +409,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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, versionId, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); @@ -456,7 +456,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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), 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 322a8283dba..aa1cd96ea91 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 @@ -79,7 +79,7 @@ public Response patch(@PathParam("type") String type, @PathParam("id") String id FHIRPatch patch = createPatch(array); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); ior = helper.doPatch(type, id, patch, ifMatch, null, onlyIfModified); status = ior.getStatus(); @@ -137,7 +137,7 @@ public Response patch(@PathParam("type") String type, @PathParam("id") String id throw buildRestException(e.getMessage(), IssueType.INVALID); } - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); ior = helper.doPatch(type, id, patch, ifMatch, null, onlyIfModified); ResponseBuilder response = @@ -200,7 +200,7 @@ public Response conditionalPatch(@PathParam("type") String type, JsonArray array throw buildRestException(msg, IssueType.INVALID); } - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); ior = helper.doPatch(type, null, patch, ifMatch, searchQueryString, onlyIfModified); ResponseBuilder response = @@ -268,7 +268,7 @@ public Response conditionalPatch(@PathParam("type") String type, Parameters para throw buildRestException(msg, IssueType.INVALID); } - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), 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 bdd8815b241..0c598dd0e1f 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 @@ -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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), 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 219ba6f48c7..9c18535c852 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 @@ -68,7 +68,7 @@ private Response doSearch(String type) { checkType(type); queryParameters = uriInfo.getQueryParameters(); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), 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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), 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(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), 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 ecb8e863c9d..184afe2e24a 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 @@ -72,7 +72,7 @@ public Response update(@PathParam("type") String type, @PathParam("id") String i checkType(type); Integer ifNoneMatch = encodeIfNoneMatch(ifNoneMatchHdr); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); ior = helper.doUpdate(type, id, resource, ifMatch, null, onlyIfModified, ifNoneMatch); ResponseBuilder response = Response.ok() @@ -144,7 +144,7 @@ public Response conditionalUpdate(@PathParam("type") String type, Resource resou throw buildRestException(msg, IssueType.INVALID); } - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), getFhirVersion()); ior = helper.doUpdate(type, null, resource, ifMatch, searchQueryString, onlyIfModified, IF_NONE_MATCH_NULL); ResponseBuilder response = @@ -216,4 +216,4 @@ private Integer encodeIfNoneMatch(String value) throws FHIROperationException { throw buildRestException("Invalid If-None-Match value", IssueType.INVALID); } } -} \ No newline at end of file +} 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 89f825909cb..7d124d4b9fb 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 @@ -60,7 +60,7 @@ public Response vread(@PathParam("type") String type, @PathParam("id") String id MultivaluedMap queryParameters = uriInfo.getQueryParameters(); - FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper()); + FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl(), getSearchHelper(), 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 e9399b4da2f..aa7b0f37cf2 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; @@ -47,9 +48,11 @@ 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.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; @@ -76,6 +79,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; @@ -180,24 +184,42 @@ public class FHIRRestHelper implements FHIRResourceHelpers { private final FHIRPersistence persistence; private final SearchHelper searchHelper; private final ResourcesConfigAdapter resourcesConfig; + private final FHIRVersionParam fhirVersion; // 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 SearchHelper, and a FHIRVersion of 4.3.0 + * @see #FHIRRestHelper(FHIRPersistence, FHIRVersion) + */ public FHIRRestHelper(FHIRPersistence persistence, SearchHelper searchHelper) { + this(persistence, searchHelper, FHIRVersionParam.VERSION_43); + } + + /** + * @param persistence a FHIRPersistence instance to use for the interactions + * @param searchHelper a SearchHelper instance for working with search parameters + * @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, SearchHelper searchHelper, FHIRVersionParam fhirVersion) { this.persistence = persistence; this.searchHelper = searchHelper; PropertyGroup resourcesPropertyGroup = FHIRConfigHelper.getPropertyGroup(FHIRConfiguration.PROPERTY_RESOURCES); this.resourcesConfig = new ResourcesConfigAdapter(resourcesPropertyGroup); + 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 @@ -244,7 +266,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. @@ -343,7 +365,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(); @@ -910,13 +933,11 @@ 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); + Class resourceType = getResourceType(type); // 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 @@ -1135,7 +1156,7 @@ private SingleResourceResult doRead(String type, String id, FHIRSearchContext searchContext = null; if (queryParameters != null) { searchContext = searchHelper.parseReadQueryParameters(resourceType, queryParameters, Interaction.READ.value(), - HTTPHandlingPreference.LENIENT.equals(requestContext.getHandlingPreference())); + HTTPHandlingPreference.LENIENT.equals(requestContext.getHandlingPreference()), fhirVersion); } // First, invoke the 'beforeRead' interceptor methods. @@ -1200,7 +1221,7 @@ public Resource doVRead(String type, String id, String versionId, MultivaluedMap FHIRSearchContext searchContext = null; if (queryParameters != null) { searchContext = searchHelper.parseReadQueryParameters(resourceType, queryParameters, Interaction.VREAD.value(), - HTTPHandlingPreference.LENIENT.equals(requestContext.getHandlingPreference())); + HTTPHandlingPreference.LENIENT.equals(requestContext.getHandlingPreference()), fhirVersion); } // First, invoke the 'beforeVread' interceptor methods. @@ -1346,7 +1367,7 @@ public Bundle doSearch(String type, String compartment, String compartmentId, log.info("Not including resources"); } FHIRSearchContext searchContext = searchHelper.parseCompartmentQueryParameters(compartment, compartmentId, resourceType, queryParameters, - isLenientHandling, includeResources); + isLenientHandling, includeResources, fhirVersion); // First, invoke the 'beforeSearch' interceptor methods. FHIRPersistenceEvent event = @@ -2972,6 +2993,14 @@ public void validateInteraction(Interaction interaction, String resourceType) th throw buildRestException("The requested interaction of type '" + interaction.value() + "' is not allowed for resource type '" + resourceType + "'", IssueType.BUSINESS_RULE, 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); + } + } } @Override @@ -2994,7 +3023,7 @@ public Bundle doHistory(MultivaluedMap queryParameters, String r requestContext.setReturnPreference(HTTPReturnPreference.REPRESENTATION); } FHIRSystemHistoryContext historyContext = FHIRPersistenceUtil.parseSystemHistoryParameters(queryParameters, - HTTPHandlingPreference.LENIENT.equals(requestContext.getHandlingPreference()), resourcesConfig); + HTTPHandlingPreference.LENIENT.equals(requestContext.getHandlingPreference()), resourcesConfig, 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 @@ -3022,8 +3051,8 @@ public Bundle doHistory(MultivaluedMap queryParameters, String r 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 @@ -3035,8 +3064,8 @@ public Bundle doHistory(MultivaluedMap queryParameters, String r } 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 @@ -3332,4 +3361,4 @@ public PayloadPersistenceResponse storePayload(Resource resource, String logical // Delegate to the persistence layer. Result will be null if offloading is not supported return persistence.storePayload(resource, logicalId, newVersionNumber, resourcePayloadKey); } -} \ No newline at end of file +} 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 8756c352ed8..71f9998cfea 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 @@ -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; @@ -45,9 +46,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(searchHelper); + CapabilitiesChild c = new CapabilitiesChild(searchHelper, 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"); @@ -60,9 +61,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(searchHelper); + CapabilitiesChild c = new CapabilitiesChild(searchHelper, 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"); @@ -76,9 +77,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(searchHelper, 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"); @@ -91,9 +92,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(searchHelper); + CapabilitiesChild c = new CapabilitiesChild(searchHelper, 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"); @@ -137,21 +138,16 @@ 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(SearchHelper searchHelper) throws Exception { + /** + * @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(SearchHelper searchHelper, FHIRVersionParam fhirVersion) throws Exception { super(); this.context = new MockServletContext(); this.searchHelper = searchHelper; - } - - @Override - public Response capabilities(String mode, String acceptHeaderValue) { - httpServletRequest = new MockHttpServletRequest(); - return super.capabilities(mode, acceptHeaderValue); - } - - @Override - protected SearchHelper getSearchHelper() { - return searchHelper; + 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 49cbb2ff12f..c2bf14c1d30 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 @@ -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; @@ -73,6 +74,7 @@ void setup() throws FHIRException { FHIRRegistry.getInstance().addProvider(new MockRegistryResourceProvider()); persistence = new MockPersistenceImpl(); searchHelper = new SearchHelper(); + helper = new FHIRRestHelper(persistence, searchHelper, 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 8957c6c62f7..70e2803f595 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 @@ -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; @@ -87,7 +88,7 @@ void setup() throws FHIRException { FHIRRequestContext.get().setTenantId("profileValidationConfigTest"); FHIRRegistry.getInstance().addProvider(new MockRegistryResourceProvider()); persistence = new MockPersistenceImpl(); - helper = new FHIRRestHelper(persistence, new SearchHelper()); + helper = new FHIRRestHelper(persistence, new SearchHelper(), FHIRVersionParam.VERSION_43); } @AfterClass @@ -1841,4 +1842,4 @@ public void testCreateWithUnsupportedProfileDefaultVersionAndAllowUnknownFalse() } } -} \ No newline at end of file +} 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(); } }