Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

issue #2143 - support custom compartment types via extension #2440

Merged
merged 1 commit into from
May 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.Set;
import java.util.stream.Collectors;

import com.ibm.fhir.core.FHIRConstants;
import com.ibm.fhir.model.format.Format;
import com.ibm.fhir.model.generator.FHIRGenerator;
import com.ibm.fhir.model.resource.SearchParameter;
Expand All @@ -35,7 +36,7 @@
* to search parameters which always reference code values with a particular system
*/
public class SearchParameterAugmenter {
private static final String IMPLICIT_SYSTEM_EXT_URL = "http://ibm.com/fhir/extension/implicit-system";
private static final String IMPLICIT_SYSTEM_EXT_URL = FHIRConstants.EXT_BASE + "implicit-system";
private static final FHIRGenerator generator = FHIRGenerator.generator(Format.JSON, false);

public static void main(String[] args) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.Set;
import java.util.stream.Collectors;

import com.ibm.fhir.core.FHIRConstants;
import com.ibm.fhir.model.format.Format;
import com.ibm.fhir.model.generator.FHIRGenerator;
import com.ibm.fhir.model.resource.SearchParameter;
Expand All @@ -35,7 +36,7 @@
* to search parameters which always reference code values with a particular system
*/
public class SearchParameterAugmenter {
private static final String IMPLICIT_SYSTEM_EXT_URL = "http://ibm.com/fhir/extension/implicit-system";
private static final String IMPLICIT_SYSTEM_EXT_URL = FHIRConstants.EXT_BASE + "implicit-system";
private static final FHIRGenerator generator = FHIRGenerator.generator(Format.JSON, false);

public static void main(String[] args) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* (C) Copyright IBM Corp. 2016,2019
* (C) Copyright IBM Corp. 2016, 2021
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -38,54 +38,54 @@
import com.ibm.fhir.provider.FHIRProvider;

public class JaxrsClientTestMain {

public static void main(String[] args) throws Exception {
Patient patient = buildPatient();
System.out.println("\nJSON:");

FHIRGenerator.generator( Format.JSON, false).generate(patient, System.out);
System.out.println("\nXML:");
FHIRGenerator.generator( Format.XML, false).generate(patient, System.out);

Client client = ClientBuilder.newBuilder()
.register(new FHIRProvider(RuntimeType.CLIENT))
.build();

WebTarget target = client.target("http://localhost:9080/fhir-server/api/v4");
Entity<Patient> entity = Entity.entity(patient, FHIRMediaType.APPLICATION_FHIR_XML);
Response response = target.path("Patient").request().post(entity, Response.class);

if (Response.Status.CREATED.getStatusCode() == response.getStatus()) {
System.out.println("");
System.out.println(response.getStatus());
System.out.println(response.getStatusInfo().getReasonPhrase());
String location = response.getLocation().toString();
System.out.println("location: " + location);

String id = location.substring(location.lastIndexOf("/") + 1);
response = target.path("Patient/" + id).request(FHIRMediaType.APPLICATION_FHIR_JSON).get();
patient = response.readEntity(Patient.class);
System.out.println("\nJSON:");
FHIRGenerator.generator( Format.JSON, false).generate(patient, System.out);
System.out.println("\nXML:");
FHIRGenerator.generator( Format.XML, false).generate(patient, System.out);

Observation observation = buildObservation(id);
System.out.println("\nJSON:");
FHIRGenerator.generator( Format.JSON, false).generate(observation, System.out);
System.out.println("\nXML:");
FHIRGenerator.generator( Format.XML, false).generate(observation, System.out);

Entity<Observation> observationEntity = Entity.entity(observation, FHIRMediaType.APPLICATION_FHIR_JSON);
response = target.path("Observation").request().post(observationEntity, Response.class);

if (Response.Status.CREATED.getStatusCode() == response.getStatus()) {
System.out.println("");
System.out.println(response.getStatus());
System.out.println(response.getStatusInfo().getReasonPhrase());
location = response.getLocation().toString();
System.out.println("location: " + location);

response = target.path("Observation").queryParam("subject", "Patient/" + id).request(FHIRMediaType.APPLICATION_FHIR_JSON).get();
Bundle bundle = response.readEntity(Bundle.class);
System.out.println("\nJSON:");
Expand All @@ -105,7 +105,7 @@ public static void main(String[] args) throws Exception {
System.out.println(response.readEntity(String.class));
}
}

public static Patient buildPatient() {
Patient patient = Patient.builder().name(HumanName.builder()
.family(string("Doe"))
Expand All @@ -114,12 +114,12 @@ public static Patient buildPatient() {
.telecom(ContactPoint.builder().system(ContactPointSystem.PHONE)
.use(ContactPointUse.HOME).value(string("555-1234")).build())
.extension(Extension.builder().url("http://ibm.com/fhir/extension/Patient/favorite-color")
.value(string("blue")).build()).build();
.value(string("blue")).build()).build();
return patient;
}

public static Observation buildObservation(String patientId) {
Observation observation = Observation.builder().status(ObservationStatus.FINAL).bodySite(
Observation observation = Observation.builder().status(ObservationStatus.FINAL).bodySite(
CodeableConcept.builder().coding(Coding.builder().code(Code.of("55284-4"))
.system(Uri.of("http://loinc.org")).build())
.text(string("Blood pressure systolic & diastolic")).build())
Expand All @@ -135,8 +135,8 @@ public static Observation buildObservation(String patientId) {
.system(Uri.of("http://loinc.org")).build())
.text(string("Diastolic")).build())
.value(Quantity.builder().value(Decimal.of(93.7)).unit(string("mmHg")).build()).build())
.build();
.build();

return observation;
}
}
}
2 changes: 2 additions & 0 deletions fhir-core/src/main/java/com/ibm/fhir/core/FHIRConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public class FHIRConstants {

public static final String UPDATE_IF_MODIFIED_HEADER = "X-FHIR-UPDATE-IF-MODIFIED";

public static final String EXT_BASE = "http://ibm.com/fhir/extension/";

/**
* General parameter names that can be used with any FHIR interaction.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1334,7 +1334,7 @@ private FHIRPersistenceNotSupportedException buildNotSupportedException(String m
.severity(IssueSeverity.FATAL)
.code(IssueType.NOT_SUPPORTED.toBuilder()
.extension(Extension.builder()
.url("http://ibm.com/fhir/extension/not-supported-detail")
.url(FHIRConstants.EXT_BASE + "not-supported-detail")
.value(Code.of("interaction"))
.build())
.build())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.Map;
import java.util.Set;

import com.ibm.fhir.core.FHIRConstants;
import com.ibm.fhir.model.type.Code;
import com.ibm.fhir.model.type.Coding;
import com.ibm.fhir.model.type.Uri;
Expand Down Expand Up @@ -94,8 +95,7 @@ private SearchConstants() {
// _has
public static final String HAS = "_has";

public static final String BASE_SYSTEM_EXT_URL = "http://ibm.com/fhir/extension/";
public static final String IMPLICIT_SYSTEM_EXT_URL = BASE_SYSTEM_EXT_URL + "implicit-system";
public static final String IMPLICIT_SYSTEM_EXT_URL = FHIRConstants.EXT_BASE + "implicit-system";

// Extracted search parameter suffix for :identifier modifier
public static final String IDENTIFIER_MODIFIER_SUFFIX = ":identifier";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
import java.util.Objects;
import java.util.Set;

import com.ibm.fhir.core.FHIRConstants;
import com.ibm.fhir.model.resource.CompartmentDefinition;
import com.ibm.fhir.model.resource.CompartmentDefinition.Resource;
import com.ibm.fhir.model.type.code.CompartmentType;
import com.ibm.fhir.model.util.FHIRUtil;
import com.ibm.fhir.registry.FHIRRegistry;
import com.ibm.fhir.search.exception.FHIRSearchException;
import com.ibm.fhir.search.exception.SearchExceptionUtil;
Expand All @@ -36,6 +39,9 @@
* Call {@link #init()} to initialize static members and avoid a slight performance hit on first use.
*/
public class CompartmentUtil {
// The URL of the compartment subtype extension...useful for defining new compartments
public static final String CUSTOM_COMPARTMENT_TYPE_EXT = FHIRConstants.EXT_BASE + "custom-compartment-type";

// Map of Compartment name to CompartmentCache
private static final Map<String, CompartmentCache> compartmentMap = new HashMap<>();

Expand Down Expand Up @@ -76,7 +82,10 @@ public static final void buildMaps(Map<String, CompartmentCache> compMap, Map<St

Collection<CompartmentDefinition> definitions = FHIRRegistry.getInstance().getResources(CompartmentDefinition.class);
for (CompartmentDefinition compartmentDefinition : definitions) {
String compartmentName = compartmentDefinition.getCode().getValue();
CompartmentType type = compartmentDefinition.getCode();

String compartmentName = type.hasValue() ? type.getValue()
: FHIRUtil.getExtensionStringValue(type, CUSTOM_COMPARTMENT_TYPE_EXT);

// The cached object (a smaller/lighter lookup resource) used for point lookups
CompartmentCache compartmentDefinitionCache = new CompartmentCache();
Expand Down Expand Up @@ -131,7 +140,7 @@ public static List<String> getCompartmentResourceTypeInclusionCriteria(final Str
}

/**
* checks that the compartment is valid, and throws and exception if, not
* checks that the compartment is valid and throws an exception if not
*
* @param compartment
* @throws FHIRSearchException
Expand All @@ -144,7 +153,7 @@ public static void checkValidCompartment(final String compartment) throws FHIRSe
}

/**
* checks that the compartment and resource are valid, and throws and exception if, not
* checks that the compartment and resource are valid and throws an exception if not
*
* @param compartment
* @throws FHIRSearchException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

package com.ibm.fhir.search.reference.value;

import java.util.Objects;

/**
* Represents a reference to a resource compartment extracted by SearchUtil
Expand All @@ -25,6 +26,9 @@ public class CompartmentReference {
* @param referenceResourceValue
*/
public CompartmentReference(String parameterName, String referenceResourceType, String referenceResourceValue) {
Objects.requireNonNull(parameterName, "parameterName");
Objects.requireNonNull(referenceResourceType, "referenceResourceType");
Objects.requireNonNull(referenceResourceValue, "referenceResourceValue");
this.parameterName = parameterName;
this.referenceResourceType = referenceResourceType;
this.referenceResourceValue = referenceResourceValue;
Expand Down
49 changes: 39 additions & 10 deletions fhir-search/src/main/java/com/ibm/fhir/search/util/SearchUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

package com.ibm.fhir.search.util;

import static com.ibm.fhir.model.util.ModelSupport.FHIR_STRING;

import java.io.FileNotFoundException;
import java.math.BigDecimal;
import java.net.URISyntaxException;
Expand Down Expand Up @@ -43,6 +45,7 @@
import com.ibm.fhir.model.resource.ValueSet;
import com.ibm.fhir.model.type.Canonical;
import com.ibm.fhir.model.type.Code;
import com.ibm.fhir.model.type.Element;
import com.ibm.fhir.model.type.Reference;
import com.ibm.fhir.model.type.Uri;
import com.ibm.fhir.model.type.code.IssueSeverity;
Expand Down Expand Up @@ -2587,24 +2590,50 @@ public static Map<String, Set<CompartmentReference>> extractCompartmentParameter
}

for (FHIRPathNode node : nodes) {
Reference reference = node.asElementNode().element().as(Reference.class);
ReferenceValue rv = ReferenceUtil.createReferenceValueFrom(reference, baseUrl);
if (rv.getType() != ReferenceType.DISPLAY_ONLY && rv.getType() != ReferenceType.INVALID) {
String compartmentName = null;
String compartmentId = null;

Element element = node.asElementNode().element();
if (element.is(Reference.class)) {
Reference reference = element.as(Reference.class);
ReferenceValue rv = ReferenceUtil.createReferenceValueFrom(reference, baseUrl);
if (rv.getType() == ReferenceType.DISPLAY_ONLY || rv.getType() == ReferenceType.INVALID) {
if (log.isLoggable(Level.FINE)) {
log.fine("Skipping reference of type " + rv.getType());
}
continue;
}
compartmentName = rv.getTargetResourceType();
compartmentId = rv.getValue();

// Check that the target resource type of the reference matches one of the
// target resource types in the compartment definition.
final String compartmentName = rv.getTargetResourceType();
if (paramEntry.getValue().contains(compartmentName)) {
// Add this reference to the set of references we're collecting for each compartment
CompartmentReference cref = new CompartmentReference(searchParm, compartmentName, rv.getValue());
Set<CompartmentReference> references = result.computeIfAbsent(compartmentName, k -> new HashSet<>());
references.add(cref);
if (!paramEntry.getValue().contains(compartmentName)) {
if (log.isLoggable(Level.FINE)) {
log.fine("Skipping reference with value " + reference.getReference() + ";"
+ " target resource type does not match any of the allowed compartment types: " + paramEntry);
}
continue;
}
} else if (element.is(FHIR_STRING)) {
if (paramEntry.getValue().size() != 1) {
log.warning("CompartmentDefinition inclusion criteria must be of type Reference unless they have 1 and only 1 resource target");
continue;
}
compartmentName = paramEntry.getValue().iterator().next();
compartmentId = element.as(FHIR_STRING).getValue();
}

// Add this reference to the set of references we're collecting for each compartment
CompartmentReference cref = new CompartmentReference(searchParm, compartmentName, compartmentId);
Set<CompartmentReference> references = result.computeIfAbsent(compartmentName, k -> new HashSet<>());
references.add(cref);
}
} else if (!useStoredCompartmentParam()) {
log.warning("Compartment parameter not found: [" + resourceType + "] '" + searchParm + "'. This will stop compartment searches from working correctly.");
log.warning("Compartment parameter not found: [" + resourceType + "] '" + searchParm + "'. "
+ "This will stop compartment searches from working correctly.");
}

}
}
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* (C) Copyright IBM Corp. 2017, 2020
* (C) Copyright IBM Corp. 2017, 2021
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand All @@ -17,6 +17,7 @@

import org.testng.annotations.Test;

import com.ibm.fhir.core.FHIRConstants;
import com.ibm.fhir.core.FHIRMediaType;
import com.ibm.fhir.model.generator.exception.FHIRGeneratorException;
import com.ibm.fhir.model.resource.Bundle;
Expand All @@ -34,7 +35,7 @@
import com.ibm.fhir.model.type.Uri;

public class SearchExtensionsTest extends FHIRServerTestBase {
private static final String EXTENSION_BASE_URL = "http://ibm.com/fhir/extension/Patient/";
private static final String EXTENSION_BASE_URL = FHIRConstants.EXT_BASE + "Patient/";

private static final boolean DEBUG_SEARCH = false;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import org.owasp.encoder.Encode;

import com.ibm.fhir.core.FHIRConstants;
import com.ibm.fhir.exception.FHIROperationException;
import com.ibm.fhir.model.resource.OperationDefinition;
import com.ibm.fhir.model.resource.OperationOutcome;
Expand All @@ -29,7 +30,6 @@
import com.ibm.fhir.model.type.code.ResourceType;
import com.ibm.fhir.model.util.FHIRUtil;
import com.ibm.fhir.model.util.ModelSupport;
import com.ibm.fhir.server.util.FHIRRestHelper;

public abstract class AbstractOperation implements FHIROperation {
protected final OperationDefinition definition;
Expand Down Expand Up @@ -186,7 +186,7 @@ private FHIROperationException buildUnsupportedResourceTypeException(String reso
.severity(IssueSeverity.FATAL)
.code(IssueType.NOT_SUPPORTED.toBuilder()
.extension(Extension.builder()
.url(FHIRRestHelper.EXTENSION_URL + "/not-supported-detail")
.url(FHIRConstants.EXT_BASE + "not-supported-detail")
.value(Code.of("resource"))
.build())
.build())
Expand Down
Loading