Skip to content

Commit

Permalink
issue #3242 - scope search-system and search-history for fhirVersion
Browse files Browse the repository at this point in the history
1. introduce enums and utilities for working with resource type names
and fhirVersion values ('4.0' and '4.3')
2. use those to provide a better abstraction for working with the
fhir-server-config `fhirServer/resources` property group
(ResourcesConfigAdapter)
3. move fhirVersion MIME-type parameter processing into a new JAX-RS
RequestFilter FHIRVersionRequestFilter and update tests accordingly
4. update FHIRPersistenceUtil.parseSystemHistoryParameters to take into
account the requested fhirVersion and scope the HistoryContext
appropriately
5. update SearchUtil.parseQueryParameters to take into account the
requested fhirVersion and scope the SearchContext appropriately

Signed-off-by: Lee Surprenant <lmsurpre@us.ibm.com>
  • Loading branch information
lmsurpre committed Feb 2, 2022
1 parent 45c3904 commit 3ed0b8b
Show file tree
Hide file tree
Showing 34 changed files with 2,146 additions and 252 deletions.
48 changes: 18 additions & 30 deletions fhir-config/src/main/java/com/ibm/fhir/config/FHIRConfigHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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";
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -160,43 +162,29 @@ private static <T> T getTypedProperty(Class<T> 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<String> getSupportedResourceTypes() throws FHIRException {
List<String> 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<String> getSupportedResourceTypes(FHIRVersionParam fhirVersion) throws FHIRException {
PropertyGroup rsrcsGroup = FHIRConfigHelper.getPropertyGroup(FHIRConfiguration.PROPERTY_RESOURCES);
List<PropertyEntry> 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.
*
Expand Down
36 changes: 36 additions & 0 deletions fhir-config/src/main/java/com/ibm/fhir/config/Interaction.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<String> supportedTypes;
private final Map<Interaction, Set<String>> 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<String> 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<String> getSupportedResourceTypes() {
return supportedTypes;
}

/**
* @return an immutable, non-null set of resource types that are configured for the given interaction and fhirVersion
*/
public Set<String> 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<String> computeSupportedResourceTypes(PropertyGroup resourcesConfig, FHIRVersionParam fhirVersion) throws Exception {
Set<String> applicableTypes = ResourceTypeHelper.getResourceTypesFor(fhirVersion);

if (resourcesConfig == null || resourcesConfig.getBooleanProperty("open", true)) {
return applicableTypes;
}

Set<String> result = new LinkedHashSet<String>();
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);
}
}
Original file line number Diff line number Diff line change
@@ -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<String> 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<String> supportedResourceTypes = resourcesConfigAdapter.getSupportedResourceTypes();
assertEquals(supportedResourceTypes.size(), 141);

for (Interaction interaction : Interaction.values()) {
supportedResourceTypes = resourcesConfigAdapter.getSupportedResourceTypes(interaction);
assertEquals(supportedResourceTypes.size(), 141);
}
}
}
56 changes: 35 additions & 21 deletions fhir-core/src/main/java/com/ibm/fhir/core/FHIRMediaType.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* (C) Copyright IBM Corp. 2019, 2021
* (C) Copyright IBM Corp. 2019, 2022
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -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;
Expand All @@ -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<String> SUPPORTED_FHIR_VERSIONS =
Collections.unmodifiableSet(new LinkedHashSet<>(Arrays.asList(VERSION_40, VERSION_401, VERSION_43, VERSION_430)));

private static final Map<String,String> fhirVersion40ParameterMap = Collections.singletonMap(FHIR_VERSION_PARAMETER, VERSION_40);
private static final Map<String,String> 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<String> 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);
}
Loading

0 comments on commit 3ed0b8b

Please sign in to comment.