From 240745debaa8e8716cdd3229affd1f5f368baae1 Mon Sep 17 00:00:00 2001 From: Lee Surprenant Date: Thu, 27 May 2021 14:44:14 -0400 Subject: [PATCH] issue #2143 - support custom compartment types via extension The CompartmentDefinition resource has a required binding to a fixed list of compartment types. To support a custom compartments, we will now look for the `http://ibm.com/fhir/extension/custom-compartment-type` extension on this element (when it has no value). Additionally, I created a constant in fhir-core FHIRConstants for our base extension url and I updated all the different places we have extension urls to use it. Finally, I included a couple extra changes that I had sitting around for the FHIR Registry. If desired, I can split these out into a separate PR. Signed-off-by: Lee Surprenant --- .../pdex/tool/SearchParameterAugmenter.java | 3 +- .../core/tool/SearchParameterAugmenter.java | 3 +- .../test/mains/JaxrsClientTestMain.java | 34 ++++++------- .../java/com/ibm/fhir/core/FHIRConstants.java | 2 + .../jdbc/impl/FHIRPersistenceJDBCImpl.java | 2 +- .../com/ibm/fhir/registry/FHIRRegistry.java | 12 ++--- .../resource/FHIRRegistryResource.java | 4 +- .../com/ibm/fhir/search/SearchConstants.java | 4 +- .../search/compartment/CompartmentUtil.java | 15 ++++-- .../reference/value/CompartmentReference.java | 4 ++ .../com/ibm/fhir/search/util/SearchUtil.java | 49 +++++++++++++++---- .../server/test/SearchExtensionsTest.java | 5 +- .../operation/spi/AbstractOperation.java | 4 +- .../fhir/server/resources/Capabilities.java | 16 +++--- .../ibm/fhir/server/util/FHIRRestHelper.java | 10 ++-- .../util/IssueTypeToHttpStatusMapper.java | 8 +-- .../util/IssueTypeToHttpStatusMapperTest.java | 11 ++++- .../fhir/operation/apply/ApplyOperation.java | 13 ++--- .../fhir/operation/term/LookupOperation.java | 4 +- 19 files changed, 127 insertions(+), 76 deletions(-) diff --git a/conformance/fhir-ig-davinci-pdex/src/test/java/com/ibm/fhir/ig/davinci/pdex/tool/SearchParameterAugmenter.java b/conformance/fhir-ig-davinci-pdex/src/test/java/com/ibm/fhir/ig/davinci/pdex/tool/SearchParameterAugmenter.java index 95380d243f5..24ac088aa3c 100644 --- a/conformance/fhir-ig-davinci-pdex/src/test/java/com/ibm/fhir/ig/davinci/pdex/tool/SearchParameterAugmenter.java +++ b/conformance/fhir-ig-davinci-pdex/src/test/java/com/ibm/fhir/ig/davinci/pdex/tool/SearchParameterAugmenter.java @@ -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; @@ -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 { diff --git a/conformance/fhir-ig-us-core/src/test/java/com/ibm/fhir/ig/us/core/tool/SearchParameterAugmenter.java b/conformance/fhir-ig-us-core/src/test/java/com/ibm/fhir/ig/us/core/tool/SearchParameterAugmenter.java index be5debb539a..bcb42e1ce45 100644 --- a/conformance/fhir-ig-us-core/src/test/java/com/ibm/fhir/ig/us/core/tool/SearchParameterAugmenter.java +++ b/conformance/fhir-ig-us-core/src/test/java/com/ibm/fhir/ig/us/core/tool/SearchParameterAugmenter.java @@ -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; @@ -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 { diff --git a/fhir-client/src/test/java/com/ibm/fhir/client/test/mains/JaxrsClientTestMain.java b/fhir-client/src/test/java/com/ibm/fhir/client/test/mains/JaxrsClientTestMain.java index defc5bed513..ff5f95fc1ca 100644 --- a/fhir-client/src/test/java/com/ibm/fhir/client/test/mains/JaxrsClientTestMain.java +++ b/fhir-client/src/test/java/com/ibm/fhir/client/test/mains/JaxrsClientTestMain.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2016,2019 + * (C) Copyright IBM Corp. 2016, 2021 * * SPDX-License-Identifier: Apache-2.0 */ @@ -38,30 +38,30 @@ 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 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); @@ -69,23 +69,23 @@ public static void main(String[] args) throws Exception { 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 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:"); @@ -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")) @@ -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()) @@ -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; - } + } } diff --git a/fhir-core/src/main/java/com/ibm/fhir/core/FHIRConstants.java b/fhir-core/src/main/java/com/ibm/fhir/core/FHIRConstants.java index 64f509e08c6..a99c6bda616 100644 --- a/fhir-core/src/main/java/com/ibm/fhir/core/FHIRConstants.java +++ b/fhir-core/src/main/java/com/ibm/fhir/core/FHIRConstants.java @@ -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. * diff --git a/fhir-persistence-jdbc/src/main/java/com/ibm/fhir/persistence/jdbc/impl/FHIRPersistenceJDBCImpl.java b/fhir-persistence-jdbc/src/main/java/com/ibm/fhir/persistence/jdbc/impl/FHIRPersistenceJDBCImpl.java index a09369988d6..49a0ede1f47 100644 --- a/fhir-persistence-jdbc/src/main/java/com/ibm/fhir/persistence/jdbc/impl/FHIRPersistenceJDBCImpl.java +++ b/fhir-persistence-jdbc/src/main/java/com/ibm/fhir/persistence/jdbc/impl/FHIRPersistenceJDBCImpl.java @@ -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()) diff --git a/fhir-registry/src/main/java/com/ibm/fhir/registry/FHIRRegistry.java b/fhir-registry/src/main/java/com/ibm/fhir/registry/FHIRRegistry.java index e48e69efc8b..bf9179459d1 100644 --- a/fhir-registry/src/main/java/com/ibm/fhir/registry/FHIRRegistry.java +++ b/fhir-registry/src/main/java/com/ibm/fhir/registry/FHIRRegistry.java @@ -22,6 +22,7 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Logger; +import java.util.stream.Collectors; import com.ibm.fhir.model.resource.DomainResource; import com.ibm.fhir.model.resource.Resource; @@ -177,13 +178,10 @@ public T getResource(String url, Class resourceType) { public Collection getResources(Class resourceType) { Objects.requireNonNull(resourceType); requireDefinitionalResourceType(resourceType); - List resources = new ArrayList<>(); - for (FHIRRegistryResourceProvider provider : providers) { - for (FHIRRegistryResource registryResource : provider.getRegistryResources(resourceType)) { - resources.add(resourceType.cast(registryResource.getResource())); - } - } - return Collections.unmodifiableList(resources); + return providers.parallelStream() + .flatMap(p -> p.getRegistryResources(resourceType).stream()) + .map(rr -> resourceType.cast(rr.getResource())) + .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); } /** diff --git a/fhir-registry/src/main/java/com/ibm/fhir/registry/resource/FHIRRegistryResource.java b/fhir-registry/src/main/java/com/ibm/fhir/registry/resource/FHIRRegistryResource.java index eadf8350fde..2511ba6b7d5 100644 --- a/fhir-registry/src/main/java/com/ibm/fhir/registry/resource/FHIRRegistryResource.java +++ b/fhir-registry/src/main/java/com/ibm/fhir/registry/resource/FHIRRegistryResource.java @@ -111,8 +111,8 @@ public boolean isDefaultVersion() { return defaultVersion; } - public Resource getResource() { - return resource; + public T getResource() { + return (T) resource; } public boolean is(Class registryResourceType) { diff --git a/fhir-search/src/main/java/com/ibm/fhir/search/SearchConstants.java b/fhir-search/src/main/java/com/ibm/fhir/search/SearchConstants.java index 220810d8c89..4a2b18bacb6 100644 --- a/fhir-search/src/main/java/com/ibm/fhir/search/SearchConstants.java +++ b/fhir-search/src/main/java/com/ibm/fhir/search/SearchConstants.java @@ -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; @@ -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"; diff --git a/fhir-search/src/main/java/com/ibm/fhir/search/compartment/CompartmentUtil.java b/fhir-search/src/main/java/com/ibm/fhir/search/compartment/CompartmentUtil.java index ee1d731fb42..564828f8604 100644 --- a/fhir-search/src/main/java/com/ibm/fhir/search/compartment/CompartmentUtil.java +++ b/fhir-search/src/main/java/com/ibm/fhir/search/compartment/CompartmentUtil.java @@ -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; @@ -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 compartmentMap = new HashMap<>(); @@ -76,7 +82,10 @@ public static final void buildMaps(Map compMap, Map 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(); @@ -131,7 +140,7 @@ public static List 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 @@ -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 diff --git a/fhir-search/src/main/java/com/ibm/fhir/search/reference/value/CompartmentReference.java b/fhir-search/src/main/java/com/ibm/fhir/search/reference/value/CompartmentReference.java index 988d27aed9b..de214e59289 100644 --- a/fhir-search/src/main/java/com/ibm/fhir/search/reference/value/CompartmentReference.java +++ b/fhir-search/src/main/java/com/ibm/fhir/search/reference/value/CompartmentReference.java @@ -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 @@ -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; 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 0f46db5574d..b0e833dafd4 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 @@ -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; @@ -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; @@ -2587,24 +2590,50 @@ public static Map> 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 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 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) { diff --git a/fhir-server-test/src/test/java/com/ibm/fhir/server/test/SearchExtensionsTest.java b/fhir-server-test/src/test/java/com/ibm/fhir/server/test/SearchExtensionsTest.java index 284835f72bb..ca80fec100e 100644 --- a/fhir-server-test/src/test/java/com/ibm/fhir/server/test/SearchExtensionsTest.java +++ b/fhir-server-test/src/test/java/com/ibm/fhir/server/test/SearchExtensionsTest.java @@ -1,5 +1,5 @@ /* - * (C) Copyright IBM Corp. 2017, 2020 + * (C) Copyright IBM Corp. 2017, 2021 * * SPDX-License-Identifier: Apache-2.0 */ @@ -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; @@ -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; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/AbstractOperation.java b/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/AbstractOperation.java index fdff32bc6f0..7296a4ec0dd 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/AbstractOperation.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/AbstractOperation.java @@ -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; @@ -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; @@ -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()) 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 656b56fc3ce..0da5e8324ab 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 @@ -13,6 +13,7 @@ import static com.ibm.fhir.config.FHIRConfiguration.PROPERTY_SECURITY_OAUTH_REG_URL; import static com.ibm.fhir.config.FHIRConfiguration.PROPERTY_SECURITY_OAUTH_REVOKE_URL; import static com.ibm.fhir.config.FHIRConfiguration.PROPERTY_SECURITY_OAUTH_TOKEN_URL; +import static com.ibm.fhir.core.FHIRConstants.EXT_BASE; import static com.ibm.fhir.model.type.String.string; import static com.ibm.fhir.server.util.IssueTypeToHttpStatusMapper.issueListToStatus; @@ -110,7 +111,6 @@ public class Capabilities extends FHIRResource { private static final String FHIR_SERVER_NAME = "IBM FHIR Server"; private static final String FHIR_COPYRIGHT = "(C) Copyright IBM Corporation 2016, 2021"; private static final String FHIR_PUBLISHER = "IBM Corporation"; - private static final String EXTENSION_URL = "http://ibm.com/fhir/extension"; private static final String BASE_CAPABILITY_URL = "http://hl7.org/fhir/CapabilityStatement/base"; private static final String BASE_2_CAPABILITY_URL = "http://hl7.org/fhir/CapabilityStatement/base2"; private static final List ALL_INTERACTIONS = Arrays.asList("create", "read", "vread", "update", "patch", "delete", "history", "search"); @@ -688,25 +688,25 @@ private CapabilityStatement addExtensionElements(CapabilityStatement capabilityS throws Exception { List extentions = new ArrayList<>(); Extension extension = Extension.builder() - .url(EXTENSION_URL + "/defaultTenantId") + .url(EXT_BASE + "defaultTenantId") .value(string(fhirConfig.getStringProperty(FHIRConfiguration.PROPERTY_DEFAULT_TENANT_ID, FHIRConfiguration.DEFAULT_TENANT_ID))) .build(); extentions.add(extension); extension = Extension.builder() - .url(EXTENSION_URL + "/websocketNotificationsEnabled") + .url(EXT_BASE + "websocketNotificationsEnabled") .value(com.ibm.fhir.model.type.Boolean.of(fhirConfig.getBooleanProperty(FHIRConfiguration.PROPERTY_WEBSOCKET_ENABLED, Boolean.FALSE))) .build(); extentions.add(extension); extension = Extension.builder() - .url(EXTENSION_URL + "/kafkaNotificationsEnabled") + .url(EXT_BASE + "kafkaNotificationsEnabled") .value(com.ibm.fhir.model.type.Boolean.of(fhirConfig.getBooleanProperty(FHIRConfiguration.PROPERTY_KAFKA_ENABLED, Boolean.FALSE))) .build(); extentions.add(extension); extension = Extension.builder() - .url(EXTENSION_URL + "/natsNotificationsEnabled") + .url(EXT_BASE + "natsNotificationsEnabled") .value(com.ibm.fhir.model.type.Boolean.of(fhirConfig.getBooleanProperty(FHIRConfiguration.PROPERTY_NATS_ENABLED, Boolean.FALSE))) .build(); extentions.add(extension); @@ -717,7 +717,7 @@ private CapabilityStatement addExtensionElements(CapabilityStatement capabilityS } extension = Extension.builder() - .url(EXTENSION_URL + "/notificationResourceTypes") + .url(EXT_BASE + "notificationResourceTypes") .value(string(notificationResourceTypes)) .build(); extentions.add(extension); @@ -733,13 +733,13 @@ private CapabilityStatement addExtensionElements(CapabilityStatement capabilityS } extension = Extension.builder() - .url(EXTENSION_URL + "/auditLogServiceName") + .url(EXT_BASE + "auditLogServiceName") .value(string(auditLogServiceName)) .build(); extentions.add(extension); extension = Extension.builder() - .url(EXTENSION_URL + "/persistenceType") + .url(EXT_BASE + "persistenceType") .value(string(getPersistenceImpl().getClass().getSimpleName())) .build(); extentions.add(extension); 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 a3789cae2cd..cb098f7d873 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.core.FHIRConstants.EXT_BASE; import static com.ibm.fhir.model.type.String.string; import static com.ibm.fhir.model.util.ModelSupport.getResourceType; import static javax.servlet.http.HttpServletResponse.SC_ACCEPTED; @@ -133,7 +134,6 @@ public class FHIRRestHelper implements FHIRResourceHelpers { private static final Logger log = java.util.logging.Logger.getLogger(FHIRRestHelper.class.getName()); - public static final String EXTENSION_URL = "http://ibm.com/fhir/extension"; private static final String LOCAL_REF_PREFIX = "urn:"; 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_GONE_STRING = string(Integer.toString(SC_GONE)); @@ -1281,7 +1281,7 @@ private Map validateBundle(Bundle bundle) throws Exception { + "the configured persistence layer does not support transactions."; IssueType extendedIssueType = IssueType.NOT_SUPPORTED.toBuilder() .extension(Extension.builder() - .url(EXTENSION_URL + "/not-supported-detail") + .url(EXT_BASE + "not-supported-detail") .value(Code.of("interaction")) .build()) .build(); @@ -1419,7 +1419,7 @@ private void methodValidation(HTTPVerb method, Resource resource) throws FHIRPer + method.getValue(); IssueType extendedIssueType = IssueType.NOT_SUPPORTED.toBuilder() .extension(Extension.builder() - .url(EXTENSION_URL + "/not-supported-detail") + .url(EXT_BASE + "not-supported-detail") .value(Code.of("interaction")) .build()) .build(); @@ -1489,7 +1489,7 @@ private void performVersionAwareUpdateCheck(Resource currentResource, String ifM + "' does not match current latest version of resource: " + currentVersion; IssueType extendedIssueType = IssueType.CONFLICT.toBuilder() .extension(Extension.builder() - .url(EXTENSION_URL + "/http-failed-precondition") + .url(EXT_BASE + "http-failed-precondition") .value(string("If-Match")) .build()) .build(); @@ -1504,7 +1504,7 @@ private FHIROperationException buildUnsupportedResourceTypeException(String reso .severity(IssueSeverity.FATAL) .code(IssueType.NOT_SUPPORTED.toBuilder() .extension(Extension.builder() - .url(EXTENSION_URL + "/not-supported-detail") + .url(EXT_BASE + "not-supported-detail") .value(Code.of("resource")) .build()) .build()) diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/util/IssueTypeToHttpStatusMapper.java b/fhir-server/src/main/java/com/ibm/fhir/server/util/IssueTypeToHttpStatusMapper.java index 1cb4f781aee..acbdd38f5fd 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/util/IssueTypeToHttpStatusMapper.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/util/IssueTypeToHttpStatusMapper.java @@ -6,6 +6,8 @@ package com.ibm.fhir.server.util; +import static com.ibm.fhir.core.FHIRConstants.EXT_BASE; + import java.util.List; import javax.ws.rs.core.Response; @@ -20,18 +22,18 @@ public class IssueTypeToHttpStatusMapper { /** * Custom extension used by the IBM FHIR Server for marking which precondition has failed (if any) */ - private static final String EXTENSION_URL_HTTP_FAILED_PRECONDITION = "http://ibm.com/fhir/extension/http-failed-precondition"; + private static final String EXTENSION_URL_HTTP_FAILED_PRECONDITION = EXT_BASE + "http-failed-precondition"; /** * Custom extension used by the IBM FHIR Server for marking what it was that wasn't supported: * resource | interaction */ - private static final String EXTENSION_URL_NOT_SUPPORTED_DETAIL = "http://ibm.com/fhir/extension/not-supported-detail"; + private static final String EXTENSION_URL_NOT_SUPPORTED_DETAIL = EXT_BASE + "not-supported-detail"; /** * Custom extension used by the IBM FHIR Server for marking which code was not found */ - private static final String EXTENSION_URL_NOT_FOUND_DETAIL = "http://ibm.com/fhir/extension/not-found-detail"; + private static final String EXTENSION_URL_NOT_FOUND_DETAIL = EXT_BASE + "not-found-detail"; /** * @return an HTTP response status based on the first issue contained within the OperationOutcome with a code; diff --git a/fhir-server/src/test/java/com/ibm/fhir/server/util/IssueTypeToHttpStatusMapperTest.java b/fhir-server/src/test/java/com/ibm/fhir/server/util/IssueTypeToHttpStatusMapperTest.java index 812167daab0..f4a46344350 100644 --- a/fhir-server/src/test/java/com/ibm/fhir/server/util/IssueTypeToHttpStatusMapperTest.java +++ b/fhir-server/src/test/java/com/ibm/fhir/server/util/IssueTypeToHttpStatusMapperTest.java @@ -1,3 +1,9 @@ +/* + * (C) Copyright IBM Corp. 2020, 2021 + * + * SPDX-License-Identifier: Apache-2.0 + */ + package com.ibm.fhir.server.util; import static org.testng.Assert.assertEquals; @@ -8,6 +14,7 @@ import org.testng.annotations.Test; +import com.ibm.fhir.core.FHIRConstants; import com.ibm.fhir.model.resource.OperationOutcome; import com.ibm.fhir.model.type.Code; import com.ibm.fhir.model.type.CodeableConcept; @@ -24,12 +31,12 @@ public void testExtensionCodeNotFound__bad_request() { IssueType issueType = IssueType.NOT_FOUND.toBuilder() .extension(Extension.builder() - .url(FHIRRestHelper.EXTENSION_URL + "/not-found-detail") + .url(FHIRConstants.EXT_BASE + "not-found-detail") .value(Code.of("code")).build()).build(); OperationOutcome.Issue issue = OperationOutcome.Issue.builder().severity(IssueSeverity.ERROR).code(issueType) .details(CodeableConcept.builder().coding(coding).build()).build(); - + System.out.println(issue.toString()); Response.Status status = IssueTypeToHttpStatusMapper.issueListToStatus(Arrays.asList(issue)); diff --git a/operation/fhir-operation-apply/src/main/java/com/ibm/fhir/operation/apply/ApplyOperation.java b/operation/fhir-operation-apply/src/main/java/com/ibm/fhir/operation/apply/ApplyOperation.java index 7eec3df0fd8..28c870decc2 100644 --- a/operation/fhir-operation-apply/src/main/java/com/ibm/fhir/operation/apply/ApplyOperation.java +++ b/operation/fhir-operation-apply/src/main/java/com/ibm/fhir/operation/apply/ApplyOperation.java @@ -14,6 +14,7 @@ import javax.ws.rs.core.MultivaluedMap; +import com.ibm.fhir.core.FHIRConstants; import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.resource.ActivityDefinition; import com.ibm.fhir.model.resource.CarePlan; @@ -68,8 +69,7 @@ public class ApplyOperation extends AbstractOperation { private static final String PARAM_SETTING = "setting"; private static final String PARAM_SETTING_CONTEXT = "settingContext"; - private static final String EXTENSION_BASE_URL = - "http://ibm.com/fhir/extension/apply/"; + private static final String EXTENSION_BASE_URL = FHIRConstants.EXT_BASE + "apply/"; @Override protected OperationDefinition buildOperationDefinition() { @@ -290,20 +290,17 @@ private CarePlan transform(PlanDefinition planDefinition, List subjects, // Setting if (setting != null) { - builder.extension(Extension.builder().value(setting).url(EXTENSION_BASE_URL - + "/setting").build()); + builder.extension(Extension.builder().value(setting).url(EXTENSION_BASE_URL + "setting").build()); } // SettingContext if (settingContext != null) { - builder.extension(Extension.builder().value(settingContext).url(EXTENSION_BASE_URL - + "/settingContext").build()); + builder.extension(Extension.builder().value(settingContext).url(EXTENSION_BASE_URL + "settingContext").build()); } // User Type if (userType != null) { - builder.extension(Extension.builder().value(userType).url(EXTENSION_BASE_URL - + "/userType").build()); + builder.extension(Extension.builder().value(userType).url(EXTENSION_BASE_URL + "userType").build()); } // User Language diff --git a/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/LookupOperation.java b/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/LookupOperation.java index 7b463775554..d09ba95f189 100644 --- a/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/LookupOperation.java +++ b/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/LookupOperation.java @@ -8,6 +8,7 @@ import static com.ibm.fhir.model.type.String.string; +import com.ibm.fhir.core.FHIRConstants; import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.resource.CodeSystem; import com.ibm.fhir.model.resource.OperationDefinition; @@ -23,7 +24,6 @@ import com.ibm.fhir.registry.FHIRRegistry; import com.ibm.fhir.server.operation.spi.FHIROperationContext; import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.util.FHIRRestHelper; import com.ibm.fhir.term.service.LookupOutcome; import com.ibm.fhir.term.service.LookupParameters; import com.ibm.fhir.term.service.exception.FHIRTermServiceException; @@ -53,7 +53,7 @@ protected Parameters doInvoke( .severity(IssueSeverity.ERROR) .code(IssueType.NOT_FOUND.toBuilder() .extension(Extension.builder() - .url(FHIRRestHelper.EXTENSION_URL + "/not-found-detail") + .url(FHIRConstants.EXT_BASE + "not-found-detail") .value(Code.of("coding")) .build()) .build())