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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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