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(); } }