Skip to content

Commit

Permalink
issues #3184 and #3186 - update Capabilities
Browse files Browse the repository at this point in the history
Signed-off-by: Lee Surprenant <[email protected]>
  • Loading branch information
lmsurpre committed Feb 15, 2022
1 parent 4dbe10a commit 11f750e
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 53 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@
import java.util.logging.Logger;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

Expand All @@ -52,7 +54,6 @@
import com.ibm.fhir.config.FHIRConfigHelper;
import com.ibm.fhir.config.FHIRConfiguration;
import com.ibm.fhir.config.PropertyGroup;
import com.ibm.fhir.config.PropertyGroup.PropertyEntry;
import com.ibm.fhir.core.FHIRMediaType;
import com.ibm.fhir.exception.FHIROperationException;
import com.ibm.fhir.model.format.Format;
Expand Down Expand Up @@ -90,6 +91,7 @@
import com.ibm.fhir.model.type.code.RestfulCapabilityMode;
import com.ibm.fhir.model.type.code.SystemRestfulInteraction;
import com.ibm.fhir.model.type.code.TypeRestfulInteraction;
import com.ibm.fhir.model.type.code.TypeRestfulInteraction.Value;
import com.ibm.fhir.model.util.ModelSupport;
import com.ibm.fhir.persistence.exception.FHIRPersistenceException;
import com.ibm.fhir.registry.FHIRRegistry;
Expand Down Expand Up @@ -126,6 +128,9 @@ public class Capabilities extends FHIRResource {

private static final String CAPABILITY_STATEMENT_CACHE_NAME = "com.ibm.fhir.server.resources.Capabilities.statementCache";

@Context
protected HttpServletRequest httpServletRequest;

// Constructor
public Capabilities() throws Exception {
super();
Expand Down Expand Up @@ -290,15 +295,17 @@ private CapabilityStatement buildCapabilityStatement() throws Exception {
PropertyGroup rsrcsGroup = FHIRConfigHelper.getPropertyGroup(FHIRConfiguration.PROPERTY_RESOURCES);

// Build the list of interactions, searchIncludes, and searchRevIncludes supported for each resource type by default.
List<Rest.Resource.Interaction> defaultInteractions = buildInteractions(ALL_INTERACTIONS);
List<Rest.Interaction> systemInteractions = buildSystemInteractions(ALL_INTERACTIONS);
List<Rest.Resource.Interaction> defaultTypeInteractions = buildTypeInteractions(ALL_INTERACTIONS);
List<com.ibm.fhir.model.type.String> defaultSearchIncludes = Collections.emptyList();
List<com.ibm.fhir.model.type.String> defaultSearchRevIncludes = Collections.emptyList();
if (rsrcsGroup != null) {
PropertyGroup parentResourcePropGroup = rsrcsGroup.getPropertyGroup(ResourceType.Value.RESOURCE.value());
if (parentResourcePropGroup != null) {
List<String> interactionConfig = parentResourcePropGroup.getStringListProperty(FHIRConfiguration.PROPERTY_FIELD_RESOURCES_INTERACTIONS);
if (interactionConfig != null) {
defaultInteractions = buildInteractions(interactionConfig);
systemInteractions = buildSystemInteractions(interactionConfig);
defaultTypeInteractions = buildTypeInteractions(interactionConfig);
}
List<String> searchIncludeConfig = parentResourcePropGroup.getStringListProperty(FHIRConfiguration.PROPERTY_FIELD_RESOURCES_SEARCH_INCLUDES);
if (searchIncludeConfig != null) {
Expand Down Expand Up @@ -338,6 +345,8 @@ private CapabilityStatement buildCapabilityStatement() throws Exception {

com.ibm.fhir.model.type.Boolean isUpdateCreate = com.ibm.fhir.model.type.Boolean.of(isUpdateCreateEnabled());

FHIRVersion fhirVersion = FHIRVersion.VERSION_4_0_1;

// Build the list of supported resources.
List<Rest.Resource> resources = new ArrayList<>();

Expand Down Expand Up @@ -375,7 +384,7 @@ private CapabilityStatement buildCapabilityStatement() throws Exception {
}

// Build the list of interactions, searchIncludes, and searchRevIncludes supported for the resource type.
List<Interaction> interactions = defaultInteractions;
List<Interaction> interactions = defaultTypeInteractions;
List<com.ibm.fhir.model.type.String> searchIncludes = defaultSearchIncludes;
List<com.ibm.fhir.model.type.String> searchRevIncludes = defaultSearchRevIncludes;
if (rsrcsGroup != null) {
Expand All @@ -384,7 +393,7 @@ private CapabilityStatement buildCapabilityStatement() throws Exception {
List<String> resourceInteractionConfig =
resourcePropGroup.getStringListProperty(FHIRConfiguration.PROPERTY_FIELD_RESOURCES_INTERACTIONS);
if (resourceInteractionConfig != null) {
interactions = buildInteractions(resourceInteractionConfig);
interactions = buildTypeInteractions(resourceInteractionConfig);
}
List<String> searchIncludeConfig = resourcePropGroup.getStringListProperty(FHIRConfiguration.PROPERTY_FIELD_RESOURCES_SEARCH_INCLUDES);
if (searchIncludeConfig != null) {
Expand All @@ -399,7 +408,7 @@ private CapabilityStatement buildCapabilityStatement() throws Exception {
}

// Build the ConformanceResource for this resource type.
Rest.Resource cr = Rest.Resource.builder()
Rest.Resource.Builder crb = Rest.Resource.builder()
.type(ResourceType.of(resourceType))
.profile(Canonical.of("http://hl7.org/fhir/profiles/" + resourceTypeName))
.interaction(interactions)
Expand All @@ -412,20 +421,12 @@ private CapabilityStatement buildCapabilityStatement() throws Exception {
.conditionalRead(ConditionalReadStatus.FULL_SUPPORT)
.searchParam(conformanceSearchParams)
.searchInclude(searchIncludes)
.searchRevInclude(searchRevIncludes)
.build();

resources.add(cr);
}

// Determine if transactions are supported for this FHIR Server configuration.
SystemRestfulInteraction transactionMode = SystemRestfulInteraction.BATCH;
try {
boolean txnSupported = getPersistenceImpl().isTransactional();
transactionMode = (txnSupported ? SystemRestfulInteraction.TRANSACTION
: SystemRestfulInteraction.BATCH);
} catch (Throwable t) {
log.log(Level.WARNING, "Unexpected error while reading server transaction mode setting", t);
.searchRevInclude(searchRevIncludes);
// Set readHistory to true if vread is supported for this resource type; otherwise leave it null
if (interactions.stream().anyMatch(i -> i.getCode().getValueAsEnum() == Value.VREAD)) {
crb.readHistory(true);
}
resources.add(crb.build());
}

CapabilityStatement.Rest.Security.Builder securityBuilder = CapabilityStatement.Rest.Security.builder()
Expand Down Expand Up @@ -489,9 +490,7 @@ private CapabilityStatement buildCapabilityStatement() throws Exception {
.mode(RestfulCapabilityMode.SERVER)
.security(securityBuilder.build())
.resource(addSupportedProfilesToResources(resources))
.interaction(CapabilityStatement.Rest.Interaction.builder()
.code(transactionMode)
.build())
.interaction(systemInteractions)
.operation(mapOperationDefinitionsToRestOperations(systemOps))
.build();

Expand Down Expand Up @@ -529,7 +528,7 @@ private CapabilityStatement buildCapabilityStatement() throws Exception {
.status(PublicationStatus.ACTIVE)
.date(DateTime.now(ZoneOffset.UTC))
.kind(CapabilityStatementKind.INSTANCE)
.fhirVersion(FHIRVersion.VERSION_4_3_0_CIBUILD)
.fhirVersion(fhirVersion)
.format(format)
.patchFormat(Code.of(FHIRMediaType.APPLICATION_JSON_PATCH),
Code.of(FHIRMediaType.APPLICATION_FHIR_JSON),
Expand Down Expand Up @@ -558,28 +557,61 @@ private CapabilityStatement buildCapabilityStatement() throws Exception {
return conformance;
}

/**
* @param interactionConfig a list of strings that represent the RESTful interactions to support at the system level
* (create, read, vread, update, patch, delete, history, and/or search)
* @return a list of Rest.Resource.Interaction objects to include in the CapabilityStatement
* @throws FHIRPersistenceException
*/
private List<Rest.Interaction> buildSystemInteractions(List<String> interactionConfig) throws Exception {
List<Rest.Interaction> interactions = new ArrayList<>();
interactions.add(buildSystemInteractionStatement(SystemRestfulInteraction.BATCH));
try {
// If transactions are supported for this FHIR Server configuration
if (getPersistenceImpl().isTransactional()) {
interactions.add(buildSystemInteractionStatement(SystemRestfulInteraction.TRANSACTION));
}
} catch (Throwable t) {
log.log(Level.WARNING, "Unexpected error while reading server transaction mode setting", t);
}

if (interactionConfig != null) {
for (String interactionString : interactionConfig) {
if ("search".equals(interactionString)) {
// special case for search since the value set uses "search-system" instead of just "search"
interactions.add(buildSystemInteractionStatement(SystemRestfulInteraction.SEARCH_SYSTEM));
} else if ("history".equals(interactionString)){
// special case for search since the value set uses "history-system" instead of just "history"
interactions.add(buildSystemInteractionStatement(SystemRestfulInteraction.HISTORY_SYSTEM));
}
}
}
return interactions;
}

/**
* @param interactionConfig a list of strings that represent the RESTful interactions to support for this resource type
* (create, read, vread, update, patch, delete, history, and/or search)
* @return a list of Rest.Resource.Interaction objects to include in the CapabilityStatement
* @throws FHIRPersistenceException
*/
private List<Rest.Resource.Interaction> buildInteractions(List<String> interactionConfig) throws Exception {
private List<Rest.Resource.Interaction> buildTypeInteractions(List<String> interactionConfig) throws Exception {
if (interactionConfig == null) return null;

List<Rest.Resource.Interaction> interactions = new ArrayList<>();
for (String interactionString : interactionConfig) {
if ("search".equals(interactionString)) {
// special case for search since the value set uses "search-type" instead of just "search"
interactions.add(buildInteractionStatement(TypeRestfulInteraction.SEARCH_TYPE));
interactions.add(buildTypeInteractionStatement(TypeRestfulInteraction.SEARCH_TYPE));
} else if ("history".equals(interactionString)){
// special case for search since the value set uses "history-instance" instead of just "history"
interactions.add(buildInteractionStatement(TypeRestfulInteraction.HISTORY_INSTANCE));
// special case for search since the value set has "history-type" and "history-instance" instead of just "history"
interactions.add(buildTypeInteractionStatement(TypeRestfulInteraction.HISTORY_TYPE));
interactions.add(buildTypeInteractionStatement(TypeRestfulInteraction.HISTORY_INSTANCE));
} else if ("delete".equals(interactionString)) {
// special case for delete since we shouldn't advertise it if the PL doesn't support it
interactions.add(buildInteractionStatement(TypeRestfulInteraction.DELETE));
interactions.add(buildTypeInteractionStatement(TypeRestfulInteraction.DELETE));
} else {
interactions.add(buildInteractionStatement(TypeRestfulInteraction.of(interactionString)));
interactions.add(buildTypeInteractionStatement(TypeRestfulInteraction.of(interactionString)));
}
}
return interactions;
Expand Down Expand Up @@ -611,20 +643,9 @@ private List<ResourceType.Value> getSupportedResourceTypes(PropertyGroup rsrcsGr
if (rsrcsGroup.getBooleanProperty(FHIRConfiguration.PROPERTY_FIELD_RESOURCES_OPEN, true)) {
resourceTypes = ALL_RESOURCE_TYPES;
} else {
List<PropertyEntry> 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
if (!FHIRConfiguration.PROPERTY_FIELD_RESOURCES_OPEN.equals(name)) {
// Skip the abstract types Resource and DomainResource
if (!Resource.class.equals(ModelSupport.getResourceType(name)) &&
!DomainResource.class.equals(ModelSupport.getResourceType(name))) {
resourceTypes.add(ResourceType.Value.from(name));
}
}
}
}
resourceTypes = FHIRConfigHelper.getSupportedResourceTypes().stream()
.map(ResourceType.Value::from)
.collect(Collectors.toList());
}
return resourceTypes;
}
Expand Down Expand Up @@ -799,8 +820,13 @@ private String getNotificationResourceTypes() throws Exception {
return Arrays.asList(notificationResourceTypes).toString().replace("[", "").replace("]", "").replace(" ", "");
}

private Interaction buildInteractionStatement(TypeRestfulInteraction value) {
Interaction ci = Interaction.builder().code(value).build();
private Rest.Resource.Interaction buildTypeInteractionStatement(TypeRestfulInteraction value) {
Rest.Resource.Interaction ci = Rest.Resource.Interaction.builder().code(value).build();
return ci;
}

private Rest.Interaction buildSystemInteractionStatement(SystemRestfulInteraction value) {
Rest.Interaction ci = Rest.Interaction.builder().code(value).build();
return ci;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.ibm.fhir.server.resources.Capabilities;

public class CapabilitiesTest {
private static final boolean DEBUG = false;

@BeforeClass
void setup() {
Expand All @@ -51,7 +52,7 @@ void testBuildCapabilityStatement_resources_omitted() throws Exception {
assertEquals(capabilityStatement.getRest().size(), 1, "Number of REST Elements");
CapabilityStatement.Rest restDefinition = capabilityStatement.getRest().get(0);

assertRestDefinition(restDefinition, 141, 8, 0, 0, 8, 0, 0);
assertRestDefinition(restDefinition, 4, 141, 9, 0, 0, 9, 0, 0);
}

@Test
Expand All @@ -66,7 +67,7 @@ void testBuildCapabilityStatement_resources_empty() throws Exception {
assertEquals(capabilityStatement.getRest().size(), 1, "Number of REST Elements");
CapabilityStatement.Rest restDefinition = capabilityStatement.getRest().get(0);

assertRestDefinition(restDefinition, 141, 0, 0, 0, 0, 0, 0);
assertRestDefinition(restDefinition, 0, 141, 0, 0, 0, 0, 0, 0);
}

@Test
Expand All @@ -81,12 +82,15 @@ void testBuildCapabilityStatement_resources_filtered() throws Exception {
assertEquals(capabilityStatement.getRest().size(), 1, "Number of REST Elements");
CapabilityStatement.Rest restDefinition = capabilityStatement.getRest().get(0);

assertRestDefinition(restDefinition, 2, 2, 1, 0, 4, 0, 1);
assertRestDefinition(restDefinition, 0, 2, 2, 1, 0, 5, 0, 1);
}

private void assertRestDefinition(CapabilityStatement.Rest restDefinition, int numOfResources,
int patientInteractions, int patientIncludes, int patientRevIncludes,
int practitionerInteractions, int practitionerIncludes, int practitionerRevIncludes) {
private void assertRestDefinition(CapabilityStatement.Rest restDefinition, int systemInteractions, int numOfResources,
int patientInteractions, int patientIncludes, int patientRevIncludes,
int practitionerInteractions, int practitionerIncludes, int practitionerRevIncludes) {
if (DEBUG) {
System.out.println(restDefinition);
}
assertEquals(restDefinition.getResource().size(), numOfResources, "Number of supported resources");
assertFalse(restDefinition.getResource().stream().anyMatch(r -> r.getType().getValueAsEnum() == ResourceType.Value.RESOURCE));
assertFalse(restDefinition.getResource().stream().anyMatch(r -> r.getType().getValueAsEnum() == ResourceType.Value.DOMAIN_RESOURCE));
Expand All @@ -96,7 +100,7 @@ private void assertRestDefinition(CapabilityStatement.Rest restDefinition, int n
}

private void assertResourceDefinition(CapabilityStatement.Rest restDefinition, ResourceType.Value resourceType, int numOfInteractions,
int numIncludes, int numRevIncludes) {
int numIncludes, int numRevIncludes) {
Optional<CapabilityStatement.Rest.Resource> resource = restDefinition.getResource().stream()
.filter(r -> r.getType().getValueAsEnum() == resourceType)
.findFirst();
Expand Down

0 comments on commit 11f750e

Please sign in to comment.