Skip to content

Commit

Permalink
issue #3390 - whole-system interactions use config
Browse files Browse the repository at this point in the history
* Introduce the ResourceTypeName enum in fhir-core and ResourcesConfigAdapter in fhir-config
* Use ResourcesConfigAdapter from whole-system search, whole-system history, $everything, and $export

I also started on #3319 - system-search now supports multiple instances of the `_type` parameter

Signed-off-by: Lee Surprenant <lmsurpre@us.ibm.com>
  • Loading branch information
lmsurpre committed Mar 16, 2022
1 parent 3f098bb commit 34f3653
Show file tree
Hide file tree
Showing 30 changed files with 1,725 additions and 323 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.Set;

import javax.batch.api.partition.PartitionMapper;
import javax.batch.api.partition.PartitionPlan;
Expand All @@ -34,6 +35,7 @@
import com.ibm.fhir.persistence.helper.FHIRTransactionHelper;
import com.ibm.fhir.search.compartment.CompartmentHelper;


@Dependent
public class PatientExportPartitionMapper implements PartitionMapper {

Expand All @@ -59,9 +61,9 @@ public PartitionPlan mapPartitions() throws Exception {

// By default we're in the Patient Compartment, if we have a valid context
// which has a resourceType specified, it's valid as the operation has already checked.
List<String> resourceTypes = compartmentHelper.getCompartmentResourceTypes("Patient");
Set<String> resourceTypes = compartmentHelper.getCompartmentResourceTypes("Patient");
if (ctx.getFhirResourceTypes() != null ) {
resourceTypes = Arrays.asList(ctx.getFhirResourceTypes().split("\\s*,\\s*"));
resourceTypes = Set.of(ctx.getFhirResourceTypes().split("\\s*,\\s*"));
}

// Register the context to get the right configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@

package com.ibm.fhir.config;

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.exception.FHIRException;

import jakarta.json.JsonValue;

Expand Down Expand Up @@ -163,38 +162,19 @@ private static <T> T getTypedProperty(Class<T> expectedDataType, String property
}

/**
* This method returns the list of supported resource types
* @return a list of resource types that isn't null
* Get the set of supported resource types for tenantId in the FHIRRequestContext
* @return an immutable set of resource type names that isn't null
* @throws IllegalStateException if there is an unexpected issue while processing the config
*/
public static List<String> getSupportedResourceTypes() throws FHIRException {
List<String> result = new ArrayList<>();

public static Set<String> getSupportedResourceTypes() {
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);
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.throwing(FHIRConfigHelper.class.getName(), "getSupportedResourceTypes", e);
throw new IllegalStateException(e);
}

return result;
}

/**
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,114 @@
/*
* (C) Copyright IBM Corp. 2022
*
* SPDX-License-Identifier: Apache-2.0
*/
package com.ibm.fhir.config;

import java.util.Arrays;
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 java.util.stream.Collectors;

import com.ibm.fhir.config.PropertyGroup.PropertyEntry;
import com.ibm.fhir.core.ResourceTypeName;

/**
* An abstraction for the ibm-fhir-server fhirServer/resources property group
*/
public class ResourcesConfigAdapter {
public static final Logger log = Logger.getLogger(ResourcesConfigAdapter.class.getName());

public static final Set<String> ALL_CONCRETE_TYPES = Arrays.stream(ResourceTypeName.values())
.filter(v -> v != ResourceTypeName.RESOURCE && v != ResourceTypeName.DOMAIN_RESOURCE)
.map(v -> v.value())
.collect(Collectors.toSet());

private final Set<String> supportedTypes;
private final Map<Interaction, Set<String>> typesByInteraction = new HashMap<>();

public ResourcesConfigAdapter(PropertyGroup resourcesConfig) throws Exception {
supportedTypes = computeSupportedResourceTypes(resourcesConfig);

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 concrete supported resource types
* @throws Exception
*/
public Set<String> getSupportedResourceTypes() {
return supportedTypes;
}

/**
* @return an immutable, non-null set of concrete resource types that are configured for the given interaction
*/
public Set<String> getSupportedResourceTypes(Interaction interaction) {
return typesByInteraction.get(interaction);
}

/**
* Construct the list of concrete supported resource types from the passed configuration
*
* @param resourcesConfig
* @return
* @throws Exception
*/
private Set<String> computeSupportedResourceTypes(PropertyGroup resourcesConfig) throws Exception {
if (resourcesConfig == null || resourcesConfig.getBooleanProperty("open", true)) {
return ALL_CONCRETE_TYPES;
}

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 (ALL_CONCRETE_TYPES.contains(name)) {
result.add(name);
} else if (log.isLoggable(Level.FINE)) {
log.fine("Configured resource type '" + name + "' is not valid.");
}
}

return Collections.unmodifiableSet(result);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* (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 jakarta.json.Json;
import jakarta.json.JsonObject;

public class ResourcesConfigAdapterTest {
@Test
public void testGetSupportedResourceTypes() throws Exception {
JsonObject json = Json.createObjectBuilder().build();
PropertyGroup pg = new PropertyGroup(json);
ResourcesConfigAdapter resourcesConfigAdapter = new ResourcesConfigAdapter(pg);

Set<String> supportedResourceTypes = resourcesConfigAdapter.getSupportedResourceTypes();
assertEquals(supportedResourceTypes.size(), 141);

System.out.println(supportedResourceTypes);

for (Interaction interaction : Interaction.values()) {
supportedResourceTypes = resourcesConfigAdapter.getSupportedResourceTypes(interaction);
assertEquals(supportedResourceTypes.size(), 141);
}
}
}
Loading

0 comments on commit 34f3653

Please sign in to comment.