diff --git a/cql/fhir-cql-server/src/main/java/com/ibm/fhir/cql/engine/server/retrieve/ServerFHIRRetrieveProvider.java b/cql/fhir-cql-server/src/main/java/com/ibm/fhir/cql/engine/server/retrieve/ServerFHIRRetrieveProvider.java index 00ad19abede..71876a69fe0 100644 --- a/cql/fhir-cql-server/src/main/java/com/ibm/fhir/cql/engine/server/retrieve/ServerFHIRRetrieveProvider.java +++ b/cql/fhir-cql-server/src/main/java/com/ibm/fhir/cql/engine/server/retrieve/ServerFHIRRetrieveProvider.java @@ -26,7 +26,7 @@ import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.persistence.SingleResourceResult; import com.ibm.fhir.search.SearchConstants; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; /** * This is an implementation of a retrieve provider for the CQL Engine that uses diff --git a/cql/fhir-cql-server/src/main/java/com/ibm/fhir/cql/engine/server/terminology/ServerFHIRTerminologyProvider.java b/cql/fhir-cql-server/src/main/java/com/ibm/fhir/cql/engine/server/terminology/ServerFHIRTerminologyProvider.java index b94eff1eb36..7fca03146b7 100644 --- a/cql/fhir-cql-server/src/main/java/com/ibm/fhir/cql/engine/server/terminology/ServerFHIRTerminologyProvider.java +++ b/cql/fhir-cql-server/src/main/java/com/ibm/fhir/cql/engine/server/terminology/ServerFHIRTerminologyProvider.java @@ -22,7 +22,7 @@ import com.ibm.fhir.model.type.Uri; import com.ibm.fhir.model.type.code.ResourceType; import com.ibm.fhir.persistence.SingleResourceResult; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; import com.ibm.fhir.term.service.FHIRTermService; import com.ibm.fhir.term.service.LookupOutcome; import com.ibm.fhir.term.service.ValidationOutcome; diff --git a/cql/fhir-cql-server/src/test/java/com/ibm/fhir/cql/engine/server/retrieve/ServerFHIRRetrieveProviderTest.java b/cql/fhir-cql-server/src/test/java/com/ibm/fhir/cql/engine/server/retrieve/ServerFHIRRetrieveProviderTest.java index 9be3b4616e0..8a654ecfdd3 100644 --- a/cql/fhir-cql-server/src/test/java/com/ibm/fhir/cql/engine/server/retrieve/ServerFHIRRetrieveProviderTest.java +++ b/cql/fhir-cql-server/src/test/java/com/ibm/fhir/cql/engine/server/retrieve/ServerFHIRRetrieveProviderTest.java @@ -42,7 +42,7 @@ import com.ibm.fhir.model.type.code.BundleType; import com.ibm.fhir.persistence.SingleResourceResult; import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceNotFoundException; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class ServerFHIRRetrieveProviderTest { diff --git a/cql/fhir-cql-server/src/test/java/com/ibm/fhir/cql/engine/server/terminology/ServerFHIRTerminologyProviderTest.java b/cql/fhir-cql-server/src/test/java/com/ibm/fhir/cql/engine/server/terminology/ServerFHIRTerminologyProviderTest.java index fc000391262..9e7dfa2782b 100644 --- a/cql/fhir-cql-server/src/test/java/com/ibm/fhir/cql/engine/server/terminology/ServerFHIRTerminologyProviderTest.java +++ b/cql/fhir-cql-server/src/test/java/com/ibm/fhir/cql/engine/server/terminology/ServerFHIRTerminologyProviderTest.java @@ -36,7 +36,7 @@ import com.ibm.fhir.model.type.Uri; import com.ibm.fhir.model.type.code.PublicationStatus; import com.ibm.fhir.persistence.SingleResourceResult; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; import com.ibm.fhir.term.service.FHIRTermService; import com.ibm.fhir.term.service.LookupOutcome; import com.ibm.fhir.term.service.ValidationOutcome; diff --git a/cql/operation/fhir-operation-apply/pom.xml b/cql/operation/fhir-operation-apply/pom.xml index 84ac7d8c96b..cc727880641 100644 --- a/cql/operation/fhir-operation-apply/pom.xml +++ b/cql/operation/fhir-operation-apply/pom.xml @@ -15,8 +15,15 @@ ${project.groupId} - fhir-server + fhir-server-spi ${project.version} + provided + + + ${project.groupId} + fhir-persistence + ${project.version} + provided jakarta.ws.rs diff --git a/cql/operation/fhir-operation-apply/src/main/java/com/ibm/fhir/operation/apply/ApplyOperation.java b/cql/operation/fhir-operation-apply/src/main/java/com/ibm/fhir/operation/apply/ApplyOperation.java index 28c870decc2..325672d616f 100644 --- a/cql/operation/fhir-operation-apply/src/main/java/com/ibm/fhir/operation/apply/ApplyOperation.java +++ b/cql/operation/fhir-operation-apply/src/main/java/com/ibm/fhir/operation/apply/ApplyOperation.java @@ -45,10 +45,10 @@ import com.ibm.fhir.model.type.code.IssueSeverity; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.registry.FHIRRegistry; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; /** * $apply is an operation specific to PlanDefinition. diff --git a/cql/operation/fhir-operation-apply/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/cql/operation/fhir-operation-apply/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from cql/operation/fhir-operation-apply/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to cql/operation/fhir-operation-apply/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation diff --git a/cql/operation/fhir-operation-cpg/pom.xml b/cql/operation/fhir-operation-cpg/pom.xml index 4a03cecb57c..610a276b20b 100644 --- a/cql/operation/fhir-operation-cpg/pom.xml +++ b/cql/operation/fhir-operation-cpg/pom.xml @@ -28,12 +28,14 @@ com.ibm.fhir fhir-model ${project.version} + provided com.ibm.fhir - fhir-server + fhir-server-spi ${project.version} + provided diff --git a/cql/operation/fhir-operation-cpg/src/main/java/com/ibm/fhir/operation/cpg/AbstractCqlOperation.java b/cql/operation/fhir-operation-cpg/src/main/java/com/ibm/fhir/operation/cpg/AbstractCqlOperation.java index 047aeb07951..ae4db1e949d 100644 --- a/cql/operation/fhir-operation-cpg/src/main/java/com/ibm/fhir/operation/cpg/AbstractCqlOperation.java +++ b/cql/operation/fhir-operation-cpg/src/main/java/com/ibm/fhir/operation/cpg/AbstractCqlOperation.java @@ -46,8 +46,8 @@ import com.ibm.fhir.model.resource.Library; import com.ibm.fhir.model.resource.Parameters; import com.ibm.fhir.model.resource.Parameters.Parameter; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public abstract class AbstractCqlOperation extends AbstractOperation { diff --git a/cql/operation/fhir-operation-cpg/src/main/java/com/ibm/fhir/operation/cpg/CqlOperation.java b/cql/operation/fhir-operation-cpg/src/main/java/com/ibm/fhir/operation/cpg/CqlOperation.java index 5124def33ab..c46ce984e6f 100644 --- a/cql/operation/fhir-operation-cpg/src/main/java/com/ibm/fhir/operation/cpg/CqlOperation.java +++ b/cql/operation/fhir-operation-cpg/src/main/java/com/ibm/fhir/operation/cpg/CqlOperation.java @@ -42,8 +42,8 @@ import com.ibm.fhir.model.type.code.PublicationStatus; import com.ibm.fhir.model.type.code.RelatedArtifactType; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class CqlOperation extends AbstractCqlOperation { diff --git a/cql/operation/fhir-operation-cpg/src/main/java/com/ibm/fhir/operation/cpg/LibraryEvaluateOperation.java b/cql/operation/fhir-operation-cpg/src/main/java/com/ibm/fhir/operation/cpg/LibraryEvaluateOperation.java index e08a078b5b2..505fb46dec3 100644 --- a/cql/operation/fhir-operation-cpg/src/main/java/com/ibm/fhir/operation/cpg/LibraryEvaluateOperation.java +++ b/cql/operation/fhir-operation-cpg/src/main/java/com/ibm/fhir/operation/cpg/LibraryEvaluateOperation.java @@ -31,8 +31,8 @@ import com.ibm.fhir.model.type.code.ResourceType; import com.ibm.fhir.persistence.SingleResourceResult; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class LibraryEvaluateOperation extends AbstractCqlOperation { diff --git a/cql/operation/fhir-operation-cpg/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/cql/operation/fhir-operation-cpg/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from cql/operation/fhir-operation-cpg/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to cql/operation/fhir-operation-cpg/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation diff --git a/cql/operation/fhir-operation-cpg/src/test/java/com/ibm/fhir/operation/cpg/AbstractCqlOperationTest.java b/cql/operation/fhir-operation-cpg/src/test/java/com/ibm/fhir/operation/cpg/AbstractCqlOperationTest.java index b5931723db5..9c7659e050a 100644 --- a/cql/operation/fhir-operation-cpg/src/test/java/com/ibm/fhir/operation/cpg/AbstractCqlOperationTest.java +++ b/cql/operation/fhir-operation-cpg/src/test/java/com/ibm/fhir/operation/cpg/AbstractCqlOperationTest.java @@ -33,8 +33,8 @@ import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.type.code.OperationKind; import com.ibm.fhir.model.type.code.PublicationStatus; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class AbstractCqlOperationTest extends BaseCqlOperationTest { diff --git a/cql/operation/fhir-operation-cpg/src/test/java/com/ibm/fhir/operation/cpg/CqlOperationTest.java b/cql/operation/fhir-operation-cpg/src/test/java/com/ibm/fhir/operation/cpg/CqlOperationTest.java index 3b9abf00224..b0b8e9a6b21 100644 --- a/cql/operation/fhir-operation-cpg/src/test/java/com/ibm/fhir/operation/cpg/CqlOperationTest.java +++ b/cql/operation/fhir-operation-cpg/src/test/java/com/ibm/fhir/operation/cpg/CqlOperationTest.java @@ -10,8 +10,6 @@ import static com.ibm.fhir.cql.helpers.ModelHelper.concept; import static com.ibm.fhir.cql.helpers.ModelHelper.fhirboolean; import static com.ibm.fhir.cql.helpers.ModelHelper.fhirstring; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; @@ -20,12 +18,14 @@ import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.fail; import javax.ws.rs.core.MultivaluedMap; -import org.testng.annotations.Test; import org.mockito.MockedStatic; +import org.testng.annotations.Test; import com.ibm.fhir.cql.Constants; import com.ibm.fhir.cql.helpers.LibraryHelper; @@ -45,8 +45,8 @@ import com.ibm.fhir.model.type.code.EncounterStatus; import com.ibm.fhir.model.type.code.IssueType; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class CqlOperationTest extends BaseCqlOperationTest { @@ -63,8 +63,15 @@ public void testInlineExpression() throws Exception { String encounterCode = "office-visit"; Coding reason = coding(codesystem, encounterCode); - Encounter encounter = - Encounter.builder().reasonCode(concept(reason)).status(EncounterStatus.FINISHED).clazz(reason).period(Period.builder().start(DateTime.now()).end(DateTime.now()).build()).build(); + Encounter encounter = Encounter.builder() + .reasonCode(concept(reason)) + .status(EncounterStatus.FINISHED) + .clazz(reason) + .period(Period.builder() + .start(DateTime.now()) + .end(DateTime.now()) + .build()) + .build(); String expression = "[Encounter] e where e.status = 'finished'"; @@ -85,7 +92,7 @@ public void testInlineExpression() throws Exception { // when(mockRegistry.getResource(javastring(fhirHelpers.getUrl()), Library.class)).thenReturn(fhirHelpers); - FHIROperationContext ctx = FHIROperationContext.createSystemOperationContext(); + FHIROperationContext ctx = FHIROperationContext.createSystemOperationContext(null); Parameters result = getOperation().doInvoke(ctx, null, null, null, parameters, resourceHelper); assertNotNull(result); @@ -136,7 +143,7 @@ public void testInlineExpressionWithIncludes() throws Exception { canonical = ModelHelper.canonical(modelInfo.getUrl(), modelInfo.getVersion()); when(mockRegistry.getResource(canonical.getValue(), Library.class)).thenReturn(modelInfo); - FHIROperationContext ctx = FHIROperationContext.createSystemOperationContext(); + FHIROperationContext ctx = FHIROperationContext.createSystemOperationContext("cql"); getOperation().doInvoke(ctx, null, null, null, parameters, resourceHelper); } } @@ -158,7 +165,7 @@ public void testInlineExpressionInvalid() throws Exception { FHIRRegistry mockRegistry = spy(FHIRRegistry.class); staticRegistry.when(FHIRRegistry::getInstance).thenReturn(mockRegistry); - FHIROperationContext ctx = FHIROperationContext.createSystemOperationContext(); + FHIROperationContext ctx = FHIROperationContext.createSystemOperationContext("cql"); getOperation().doInvoke(ctx, null, null, null, parameters, resourceHelper); fail("Missing expected Exception"); } catch (FHIROperationException fex) { @@ -189,7 +196,7 @@ public void testInlineExpressionWithDebug() throws Exception { FHIRRegistry mockRegistry = spy(FHIRRegistry.class); staticRegistry.when(FHIRRegistry::getInstance).thenReturn(mockRegistry); - FHIROperationContext ctx = FHIROperationContext.createSystemOperationContext(); + FHIROperationContext ctx = FHIROperationContext.createSystemOperationContext("cql"); Parameters result = getOperation().doInvoke(ctx, null, null, null, parameters, resourceHelper); assertNotNull(result); diff --git a/cql/operation/fhir-operation-cpg/src/test/java/com/ibm/fhir/operation/cpg/LibraryEvaluateOperationTest.java b/cql/operation/fhir-operation-cpg/src/test/java/com/ibm/fhir/operation/cpg/LibraryEvaluateOperationTest.java index c9c20726831..b2adf10af72 100644 --- a/cql/operation/fhir-operation-cpg/src/test/java/com/ibm/fhir/operation/cpg/LibraryEvaluateOperationTest.java +++ b/cql/operation/fhir-operation-cpg/src/test/java/com/ibm/fhir/operation/cpg/LibraryEvaluateOperationTest.java @@ -42,7 +42,7 @@ import com.ibm.fhir.model.type.code.EncounterStatus; import com.ibm.fhir.model.type.code.ProcedureStatus; import com.ibm.fhir.registry.FHIRRegistry; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class LibraryEvaluateOperationTest extends BaseCqlOperationTest { diff --git a/cql/operation/fhir-operation-cqf/pom.xml b/cql/operation/fhir-operation-cqf/pom.xml index d7a98d8640c..217f46c9f19 100644 --- a/cql/operation/fhir-operation-cqf/pom.xml +++ b/cql/operation/fhir-operation-cqf/pom.xml @@ -34,12 +34,21 @@ com.ibm.fhir fhir-model ${project.version} + provided + + com.ibm.fhir + fhir-server-spi + ${project.version} + provided + + com.ibm.fhir fhir-search ${project.version} + provided diff --git a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/AbstractDataRequirementsOperation.java b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/AbstractDataRequirementsOperation.java index 3627f05f778..114ebe7b657 100644 --- a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/AbstractDataRequirementsOperation.java +++ b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/AbstractDataRequirementsOperation.java @@ -24,10 +24,10 @@ import com.ibm.fhir.model.type.RelatedArtifact; import com.ibm.fhir.model.type.code.PublicationStatus; import com.ibm.fhir.model.type.code.RelatedArtifactType; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public abstract class AbstractDataRequirementsOperation extends AbstractOperation { public static final String PARAM_OUT_RETURN = "return"; diff --git a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/AbstractMeasureOperation.java b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/AbstractMeasureOperation.java index bf035ec3516..1ed77bb0732 100644 --- a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/AbstractMeasureOperation.java +++ b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/AbstractMeasureOperation.java @@ -45,8 +45,8 @@ import com.ibm.fhir.model.resource.Patient; import com.ibm.fhir.model.type.Date; import com.ibm.fhir.registry.FHIRRegistry; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public abstract class AbstractMeasureOperation extends AbstractOperation { diff --git a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/CareGapsOperation.java b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/CareGapsOperation.java index 3c99f937748..19fb49e30bf 100644 --- a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/CareGapsOperation.java +++ b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/CareGapsOperation.java @@ -34,9 +34,9 @@ import com.ibm.fhir.model.type.code.ResourceType; import com.ibm.fhir.registry.FHIRRegistry; import com.ibm.fhir.search.SearchConstants; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class CareGapsOperation extends AbstractMeasureOperation { diff --git a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/EvaluateMeasureOperation.java b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/EvaluateMeasureOperation.java index f62f915f042..ad15b126bcb 100644 --- a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/EvaluateMeasureOperation.java +++ b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/EvaluateMeasureOperation.java @@ -27,9 +27,9 @@ import com.ibm.fhir.model.type.code.ResourceType; import com.ibm.fhir.persistence.SingleResourceResult; 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.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class EvaluateMeasureOperation extends AbstractMeasureOperation { diff --git a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/LibraryDataRequirementsOperation.java b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/LibraryDataRequirementsOperation.java index abd2deb92d6..e767a7401f4 100644 --- a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/LibraryDataRequirementsOperation.java +++ b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/LibraryDataRequirementsOperation.java @@ -18,8 +18,8 @@ import com.ibm.fhir.model.type.code.ResourceType; import com.ibm.fhir.persistence.SingleResourceResult; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class LibraryDataRequirementsOperation extends AbstractDataRequirementsOperation { diff --git a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/MeasureCollectDataOperation.java b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/MeasureCollectDataOperation.java index fa00e1dee77..2e3e98d26d1 100644 --- a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/MeasureCollectDataOperation.java +++ b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/MeasureCollectDataOperation.java @@ -25,8 +25,8 @@ import com.ibm.fhir.model.util.ModelSupport.ElementInfo; import com.ibm.fhir.persistence.SingleResourceResult; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class MeasureCollectDataOperation extends EvaluateMeasureOperation { diff --git a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/MeasureDataRequirementsOperation.java b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/MeasureDataRequirementsOperation.java index c303c31e7a7..8c6d6da46a3 100644 --- a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/MeasureDataRequirementsOperation.java +++ b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/MeasureDataRequirementsOperation.java @@ -20,8 +20,8 @@ import com.ibm.fhir.model.type.code.ResourceType; import com.ibm.fhir.persistence.SingleResourceResult; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class MeasureDataRequirementsOperation extends AbstractDataRequirementsOperation { diff --git a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/MeasureSubmitDataOperation.java b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/MeasureSubmitDataOperation.java index 0b163c302eb..eed2a1c2c2b 100644 --- a/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/MeasureSubmitDataOperation.java +++ b/cql/operation/fhir-operation-cqf/src/main/java/com/ibm/fhir/operation/cqf/MeasureSubmitDataOperation.java @@ -21,10 +21,10 @@ import com.ibm.fhir.model.type.code.BundleType; import com.ibm.fhir.model.type.code.HTTPVerb; import com.ibm.fhir.registry.FHIRRegistry; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class MeasureSubmitDataOperation extends AbstractOperation { diff --git a/cql/operation/fhir-operation-cqf/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/cql/operation/fhir-operation-cqf/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from cql/operation/fhir-operation-cqf/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to cql/operation/fhir-operation-cqf/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation diff --git a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/BaseDataRequirementsOperationTest.java b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/BaseDataRequirementsOperationTest.java index f2f4b6c2574..9ccd9b2d9fc 100644 --- a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/BaseDataRequirementsOperationTest.java +++ b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/BaseDataRequirementsOperationTest.java @@ -45,8 +45,8 @@ import com.ibm.fhir.model.type.code.PublicationStatus; import com.ibm.fhir.model.type.code.RelatedArtifactType; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public abstract class BaseDataRequirementsOperationTest { diff --git a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/CareGapsOperationTest.java b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/CareGapsOperationTest.java index 9005a4e5abd..5a48fcd268f 100644 --- a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/CareGapsOperationTest.java +++ b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/CareGapsOperationTest.java @@ -11,8 +11,6 @@ import static com.ibm.fhir.cql.helpers.ModelHelper.concept; import static com.ibm.fhir.cql.helpers.ModelHelper.fhirstring; import static com.ibm.fhir.cql.helpers.ModelHelper.valueset; -import static org.testng.Assert.assertNotNull; -import static org.testng.Assert.assertSame; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; @@ -23,14 +21,16 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertSame; import java.time.LocalDate; import java.time.temporal.ChronoUnit; import java.util.List; +import org.mockito.MockedStatic; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import org.mockito.MockedStatic; import com.ibm.fhir.cql.helpers.ParameterMap; import com.ibm.fhir.model.resource.Bundle; @@ -51,8 +51,8 @@ import com.ibm.fhir.model.type.code.ProcedureStatus; import com.ibm.fhir.persistence.SingleResourceResult; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class CareGapsOperationTest { @@ -70,8 +70,15 @@ public void testDoEvaluationEXM74() throws Exception { String codesystem = "http://snomed.ct/info"; String encounterCode = "office-visit"; Coding reason = coding(codesystem, encounterCode); - Encounter encounter = - Encounter.builder().reasonCode(concept(reason)).status(EncounterStatus.FINISHED).clazz(reason).period(Period.builder().start(DateTime.now()).end(DateTime.now()).build()).build(); + Encounter encounter = Encounter.builder() + .reasonCode(concept(reason)) + .status(EncounterStatus.FINISHED) + .clazz(reason) + .period(Period.builder() + .start(DateTime.now()) + .end(DateTime.now()) + .build()) + .build(); String procedureCode = "fluoride-application"; Coding type = coding(codesystem, procedureCode); @@ -116,7 +123,7 @@ public void testDoEvaluationEXM74() throws Exception { fhirLibraries.stream().forEach(l -> when(mockRegistry.getResource(canonical(l.getUrl(), l.getVersion()).getValue(), Library.class)).thenReturn(l)); Parameters result = - operation.doInvoke(FHIROperationContext.createResourceTypeOperationContext(), Measure.class, null, null, parameters, resourceHelper); + operation.doInvoke(FHIROperationContext.createResourceTypeOperationContext("care-gap"), Measure.class, null, null, parameters, resourceHelper); assertNotNull(result); ParameterMap resultMap = new ParameterMap(result); @@ -127,7 +134,7 @@ public void testDoEvaluationEXM74() throws Exception { @SuppressWarnings("unchecked") protected SingleResourceResult asResult(Resource patient) { - SingleResourceResult result = (SingleResourceResult) mock(SingleResourceResult.class); + SingleResourceResult result = mock(SingleResourceResult.class); when(result.isSuccess()).thenReturn(true); when(result.getResource()).thenReturn(patient); return result; diff --git a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/EvaluateMeasureOperationTest.java b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/EvaluateMeasureOperationTest.java index 9aac0d88c79..3276d163c02 100644 --- a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/EvaluateMeasureOperationTest.java +++ b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/EvaluateMeasureOperationTest.java @@ -11,8 +11,6 @@ import static com.ibm.fhir.cql.helpers.ModelHelper.concept; import static com.ibm.fhir.cql.helpers.ModelHelper.fhirstring; import static com.ibm.fhir.cql.helpers.ModelHelper.valueset; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; @@ -21,6 +19,8 @@ import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; import java.time.LocalDate; import java.time.LocalTime; @@ -31,9 +31,9 @@ import java.util.List; import java.util.stream.Collectors; +import org.mockito.MockedStatic; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import org.mockito.MockedStatic; import com.ibm.fhir.cql.helpers.ParameterMap; import com.ibm.fhir.model.resource.Encounter; @@ -53,90 +53,90 @@ import com.ibm.fhir.model.type.code.MeasureReportType; import com.ibm.fhir.model.type.code.ProcedureStatus; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class EvaluateMeasureOperationTest { - + private EvaluateMeasureOperation operation; - + @BeforeMethod public void setup() { operation = new EvaluateMeasureOperation(); } - + @Test public void testDoEvaluationEXM74() throws Exception { Patient patient = (Patient) TestHelper.getTestResource("Patient.json"); String codesystem = "http://snomed.ct/info"; String encounterCode = "office-visit"; - Coding reason = coding(codesystem, encounterCode); + Coding reason = coding(codesystem, encounterCode); Encounter encounter = Encounter.builder() .reasonCode(concept(reason)) .status(EncounterStatus.FINISHED) .clazz(reason) .period(Period.builder().start(DateTime.now()).end(DateTime.now()).build()) .build(); - + String procedureCode = "fluoride-application"; Coding type = coding(codesystem, procedureCode); Procedure procedure = Procedure.builder().subject(Reference.builder().reference(fhirstring("Patient/" + patient.getId())).build()).code(concept(type)).status(ProcedureStatus.COMPLETED).performed(DateTime.of("2019-03-14")).build(); - + List measures = TestHelper.getBundleResources("EXM74-10.2.000-request.json", Measure.class); assertEquals( measures.size(), 1 ); Measure measure = measures.get(0); String measureURL = canonical(measure.getUrl(), measure.getVersion()).getValue(); - + List fhirLibraries = TestHelper.getBundleResources("EXM74-10.2.000-request.json", Library.class); - + List names = fhirLibraries.stream().map( l -> l.getName().getValue()).collect(Collectors.toList()); System.out.println(names); - + LocalDate periodStart = LocalDate.of(2000, 1, 1); LocalDate periodEnd = periodStart.plus(1, ChronoUnit.YEARS); - + Parameters.Parameter pPeriodStart = Parameters.Parameter.builder().name(fhirstring(EvaluateMeasureOperation.PARAM_IN_PERIOD_START)).value(Date.of(periodStart)).build(); Parameters.Parameter pPeriodEnd = Parameters.Parameter.builder().name(fhirstring(EvaluateMeasureOperation.PARAM_IN_PERIOD_END)).value(Date.of(periodEnd)).build(); Parameters.Parameter pReportType = Parameters.Parameter.builder().name(fhirstring(EvaluateMeasureOperation.PARAM_IN_REPORT_TYPE)).value(MeasureReportType.INDIVIDUAL).build(); Parameters.Parameter pMeasure = Parameters.Parameter.builder().name(fhirstring(EvaluateMeasureOperation.PARAM_IN_MEASURE)).value(fhirstring(measureURL)).build(); Parameters.Parameter pSubject = Parameters.Parameter.builder().name(fhirstring(EvaluateMeasureOperation.PARAM_IN_SUBJECT)).value(fhirstring("Patient/" + patient.getId())).build(); - + Parameters parameters = Parameters.builder().parameter(pPeriodStart, pPeriodEnd, pReportType, pMeasure, pSubject).build(); FHIRResourceHelpers resourceHelper = mock(FHIRResourceHelpers.class); when(resourceHelper.doRead(eq("Patient"), eq(patient.getId()), anyBoolean(), anyBoolean(), any())).thenAnswer(x -> TestHelper.asResult(patient)); - + when(resourceHelper.doSearch(eq("Encounter"), anyString(), anyString(), any(), anyString(), any())).thenReturn( bundle(encounter) ); when(resourceHelper.doSearch(eq("Procedure"), anyString(), anyString(), any(), anyString(), any())).thenReturn( bundle(procedure) ); - + try (MockedStatic staticRegistry = mockStatic(FHIRRegistry.class)) { FHIRRegistry mockRegistry = spy(FHIRRegistry.class); staticRegistry.when(FHIRRegistry::getInstance).thenReturn(mockRegistry); - + when(mockRegistry.getResource("http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001", ValueSet.class)).thenReturn( valueset(codesystem, encounterCode) ); when(mockRegistry.getResource("http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.125.12.1002", ValueSet.class)).thenReturn( valueset(codesystem, procedureCode) ); - + when(mockRegistry.getResource(measureURL, Measure.class)).thenReturn( measure ); fhirLibraries.stream().forEach( l -> when(mockRegistry.getResource( canonical(l.getUrl(), l.getVersion()).getValue(), Library.class )).thenReturn(l) ); - - Parameters result = operation.doInvoke(FHIROperationContext.createResourceTypeOperationContext(), + + Parameters result = operation.doInvoke(FHIROperationContext.createResourceTypeOperationContext("evaluate-measure"), Measure.class, null, null, parameters, resourceHelper); assertNotNull(result); - + ParameterMap resultMap = new ParameterMap(result); MeasureReport report = (MeasureReport) resultMap.getSingletonParameter(EvaluateMeasureOperation.PARAM_OUT_RETURN).getResource(); assertEquals( report.getMeasure().getValue(), measureURL ); - + ZoneOffset zoneOffset = OffsetDateTime.now().getOffset(); ZonedDateTime expectedStart = OffsetDateTime.of(periodStart, LocalTime.MIN, zoneOffset).toZonedDateTime(); ZonedDateTime expectedEnd= OffsetDateTime.of(periodEnd, LocalTime.MAX, zoneOffset).toZonedDateTime(); Period expectedPeriod = Period.builder().start(DateTime.of(expectedStart)).end(DateTime.of(expectedEnd)).build(); assertEquals( report.getPeriod(), expectedPeriod ); - + assertEquals( report.getGroup().size(), 1 ); MeasureReport.Group group = report.getGroup().get(0); assertEquals( group.getPopulation().size(), 3 ); diff --git a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/LibraryDataRequirementsOperationTest.java b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/LibraryDataRequirementsOperationTest.java index 1c689b60316..12b0d72a679 100644 --- a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/LibraryDataRequirementsOperationTest.java +++ b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/LibraryDataRequirementsOperationTest.java @@ -11,22 +11,23 @@ import com.ibm.fhir.model.resource.Library; import com.ibm.fhir.model.resource.Parameters; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; public class LibraryDataRequirementsOperationTest extends BaseDataRequirementsOperationTest { - + @Test public void testInstanceLibrary() throws Exception { - runTest( FHIROperationContext.createInstanceOperationContext(), Library.class, primaryLibrary -> primaryLibrary.getId(), primaryLibrary -> null ); + runTest( FHIROperationContext.createInstanceOperationContext("data-requirements"), Library.class, primaryLibrary -> primaryLibrary.getId(), primaryLibrary -> null ); } - + @Test public void testSystemLibrary() throws Exception { - runTest( FHIROperationContext.createSystemOperationContext(), null, primaryLibrary -> null, primaryLibrary -> { + runTest( FHIROperationContext.createSystemOperationContext("data-requirements"), null, primaryLibrary -> null, primaryLibrary -> { return Parameters.builder().parameter( Parameters.Parameter.builder().name(fhirstring("target")).value(fhirstring(primaryLibrary.getId())).build()).build(); }); } - + + @Override public AbstractDataRequirementsOperation getOperation() { return new LibraryDataRequirementsOperation(); } diff --git a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/MeasureCollectDataOperationTest.java b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/MeasureCollectDataOperationTest.java index f28699686b0..ec86d4cadb3 100644 --- a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/MeasureCollectDataOperationTest.java +++ b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/MeasureCollectDataOperationTest.java @@ -12,7 +12,6 @@ import static com.ibm.fhir.cql.helpers.ModelHelper.fhirstring; import static com.ibm.fhir.cql.helpers.ModelHelper.reference; import static com.ibm.fhir.cql.helpers.ModelHelper.valueset; - import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; @@ -53,95 +52,95 @@ import com.ibm.fhir.model.type.code.MeasureReportType; import com.ibm.fhir.model.type.code.ProcedureStatus; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class MeasureCollectDataOperationTest { private MeasureCollectDataOperation operation; - + @BeforeMethod public void setup() { operation = new MeasureCollectDataOperation(); } - + @Test public void testDoEvaluationEXM74() throws Exception { Patient patient = (Patient) TestHelper.getTestResource("Patient.json"); String codesystem = "http://snomed.ct/info"; String encounterCode = "office-visit"; - Coding reason = coding(codesystem, encounterCode); + Coding reason = coding(codesystem, encounterCode); Encounter encounter = Encounter.builder() .reasonCode(concept(reason)) .status(EncounterStatus.FINISHED) .clazz(reason) .period(Period.builder().start(DateTime.now()).end(DateTime.now()).build()) .build(); - + String procedureCode = "fluoride-application"; Coding type = coding(codesystem, procedureCode); Procedure procedure = Procedure.builder().subject(Reference.builder().reference(fhirstring("Patient/" + patient.getId())).build()).code(concept(type)).status(ProcedureStatus.COMPLETED).performed(DateTime.of("2019-03-14")).build(); - + List measures = TestHelper.getBundleResources("EXM74-10.2.000-request.json", Measure.class); assertEquals( measures.size(), 1 ); Measure measure = measures.get(0); String measureURL = canonical(measure.getUrl(), measure.getVersion()).getValue(); - + List fhirLibraries = TestHelper.getBundleResources("EXM74-10.2.000-request.json", Library.class); - + List names = fhirLibraries.stream().map( l -> l.getName().getValue()).collect(Collectors.toList()); System.out.println(names); - + LocalDate periodStart = LocalDate.of(2000, 1, 1); LocalDate periodEnd = periodStart.plus(1, ChronoUnit.YEARS); - + Parameters.Parameter pPeriodStart = Parameters.Parameter.builder().name(fhirstring(MeasureCollectDataOperation.PARAM_IN_PERIOD_START)).value(Date.of(periodStart)).build(); Parameters.Parameter pPeriodEnd = Parameters.Parameter.builder().name(fhirstring(MeasureCollectDataOperation.PARAM_IN_PERIOD_END)).value(Date.of(periodEnd)).build(); Parameters.Parameter pReportType = Parameters.Parameter.builder().name(fhirstring(MeasureCollectDataOperation.PARAM_IN_REPORT_TYPE)).value(MeasureReportType.INDIVIDUAL).build(); Parameters.Parameter pMeasure = Parameters.Parameter.builder().name(fhirstring(MeasureCollectDataOperation.PARAM_IN_MEASURE)).value(fhirstring(measureURL)).build(); Parameters.Parameter pSubject = Parameters.Parameter.builder().name(fhirstring(MeasureCollectDataOperation.PARAM_IN_SUBJECT)).value(fhirstring("Patient/" + patient.getId())).build(); - + Parameters parameters = Parameters.builder().parameter(pPeriodStart, pPeriodEnd, pReportType, pMeasure, pSubject).build(); FHIRResourceHelpers resourceHelper = mock(FHIRResourceHelpers.class); when(resourceHelper.doRead(eq("Patient"), eq(patient.getId()), anyBoolean(), anyBoolean(), any())).thenAnswer(x -> TestHelper.asResult(patient)); - + when(resourceHelper.doSearch(eq("Encounter"), anyString(), anyString(), any(), anyString(), any())).thenReturn( bundle(encounter) ); when(resourceHelper.doSearch(eq("Procedure"), anyString(), anyString(), any(), anyString(), any())).thenReturn( bundle(procedure) ); - + try (MockedStatic staticRegistry = mockStatic(FHIRRegistry.class)) { FHIRRegistry mockRegistry = spy(FHIRRegistry.class); staticRegistry.when(FHIRRegistry::getInstance).thenReturn(mockRegistry); - + when(mockRegistry.getResource("http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001", ValueSet.class)).thenReturn( valueset(codesystem, encounterCode) ); when(mockRegistry.getResource("http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.125.12.1002", ValueSet.class)).thenReturn( valueset(codesystem, procedureCode) ); - + when(mockRegistry.getResource(measureURL, Measure.class)).thenReturn( measure ); fhirLibraries.stream().forEach( l -> when(mockRegistry.getResource( canonical(l.getUrl(), l.getVersion()).getValue(), Library.class )).thenReturn(l) ); - - Parameters result = operation.doInvoke(FHIROperationContext.createResourceTypeOperationContext(), + + Parameters result = operation.doInvoke(FHIROperationContext.createResourceTypeOperationContext("collect-data"), Measure.class, null, null, parameters, resourceHelper); assertNotNull(result); - + ParameterMap resultMap = new ParameterMap(result); MeasureReport report = (MeasureReport) resultMap.getSingletonParameter(MeasureCollectDataOperation.PARAM_OUT_MEASURE_REPORT).getResource(); assertEquals( report.getMeasure().getValue(), measureURL ); - + List resources = resultMap.getParameter(MeasureCollectDataOperation.PARAM_OUT_RESOURCE); assertEquals(resources.size(), 0); } } - + @Test public void testResolveReferences() throws Exception { Patient patient = (Patient) TestHelper.getTestResource("Patient.json"); - + String codesystem = "http://snomed.ct/info"; String encounterCode = "office-visit"; Coding reason = coding(codesystem, encounterCode); - + Encounter encounter = Encounter.builder() .reasonCode(concept(reason)) .status(EncounterStatus.FINISHED) @@ -149,13 +148,13 @@ public void testResolveReferences() throws Exception { .period(Period.builder().start(DateTime.now()).end(DateTime.now()).build()) .subject( reference(patient) ) .build(); - + FHIRResourceHelpers resourceHelper = mock(FHIRResourceHelpers.class); when(resourceHelper.doRead(eq("Patient"), eq(patient.getId()), anyBoolean(), anyBoolean(), any())).thenAnswer(x -> TestHelper.asResult(patient)); - + Parameters.Builder builder = Parameters.builder(); operation.resolveReferences(encounter, builder, new HashMap<>(), resourceHelper); - + Parameters parameters = builder.build(); assertEquals(parameters.getParameter().size(), 1); } diff --git a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/MeasureDataRequirementsOperationTest.java b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/MeasureDataRequirementsOperationTest.java index a62d6ecad59..3fb9633e5a5 100644 --- a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/MeasureDataRequirementsOperationTest.java +++ b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/MeasureDataRequirementsOperationTest.java @@ -10,12 +10,12 @@ import static com.ibm.fhir.cql.helpers.ModelHelper.fhircode; import static com.ibm.fhir.cql.helpers.ModelHelper.fhirstring; import static com.ibm.fhir.cql.helpers.ModelHelper.fhiruri; -import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -27,21 +27,21 @@ import com.ibm.fhir.model.type.Expression; import com.ibm.fhir.model.type.code.PublicationStatus; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class MeasureDataRequirementsOperationTest extends BaseDataRequirementsOperationTest { private Measure measure; - @Override + @Override public AbstractDataRequirementsOperation getOperation() { return new MeasureDataRequirementsOperation(); } - + @BeforeMethod public void setup() { - + measure = Measure.builder() .id("Test-1.0.0") .name(fhirstring("Test")) @@ -60,32 +60,33 @@ public void setup() { .build()) .build(); } - + @Test public void testInstanceExecution() throws Exception { String measureId = measure.getId(); - + Parameters inParams = Parameters.builder() .parameter(Parameters.Parameter.builder().name(fhirstring("periodStart")).value(Date.of("2000-01-01")).build()) .parameter(Parameters.Parameter.builder().name(fhirstring("periodEnd")).value(Date.of("2001-01-01")).build()) .build(); - - Parameters outParams = runTest(FHIROperationContext.createInstanceOperationContext(), Measure.class, primaryLibrary -> measureId, primaryLibrary -> inParams); + + Parameters outParams = runTest(FHIROperationContext.createInstanceOperationContext("data-requirements"), + Measure.class, primaryLibrary -> measureId, primaryLibrary -> inParams); assertNotNull(outParams); - + Library moduleDefinition = (Library) outParams.getParameter().get(0).getResource(); assertEquals(moduleDefinition.getRelatedArtifact().stream().filter( ra -> ra.getResource().getValue().equals(measure.getLibrary().get(0).getValue()) ).count(), 1); } - + @Override protected Library initializeLibraries(FHIRRegistry mockRegistry, FHIRResourceHelpers resourceHelper) throws Exception { Library primaryLibrary = super.initializeLibraries(mockRegistry, resourceHelper); - + measure = measure.toBuilder().library( canonical(primaryLibrary) ).build(); - + when(resourceHelper.doRead(eq("Measure"), eq(measure.getId()), anyBoolean(), anyBoolean(), any())).thenAnswer(x -> TestHelper.asResult(measure)); when(mockRegistry.getResource( canonical(measure.getUrl(), measure.getVersion()).getValue(), Measure.class )).thenReturn(measure); - + return primaryLibrary; } } diff --git a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/MeasureSubmitDataOperationTest.java b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/MeasureSubmitDataOperationTest.java index 7378289f7ef..7d882981bb0 100644 --- a/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/MeasureSubmitDataOperationTest.java +++ b/cql/operation/fhir-operation-cqf/src/test/java/com/ibm/fhir/operation/cqf/MeasureSubmitDataOperationTest.java @@ -5,22 +5,25 @@ */ package com.ibm.fhir.operation.cqf; -import static com.ibm.fhir.cql.helpers.ModelHelper.*; +import static com.ibm.fhir.cql.helpers.ModelHelper.coding; import static com.ibm.fhir.cql.helpers.ModelHelper.fhirstring; import static com.ibm.fhir.cql.helpers.ModelHelper.reference; -import static org.testng.Assert.assertNotNull; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.testng.Assert.assertNotNull; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.UUID; +import org.mockito.MockedStatic; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import org.mockito.MockedStatic; import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.resource.Bundle; @@ -35,54 +38,54 @@ import com.ibm.fhir.model.type.code.BundleType; import com.ibm.fhir.model.type.code.EncounterStatus; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class MeasureSubmitDataOperationTest { MeasureSubmitDataOperation op; - + @BeforeMethod public void setup() { op = new MeasureSubmitDataOperation(); } - + @Test public void testInstanceExecutionWithResources() throws Exception { - + MeasureReport.Builder builder = MeasureReport.builder() .id("measure-report-1"); builder.setValidating(false); - + Patient p = Patient.builder() .id(UUID.randomUUID().toString()) .name(HumanName.builder().family(fhirstring("Doe")).given(fhirstring("John")).build()) .build(); - + Encounter e = Encounter.builder() //.id(UUID.randomUUID().toString()) .status(EncounterStatus.FINISHED) .subject( reference( p ) ) .clazz( coding("wellness") ) .build(); - + MeasureReport report = builder.build(); List resources = Arrays.asList(p,e); - + runTest(report, resources); } - + @Test public void testInstanceExecutionWithoutResources() throws Exception { - + MeasureReport.Builder builder = MeasureReport.builder() .id("measure-report-1"); builder.setValidating(false); - + MeasureReport report = builder.build(); List resources = Collections.emptyList(); - + runTest(report, resources); - } + } protected Parameters runTest(MeasureReport report, List resources) throws Exception, FHIROperationException { Parameters inParams = createInParameters(report, resources); @@ -90,15 +93,16 @@ protected Parameters runTest(MeasureReport report, List resources) thr .type(BundleType.COLLECTION) .total(UnsignedInt.of(0)) .build(); - + try (MockedStatic staticRegistry = mockStatic(FHIRRegistry.class)) { FHIRRegistry mockRegistry = spy(FHIRRegistry.class); staticRegistry.when(FHIRRegistry::getInstance).thenReturn(mockRegistry); - + FHIRResourceHelpers resourceHelper = mock(FHIRResourceHelpers.class); when( resourceHelper.doBundle(any(Bundle.class), anyBoolean())).thenReturn(responseBundle); - - Parameters outParams = op.doInvoke(FHIROperationContext.createInstanceOperationContext(), null, null, null, inParams, resourceHelper); + + Parameters outParams = op.doInvoke(FHIROperationContext.createInstanceOperationContext("submit-data"), + null, null, null, inParams, resourceHelper); assertNotNull(outParams); return outParams; } diff --git a/fhir-bulkdata-webapp/src/main/java/com/ibm/fhir/bulkdata/jbatch/load/ChunkWriter.java b/fhir-bulkdata-webapp/src/main/java/com/ibm/fhir/bulkdata/jbatch/load/ChunkWriter.java index cf47c5000f4..1e6e3b58c82 100644 --- a/fhir-bulkdata-webapp/src/main/java/com/ibm/fhir/bulkdata/jbatch/load/ChunkWriter.java +++ b/fhir-bulkdata-webapp/src/main/java/com/ibm/fhir/bulkdata/jbatch/load/ChunkWriter.java @@ -56,10 +56,10 @@ import com.ibm.fhir.persistence.FHIRPersistence; import com.ibm.fhir.persistence.context.FHIRPersistenceContext; import com.ibm.fhir.persistence.context.FHIRPersistenceContextFactory; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.persistence.exception.FHIRPersistenceException; import com.ibm.fhir.persistence.helper.FHIRPersistenceHelper; import com.ibm.fhir.persistence.helper.FHIRTransactionHelper; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.persistence.payload.PayloadPersistenceHelper; import com.ibm.fhir.persistence.util.FHIRPersistenceUtil; import com.ibm.fhir.validation.exception.FHIRValidationException; @@ -301,18 +301,18 @@ public void writeItems(List arg0) throws Exception { * @throws FHIRPersistenceException */ public OperationOutcome conditionalFingerprintUpdate(ImportTransientUserData chunkData, boolean skip, Map localCache, FHIRPersistence persistence, FHIRPersistenceContext context, String logicalId, Resource resource) throws FHIRPersistenceException { - + // Since issue 1869, we must perform the read at this point in order to obtain the - // latest version id. The persistence layer no longer makes any changes to the + // latest version id. The persistence layer no longer makes any changes to the // resource so the resource needs to be fully prepared before the persistence // create/update call is made final Resource oldResource = persistence.read(context, resource.getClass(), logicalId).getResource(); - + final com.ibm.fhir.model.type.Instant lastUpdated = PayloadPersistenceHelper.getCurrentInstant(); final int newVersionNumber = oldResource != null && oldResource.getMeta() != null && oldResource.getMeta().getVersionId() != null ? Integer.parseInt(oldResource.getMeta().getVersionId().getValue()) + 1 : 1; resource = FHIRPersistenceUtil.copyAndSetResourceMetaFields(resource, logicalId, newVersionNumber, lastUpdated); - + OperationOutcome oo; if (skip) { // Key is scoped to the ResourceType. @@ -361,4 +361,4 @@ public OperationOutcome conditionalFingerprintUpdate(ImportTransientUserData chu } return oo; } -} \ No newline at end of file +} diff --git a/fhir-notification/src/main/java/com/ibm/fhir/notification/FHIRNotificationService.java b/fhir-notification/src/main/java/com/ibm/fhir/notification/FHIRNotificationService.java index fe82ac47d89..974ee63aa21 100644 --- a/fhir-notification/src/main/java/com/ibm/fhir/notification/FHIRNotificationService.java +++ b/fhir-notification/src/main/java/com/ibm/fhir/notification/FHIRNotificationService.java @@ -18,7 +18,7 @@ import com.ibm.fhir.config.FHIRRequestContext; import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.notification.exception.FHIRNotificationException; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptor; import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptorException; import com.ibm.fhir.persistence.interceptor.impl.FHIRPersistenceInterceptorMgr; diff --git a/fhir-parent/pom.xml b/fhir-parent/pom.xml index dec1b92aded..999d1d3359d 100644 --- a/fhir-parent/pom.xml +++ b/fhir-parent/pom.xml @@ -95,6 +95,7 @@ ../notification/fhir-notification-kafka ../notification/fhir-notification-nats ../notification/fhir-notification-websocket + ../fhir-server-spi ../fhir-server ../fhir-server-webapp ../fhir-server-test diff --git a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/FHIRPersistenceContext.java b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/FHIRPersistenceContext.java index 326d39bc74a..1ba61b8e649 100644 --- a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/FHIRPersistenceContext.java +++ b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/FHIRPersistenceContext.java @@ -6,7 +6,6 @@ package com.ibm.fhir.persistence.context; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.search.context.FHIRSearchContext; /** diff --git a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/FHIRPersistenceContextFactory.java b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/FHIRPersistenceContextFactory.java index 7a3462ac795..5764fe9a85b 100644 --- a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/FHIRPersistenceContextFactory.java +++ b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/FHIRPersistenceContextFactory.java @@ -13,7 +13,6 @@ import com.ibm.fhir.core.FHIRConstants; import com.ibm.fhir.persistence.context.impl.FHIRHistoryContextImpl; import com.ibm.fhir.persistence.context.impl.FHIRPersistenceContextImpl; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.search.context.FHIRSearchContext; /** diff --git a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/FHIRPersistenceEvent.java b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/FHIRPersistenceEvent.java similarity index 99% rename from fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/FHIRPersistenceEvent.java rename to fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/FHIRPersistenceEvent.java index 94203fd6edb..662f9bb46e9 100644 --- a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/FHIRPersistenceEvent.java +++ b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/FHIRPersistenceEvent.java @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package com.ibm.fhir.persistence.interceptor; +package com.ibm.fhir.persistence.context; import java.util.HashMap; import java.util.Map; diff --git a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/impl/FHIRPersistenceContextImpl.java b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/impl/FHIRPersistenceContextImpl.java index ac464b23a08..6be0d59d38b 100644 --- a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/impl/FHIRPersistenceContextImpl.java +++ b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/context/impl/FHIRPersistenceContextImpl.java @@ -8,7 +8,7 @@ import com.ibm.fhir.persistence.context.FHIRHistoryContext; import com.ibm.fhir.persistence.context.FHIRPersistenceContext; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.search.context.FHIRSearchContext; /** diff --git a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/FHIRPersistenceInterceptor.java b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/FHIRPersistenceInterceptor.java index 60d5e741298..ad8f3821a24 100644 --- a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/FHIRPersistenceInterceptor.java +++ b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/FHIRPersistenceInterceptor.java @@ -6,18 +6,22 @@ package com.ibm.fhir.persistence.interceptor; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; + /** * This interface describes a persistence interceptor. Persistence interceptors are invoked by the FHIR Server to allow * users to inject business logic into the REST API processing flow. To make use of this interceptor, develop a class * that implements this interface, then store your implementation class name in a file called * META-INF/services/com.ibm.fhir.persistence.FHIRPersistenceInterceptor within your jar file. + * @deprecated */ +@Deprecated public interface FHIRPersistenceInterceptor { /** * This method is called during the processing of a 'create' REST API invocation, immediately before the new * resource is stored by the persistence layer. - * + * * @param event * information about the 'create' event * @throws FHIRPersistenceInterceptorException @@ -28,7 +32,7 @@ default void beforeCreate(FHIRPersistenceEvent event) throws FHIRPersistenceInte /** * This method is called during the processing of a 'create' REST API invocation, immediately after the new resource * has been stored by the persistence layer. - * + * * @param event * information about the 'create' event * @throws FHIRPersistenceInterceptorException @@ -39,7 +43,7 @@ default void afterCreate(FHIRPersistenceEvent event) throws FHIRPersistenceInter /** * This method is called during the processing of an 'update' REST API invocation, immediately before the updated * resource is stored by the persistence layer. - * + * * @param event * information about the 'update' event * @throws FHIRPersistenceInterceptorException @@ -50,18 +54,18 @@ default void beforeUpdate(FHIRPersistenceEvent event) throws FHIRPersistenceInte /** * This method is called during the processing of an 'update' REST API invocation, immediately after the updated * resource has been stored by the persistence layer. - * + * * @param event * information about the 'update' event * @throws FHIRPersistenceInterceptorException */ default void afterUpdate(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { } - + /** * This method is called during the processing of an 'patch' REST API invocation, immediately before the updated * resource is stored by the persistence layer. - * + * * @param event * information about the 'patch' event * @throws FHIRPersistenceInterceptorException @@ -72,7 +76,7 @@ default void beforePatch(FHIRPersistenceEvent event) throws FHIRPersistenceInter /** * This method is called during the processing of an 'patch' REST API invocation, immediately after the updated * resource has been stored by the persistence layer. - * + * * @param event * information about the 'patch' event * @throws FHIRPersistenceInterceptorException @@ -81,9 +85,9 @@ default void afterPatch(FHIRPersistenceEvent event) throws FHIRPersistenceInterc } /** - * This method is called during the processing of a 'delete' REST API invocation, immediately before the + * This method is called during the processing of a 'delete' REST API invocation, immediately before the * resource is deleted by the persistence layer. - * + * * @param event * information about the 'delete' event * @throws FHIRPersistenceInterceptorException @@ -94,18 +98,18 @@ default void beforeDelete(FHIRPersistenceEvent event) throws FHIRPersistenceInte /** * This method is called during the processing of a 'delete' REST API invocation, immediately after the * resource has been deleted by the persistence layer. - * + * * @param event * information about the 'delete' event * @throws FHIRPersistenceInterceptorException */ default void afterDelete(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { } - + /** * This method is called during the processing of a 'read' REST API invocation, immediately before the resource is * read by the persistence layer. - * + * * @param event * information about the 'read' event * @throws FHIRPersistenceInterceptorException @@ -116,7 +120,7 @@ default void beforeRead(FHIRPersistenceEvent event) throws FHIRPersistenceInterc /** * This method is called during the processing of a 'read' REST API invocation, immediately after the resource has * been read by the persistence layer. - * + * * @param event * information about the 'read' event * @throws FHIRPersistenceInterceptorException @@ -127,7 +131,7 @@ default void afterRead(FHIRPersistenceEvent event) throws FHIRPersistenceInterce /** * This method is called during the processing of a 'vread' (versioned read) REST API invocation, immediately before * the resource is read by the persistence layer. - * + * * @param event * information about the 'vread' event * @throws FHIRPersistenceInterceptorException @@ -138,7 +142,7 @@ default void beforeVread(FHIRPersistenceEvent event) throws FHIRPersistenceInter /** * This method is called during the processing of a 'vread' REST API invocation, immediately after the resource has * been read by the persistence layer. - * + * * @param event * information about the 'vread' event * @throws FHIRPersistenceInterceptorException @@ -149,7 +153,7 @@ default void afterVread(FHIRPersistenceEvent event) throws FHIRPersistenceInterc /** * This method is called during the processing of a 'history' REST API invocation, immediately before the resource's * history is read by the persistence layer. - * + * * @param event * information about the 'history' event * @throws FHIRPersistenceInterceptorException @@ -160,7 +164,7 @@ default void beforeHistory(FHIRPersistenceEvent event) throws FHIRPersistenceInt /** * This method is called during the processing of a 'history' REST API invocation, immediately after the resource's * history has been read by the persistence layer. - * + * * @param event * information about the 'history' event * @throws FHIRPersistenceInterceptorException @@ -171,7 +175,7 @@ default void afterHistory(FHIRPersistenceEvent event) throws FHIRPersistenceInte /** * This method is called during the processing of a 'search' REST API invocation, immediately before the search is * performed by the persistence layer. - * + * * @param event * information about the 'search' event * @throws FHIRPersistenceInterceptorException @@ -182,7 +186,7 @@ default void beforeSearch(FHIRPersistenceEvent event) throws FHIRPersistenceInte /** * This method is called during the processing of a 'search' REST API invocation, immediately after the search has * been performed by the persistence layer. - * + * * @param event * information about the 'search' event * @throws FHIRPersistenceInterceptorException diff --git a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/FHIRPersistenceInterceptorException.java b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/FHIRPersistenceInterceptorException.java index 35985c2989f..0d45a4bfff3 100644 --- a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/FHIRPersistenceInterceptorException.java +++ b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/FHIRPersistenceInterceptorException.java @@ -11,6 +11,10 @@ import com.ibm.fhir.model.resource.OperationOutcome; import com.ibm.fhir.persistence.exception.FHIRPersistenceException; +/** + * @deprecated moved to com.ibm.fhir.server.interceptor in fhir-server + */ +@Deprecated public class FHIRPersistenceInterceptorException extends FHIRPersistenceException { private static final long serialVersionUID = 1L; @@ -27,11 +31,11 @@ public FHIRPersistenceInterceptorException withIssue(OperationOutcome.Issue... i super.withIssue(issues); return this; } - + @Override public FHIRPersistenceInterceptorException withIssue(Collection issues) { super.withIssue(issues); return this; } - + } diff --git a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/impl/FHIRPersistenceInterceptorMgr.java b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/impl/FHIRPersistenceInterceptorMgr.java index 32bb8007b22..13605d528ea 100644 --- a/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/impl/FHIRPersistenceInterceptorMgr.java +++ b/fhir-persistence/src/main/java/com/ibm/fhir/persistence/interceptor/impl/FHIRPersistenceInterceptorMgr.java @@ -14,7 +14,7 @@ import java.util.logging.Logger; import com.ibm.fhir.core.FHIRUtilities; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptor; import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptorException; @@ -28,7 +28,9 @@ * and then insert your implementation class name into a file called * META-INF/services/com.ibm.fhir.persistence.FHIRPersistenceInterceptor and store that file in your jar. * These "interceptor" jars should be stored in a common place defined by the FHIR Server. + * @deprecated moved to com.ibm.fhir.server.interceptor in fhir-server */ +@Deprecated public class FHIRPersistenceInterceptorMgr { private static final Logger log = Logger.getLogger(FHIRPersistenceInterceptorMgr.class.getName()); diff --git a/fhir-persistence/src/test/java/com/ibm/fhir/persistence/test/FHIRPersistenceContextTest.java b/fhir-persistence/src/test/java/com/ibm/fhir/persistence/test/FHIRPersistenceContextTest.java index eabf20109b3..f6c9bc0c01b 100644 --- a/fhir-persistence/src/test/java/com/ibm/fhir/persistence/test/FHIRPersistenceContextTest.java +++ b/fhir-persistence/src/test/java/com/ibm/fhir/persistence/test/FHIRPersistenceContextTest.java @@ -17,8 +17,8 @@ import com.ibm.fhir.persistence.context.FHIRHistoryContext; import com.ibm.fhir.persistence.context.FHIRPersistenceContext; import com.ibm.fhir.persistence.context.FHIRPersistenceContextFactory; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.persistence.context.impl.FHIRPersistenceContextImpl; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.search.context.FHIRSearchContext; import com.ibm.fhir.search.context.FHIRSearchContextFactory; diff --git a/fhir-persistence/src/test/java/com/ibm/fhir/persistence/test/FHIRPersistenceEventTest.java b/fhir-persistence/src/test/java/com/ibm/fhir/persistence/test/FHIRPersistenceEventTest.java index 16da9c21216..559572dc408 100644 --- a/fhir-persistence/src/test/java/com/ibm/fhir/persistence/test/FHIRPersistenceEventTest.java +++ b/fhir-persistence/src/test/java/com/ibm/fhir/persistence/test/FHIRPersistenceEventTest.java @@ -18,7 +18,7 @@ import com.ibm.fhir.model.resource.Patient; import com.ibm.fhir.model.test.TestUtil; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; /** * Tests associated with the FHIRPersistenceContextImpl class. diff --git a/fhir-server-spi/.gitignore b/fhir-server-spi/.gitignore new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fhir-server-spi/pom.xml b/fhir-server-spi/pom.xml new file mode 100644 index 00000000000..8a678b3f4a2 --- /dev/null +++ b/fhir-server-spi/pom.xml @@ -0,0 +1,35 @@ + + 4.0.0 + + + com.ibm.fhir + fhir-parent + 4.10.0-SNAPSHOT + ../fhir-parent + + + fhir-server-spi + + + + jakarta.ws.rs + jakarta.ws.rs-api + provided + + + ${project.groupId} + fhir-model + ${project.version} + provided + + + ${project.groupId} + fhir-persistence + ${project.version} + provided + + + + diff --git a/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/interceptor/FHIRPersistenceInterceptor.java b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/interceptor/FHIRPersistenceInterceptor.java new file mode 100644 index 00000000000..bd69c0a9696 --- /dev/null +++ b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/interceptor/FHIRPersistenceInterceptor.java @@ -0,0 +1,216 @@ +/* + * (C) Copyright IBM Corp. 2016, 2021 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.server.spi.interceptor; + +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; + +/** + * This interface describes a persistence interceptor. Persistence interceptors are invoked by the FHIR Server to allow + * users to inject business logic into the REST API processing flow. To make use of this interceptor, develop a class + * that implements this interface, then store your implementation class name in a file called + * META-INF/services/com.ibm.fhir.persistence.FHIRPersistenceInterceptor within your jar file. + */ +public interface FHIRPersistenceInterceptor { + /** + * This method is called during the processing of a 'create' REST API invocation, immediately before the new + * resource is stored by the persistence layer. + * + * @param event + * information about the 'create' event + * @throws FHIRPersistenceInterceptorException + */ + default void beforeCreate(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of a 'create' REST API invocation, immediately after the new resource + * has been stored by the persistence layer. + * + * @param event + * information about the 'create' event + * @throws FHIRPersistenceInterceptorException + */ + default void afterCreate(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of an 'update' REST API invocation, immediately before the updated + * resource is stored by the persistence layer. + * + * @param event + * information about the 'update' event + * @throws FHIRPersistenceInterceptorException + */ + default void beforeUpdate(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of an 'update' REST API invocation, immediately after the updated + * resource has been stored by the persistence layer. + * + * @param event + * information about the 'update' event + * @throws FHIRPersistenceInterceptorException + */ + default void afterUpdate(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of an 'patch' REST API invocation, immediately before the updated + * resource is stored by the persistence layer. + * + * @param event + * information about the 'patch' event + * @throws FHIRPersistenceInterceptorException + */ + default void beforePatch(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of an 'patch' REST API invocation, immediately after the updated + * resource has been stored by the persistence layer. + * + * @param event + * information about the 'patch' event + * @throws FHIRPersistenceInterceptorException + */ + default void afterPatch(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of a 'delete' REST API invocation, immediately before the + * resource is deleted by the persistence layer. + * + * @param event + * information about the 'delete' event + * @throws FHIRPersistenceInterceptorException + */ + default void beforeDelete(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of a 'delete' REST API invocation, immediately after the + * resource has been deleted by the persistence layer. + * + * @param event + * information about the 'delete' event + * @throws FHIRPersistenceInterceptorException + */ + default void afterDelete(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of a 'read' REST API invocation, immediately before the resource is + * read by the persistence layer. + * + * @param event + * information about the 'read' event + * @throws FHIRPersistenceInterceptorException + */ + default void beforeRead(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of a 'read' REST API invocation, immediately after the resource has + * been read by the persistence layer. + * + * @param event + * information about the 'read' event + * @throws FHIRPersistenceInterceptorException + */ + default void afterRead(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of a 'vread' (versioned read) REST API invocation, immediately before + * the resource is read by the persistence layer. + * + * @param event + * information about the 'vread' event + * @throws FHIRPersistenceInterceptorException + */ + default void beforeVread(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of a 'vread' REST API invocation, immediately after the resource has + * been read by the persistence layer. + * + * @param event + * information about the 'vread' event + * @throws FHIRPersistenceInterceptorException + */ + default void afterVread(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of a 'history' REST API invocation, immediately before the resource's + * history is read by the persistence layer. + * + * @param event + * information about the 'history' event + * @throws FHIRPersistenceInterceptorException + */ + default void beforeHistory(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of a 'history' REST API invocation, immediately after the resource's + * history has been read by the persistence layer. + * + * @param event + * information about the 'history' event + * @throws FHIRPersistenceInterceptorException + */ + default void afterHistory(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of a 'search' REST API invocation, immediately before the search is + * performed by the persistence layer. + * + * @param event + * information about the 'search' event + * @throws FHIRPersistenceInterceptorException + */ + default void beforeSearch(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the processing of a 'search' REST API invocation, immediately after the search has + * been performed by the persistence layer. + * + * @param event + * information about the 'search' event + * @throws FHIRPersistenceInterceptorException + */ + default void afterSearch(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the invocation of a 'custom operation', immediately before the operation logic + * is executed. + * + * @param context + * information about the 'invoke' event + * @throws FHIRPersistenceInterceptorException + */ + default void beforeInvoke(FHIROperationContext event) throws FHIRPersistenceInterceptorException { + } + + /** + * This method is called during the invocation of a 'custom operation', immediately after the operation logic + * is executed. + * + * @param context + * information about the 'invoke' event + * @throws FHIRPersistenceInterceptorException + */ + default void afterInvoke(FHIROperationContext event) throws FHIRPersistenceInterceptorException { + } +} diff --git a/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/interceptor/FHIRPersistenceInterceptorException.java b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/interceptor/FHIRPersistenceInterceptorException.java new file mode 100644 index 00000000000..cee2afc7062 --- /dev/null +++ b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/interceptor/FHIRPersistenceInterceptorException.java @@ -0,0 +1,37 @@ +/* + * (C) Copyright IBM Corp. 2016, 2021 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.server.spi.interceptor; + +import java.util.Collection; + +import com.ibm.fhir.model.resource.OperationOutcome; +import com.ibm.fhir.persistence.exception.FHIRPersistenceException; + +public class FHIRPersistenceInterceptorException extends FHIRPersistenceException { + private static final long serialVersionUID = 1L; + + public FHIRPersistenceInterceptorException(String message) { + super(message); + } + + public FHIRPersistenceInterceptorException(String message, Throwable cause) { + super(message, cause); + } + + @Override + public FHIRPersistenceInterceptorException withIssue(OperationOutcome.Issue... issues) { + super.withIssue(issues); + return this; + } + + @Override + public FHIRPersistenceInterceptorException withIssue(Collection issues) { + super.withIssue(issues); + return this; + } + +} diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/AbstractOperation.java b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/AbstractOperation.java similarity index 99% rename from fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/AbstractOperation.java rename to fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/AbstractOperation.java index af7708efcc2..366d1823464 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/AbstractOperation.java +++ b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/AbstractOperation.java @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package com.ibm.fhir.server.operation.spi; +package com.ibm.fhir.server.spi.operation; import java.util.ArrayList; import java.util.List; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/FHIROperation.java b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIROperation.java similarity index 87% rename from fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/FHIROperation.java rename to fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIROperation.java index b3318b9e7ad..e4b35101dbb 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/FHIROperation.java +++ b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIROperation.java @@ -1,10 +1,10 @@ /* - * (C) Copyright IBM Corp. 2016, 2020 + * (C) Copyright IBM Corp. 2016, 2021 * * SPDX-License-Identifier: Apache-2.0 */ -package com.ibm.fhir.server.operation.spi; +package com.ibm.fhir.server.spi.operation; import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.resource.OperationDefinition; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/FHIROperationContext.java b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIROperationContext.java similarity index 83% rename from fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/FHIROperationContext.java rename to fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIROperationContext.java index e90741d5c8b..fe77fa78dd3 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/FHIROperationContext.java +++ b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIROperationContext.java @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package com.ibm.fhir.server.operation.spi; +package com.ibm.fhir.server.spi.operation; import java.util.HashMap; import java.util.Map; @@ -74,14 +74,26 @@ public enum Type { SYSTEM, RESOURCE_TYPE, INSTANCE } */ public static final String PROPNAME_HTTP_REQUEST = "HTTP_REQUEST"; - private Type type = null; + /** + * The request parameters for this invocation + */ + public static final String PROPNAME_REQUEST_PARAMETERS = "REQUEST_PARAMETERS"; + + /** + * The response parameters for this invocation + */ + public static final String PROPNAME_RESPONSE_PARAMETERS = "RESPONSE_PARAMETERS"; + + private final Type type; + private final String code; private Map properties = null; - private FHIROperationContext(Type type) { + private FHIROperationContext(Type type, String code) { if (type == null) { throw new IllegalArgumentException("Context type cannot be null"); } this.type = type; + this.code = code; properties = new HashMap(); } @@ -89,6 +101,10 @@ public Type getType() { return type; } + public String getOperationCode() { + return code; + } + public void setProperty(String name, Object value) { properties.put(name, value); } @@ -105,16 +121,16 @@ public HttpHeaders getHttpHeaders() { return (HttpHeaders) getProperty(PROPNAME_HTTP_HEADERS); } - public static FHIROperationContext createSystemOperationContext() { - return new FHIROperationContext(Type.SYSTEM); + public static FHIROperationContext createSystemOperationContext(String operationCode) { + return new FHIROperationContext(Type.SYSTEM, operationCode); } - public static FHIROperationContext createResourceTypeOperationContext() { - return new FHIROperationContext(Type.RESOURCE_TYPE); + public static FHIROperationContext createResourceTypeOperationContext(String operationCode) { + return new FHIROperationContext(Type.RESOURCE_TYPE, operationCode); } - public static FHIROperationContext createInstanceOperationContext() { - return new FHIROperationContext(Type.INSTANCE); + public static FHIROperationContext createInstanceOperationContext(String operationCode) { + return new FHIROperationContext(Type.INSTANCE, operationCode); } /** diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/util/FHIROperationUtil.java b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIROperationUtil.java similarity index 99% rename from fhir-server/src/main/java/com/ibm/fhir/server/util/FHIROperationUtil.java rename to fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIROperationUtil.java index a8cdb39d05a..843e83b2e7b 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/util/FHIROperationUtil.java +++ b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIROperationUtil.java @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package com.ibm.fhir.server.util; +package com.ibm.fhir.server.spi.operation; import static com.ibm.fhir.model.type.String.string; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/FHIRResourceHelpers.java b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIRResourceHelpers.java similarity index 95% rename from fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/FHIRResourceHelpers.java rename to fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIRResourceHelpers.java index 9b51af3268d..70835f7c7ca 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/FHIRResourceHelpers.java +++ b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIRResourceHelpers.java @@ -4,7 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -package com.ibm.fhir.server.operation.spi; +package com.ibm.fhir.server.spi.operation; import java.time.Instant; import java.util.List; @@ -17,17 +17,16 @@ import com.ibm.fhir.model.patch.FHIRPatch; import com.ibm.fhir.model.resource.Bundle; import com.ibm.fhir.model.resource.OperationOutcome; -import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.resource.OperationOutcome.Issue; +import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.persistence.FHIRPersistenceTransaction; import com.ibm.fhir.persistence.ResourceEraseRecord; import com.ibm.fhir.persistence.SingleResourceResult; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.persistence.erase.EraseDTO; import com.ibm.fhir.persistence.exception.FHIRPersistenceException; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.persistence.payload.PayloadKey; import com.ibm.fhir.search.context.FHIRSearchContext; -import com.ibm.fhir.server.util.FHIRRestHelper.Interaction; /** * This interface describes the set of helper methods from the FHIR REST layer that are used by custom operation @@ -39,6 +38,36 @@ public interface FHIRResourceHelpers { // Constant for indicating whether an update can be skipped when the requested update resource matches the existing one public static final boolean SKIPPABLE_UPDATE = true; + 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); + } + } + /** * Validate an interaction for a specified resource type. * @@ -65,7 +94,7 @@ public interface FHIRResourceHelpers { * @throws Exception */ FHIRRestOperationResponse doCreate(String type, Resource resource, String ifNoneExist, boolean doValidation) throws Exception; - + /** * Performs the heavy lifting associated with a 'create' interaction. Validates the resource. * @@ -93,7 +122,7 @@ default FHIRRestOperationResponse doCreate(String type, Resource resource, Strin * @return * @throws Exception */ - FHIRRestOperationResponse doCreateMeta(FHIRPersistenceEvent event, List warnings, String type, Resource resource, + FHIRRestOperationResponse doCreateMeta(FHIRPersistenceEvent event, List warnings, String type, Resource resource, String ifNoneExist) throws Exception; /** @@ -108,7 +137,7 @@ FHIRRestOperationResponse doCreateMeta(FHIRPersistenceEvent event, List w FHIRRestOperationResponse doCreatePersist(FHIRPersistenceEvent event, List warnings, Resource resource) throws Exception; /** - * 1st phase of update interaction. + * 1st phase of update interaction. * @param event * @param type * @param id @@ -140,7 +169,7 @@ FHIRRestOperationResponse doUpdateMeta(FHIRPersistenceEvent event, String type, */ public FHIRRestOperationResponse doPatchOrUpdatePersist(FHIRPersistenceEvent event, String type, String id, boolean isPatch, Resource newResource, Resource prevResource, List warnings, boolean isDeleted) throws Exception; - + /** * Builds a collection of properties that will be passed to the persistence interceptors. * @@ -157,7 +186,7 @@ public FHIRRestOperationResponse doPatchOrUpdatePersist(FHIRPersistenceEvent eve */ Map buildPersistenceEventProperties(String type, String id, String version, FHIRSearchContext searchContext) throws FHIRPersistenceException; - + /** * Performs an update operation (a new version of the Resource will be stored). Validates the resource. * @@ -399,8 +428,6 @@ Bundle doSearch(String type, String compartment, String compartmentId, Multivalu * the resource logical id associated with the request * @param versionId * the resource version id associated with the request - * @param operationName - * the name of the custom operation to be invoked * @param resource * the input resource associated with the custom operation to be invoked * @param queryParameters @@ -409,7 +436,7 @@ Bundle doSearch(String type, String compartment, String compartmentId, Multivalu * @return a Resource that represents the response to the custom operation * @throws Exception */ - Resource doInvoke(FHIROperationContext operationContext, String resourceTypeName, String logicalId, String versionId, String operationName, + Resource doInvoke(FHIROperationContext operationContext, String resourceTypeName, String logicalId, String versionId, Resource resource, MultivaluedMap queryParameters) throws Exception; /** @@ -466,14 +493,14 @@ default ResourceEraseRecord doErase(FHIROperationContext operationContext, Erase * @throws Exception */ List doRetrieveIndex(FHIROperationContext operationContext, String resourceTypeName, int count, Instant notModifiedAfter, Long afterIndexId) throws Exception; - + /** * Generate a new resource id. This is typically delegated to the persistence layer, which * may want to create FHIR-compliant ids optimized for a certain type of storage. * @return */ String generateResourceId(); - + /** * If the underlying persistence layer supports offloading payload storage, initiate the * request here. diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/FHIRRestOperationResponse.java b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIRRestOperationResponse.java similarity index 89% rename from fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/FHIRRestOperationResponse.java rename to fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIRRestOperationResponse.java index b6ab21b183c..225627d5323 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/operation/spi/FHIRRestOperationResponse.java +++ b/fhir-server-spi/src/main/java/com/ibm/fhir/server/spi/operation/FHIRRestOperationResponse.java @@ -1,10 +1,10 @@ /* - * (C) Copyright IBM Corp. 2017,2019 + * (C) Copyright IBM Corp. 2017, 2021 * * SPDX-License-Identifier: Apache-2.0 */ -package com.ibm.fhir.server.operation.spi; +package com.ibm.fhir.server.spi.operation; import java.net.URI; import java.util.concurrent.Future; @@ -13,7 +13,6 @@ import com.ibm.fhir.model.resource.OperationOutcome; import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.model.type.Instant; import com.ibm.fhir.persistence.payload.PayloadKey; /** @@ -25,22 +24,20 @@ public class FHIRRestOperationResponse { private Resource resource; private Resource prevResource; private OperationOutcome operationOutcome; - private int versionNumber; - private Instant lastUpdated; private boolean deleted; - + // Flag to indicate the request is complete and can be returned as-is private boolean completed; - + // A nested response we may get when offloading payload storage (e.g. in COS, Cassandra) private Future storePayloadResponse; - + // The id of the resource, which could be new in the case of create private String resourceId; - + public FHIRRestOperationResponse() { } - + public FHIRRestOperationResponse(Response.Status status, URI locationURI, Resource resource) { setStatus(status); setLocationURI(locationURI); @@ -51,15 +48,13 @@ public FHIRRestOperationResponse(Response.Status status, URI locationURI, Operat setLocationURI(locationURI); setOperationOutcome(operationOutcome); } - - public FHIRRestOperationResponse(Resource resource, String resourceId, int versionNumber, Instant lastUpdated, Future storePayloadResponse) { + + public FHIRRestOperationResponse(Resource resource, String resourceId, Future storePayloadResponse) { this.resource = resource; this.resourceId = resourceId; - this.versionNumber = versionNumber; - this.lastUpdated = lastUpdated; this.setStorePayloadResponse(storePayloadResponse); } - + public Response.Status getStatus() { return status; } @@ -94,7 +89,7 @@ public OperationOutcome getOperationOutcome() { public void setOperationOutcome(OperationOutcome operationOutcome) { this.operationOutcome = operationOutcome; } - + /** * Getter for the resourceId * @return diff --git a/fhir-server-spi/src/main/resources/.empty b/fhir-server-spi/src/main/resources/.empty new file mode 100644 index 00000000000..e69de29bb2d diff --git a/fhir-server-test/src/main/java/com/ibm/fhir/server/test/persistence/TaggingInterceptor.java b/fhir-server-test/src/main/java/com/ibm/fhir/server/test/persistence/TaggingInterceptor.java index 0ba6fa557ae..0093aee6583 100644 --- a/fhir-server-test/src/main/java/com/ibm/fhir/server/test/persistence/TaggingInterceptor.java +++ b/fhir-server-test/src/main/java/com/ibm/fhir/server/test/persistence/TaggingInterceptor.java @@ -10,9 +10,9 @@ import com.ibm.fhir.model.type.Coding; import com.ibm.fhir.model.type.Meta; import com.ibm.fhir.model.type.Uri; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptor; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptorException; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptor; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptorException; /** * A sample persistence interceptor that adds a tag to each resource before it gets persisted. diff --git a/fhir-server-test/src/test/java/com/ibm/fhir/server/test/websocket/FHIRNotificationServiceClientEndpoint.java b/fhir-server-test/src/test/java/com/ibm/fhir/server/test/websocket/FHIRNotificationServiceClientEndpoint.java index ea8d6db2f0b..68c0f0fe52d 100644 --- a/fhir-server-test/src/test/java/com/ibm/fhir/server/test/websocket/FHIRNotificationServiceClientEndpoint.java +++ b/fhir-server-test/src/test/java/com/ibm/fhir/server/test/websocket/FHIRNotificationServiceClientEndpoint.java @@ -32,8 +32,8 @@ import org.glassfish.tyrus.core.CloseReasons; -import com.ibm.fhir.notification.FHIRNotificationEvent; -import com.ibm.fhir.notification.util.FHIRNotificationUtil; +import com.ibm.fhir.server.notification.FHIRNotificationEvent; +import com.ibm.fhir.server.notification.FHIRNotificationUtil; public class FHIRNotificationServiceClientEndpoint extends Endpoint { private boolean DEBUG = false; diff --git a/fhir-server-test/src/test/java/com/ibm/fhir/server/test/websocket/WebSocketNotificationsTest.java b/fhir-server-test/src/test/java/com/ibm/fhir/server/test/websocket/WebSocketNotificationsTest.java index 79b478a934e..d3a4ca4fc0e 100644 --- a/fhir-server-test/src/test/java/com/ibm/fhir/server/test/websocket/WebSocketNotificationsTest.java +++ b/fhir-server-test/src/test/java/com/ibm/fhir/server/test/websocket/WebSocketNotificationsTest.java @@ -24,7 +24,7 @@ import com.ibm.fhir.model.resource.Observation; import com.ibm.fhir.model.resource.Patient; import com.ibm.fhir.model.test.TestUtil; -import com.ibm.fhir.notification.FHIRNotificationEvent; +import com.ibm.fhir.server.notification.FHIRNotificationEvent; import com.ibm.fhir.server.test.FHIRServerTestBase; /** diff --git a/fhir-server/pom.xml b/fhir-server/pom.xml index 28eac37ab41..1c575a76218 100644 --- a/fhir-server/pom.xml +++ b/fhir-server/pom.xml @@ -48,6 +48,23 @@ jakarta.websocket-api provided + + org.apache.kafka + kafka-clients + + + io.nats + jnats + + + io.nats + java-nats-streaming + + + ${project.groupId} + fhir-server-spi + ${project.version} + ${project.groupId} fhir-validation @@ -85,36 +102,32 @@ ${project.groupId} - fhir-notification + fhir-persistence-jdbc ${project.version} - ${project.groupId} - fhir-notification-websocket - ${project.version} + org.owasp.encoder + encoder - ${project.groupId} - fhir-notification-kafka - ${project.version} + org.apache.httpcomponents + httpclient ${project.groupId} - fhir-notification-nats - ${project.version} + fhir-examples + test ${project.groupId} - fhir-persistence-jdbc + fhir-model ${project.version} + test-jar - org.owasp.encoder - encoder - - - org.apache.httpcomponents - httpclient + org.skyscreamer + jsonassert + test org.glassfish.tyrus.bundles diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/interceptor/FHIRPersistenceInterceptorMgr.java b/fhir-server/src/main/java/com/ibm/fhir/server/interceptor/FHIRPersistenceInterceptorMgr.java new file mode 100644 index 00000000000..05e1939e9e8 --- /dev/null +++ b/fhir-server/src/main/java/com/ibm/fhir/server/interceptor/FHIRPersistenceInterceptorMgr.java @@ -0,0 +1,197 @@ +/* + * (C) Copyright IBM Corp. 2016, 2021 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.server.interceptor; + +import java.util.Iterator; +import java.util.List; +import java.util.ServiceLoader; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.ibm.fhir.core.FHIRUtilities; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptor; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptorException; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; + +/** + * This class implements the FHIR persistence interceptor framework. This framework allows users to inject business + * logic into the REST API request processing code path at various points. + * + * Interceptors are discovered using the jdk's ServiceProvider class. + * + * To register an interceptor implementation, develop a class that implements the FHIRPersistenceInterceptor interface, + * and then insert your implementation class name into a file called + * META-INF/services/com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptor and store that file in your jar. + * These "interceptor" jars should be stored in a common place defined by the FHIR Server. + */ +public class FHIRPersistenceInterceptorMgr { + private static final Logger log = Logger.getLogger(FHIRPersistenceInterceptorMgr.class.getName()); + + private static FHIRPersistenceInterceptorMgr instance = new FHIRPersistenceInterceptorMgr(); + + // Our list of discovered interceptors. + List interceptors = new CopyOnWriteArrayList<>(); + + public static FHIRPersistenceInterceptorMgr getInstance() { + return instance; + } + + private FHIRPersistenceInterceptorMgr() { + // Discover all implementations of our interceptor interface, then add them to our list of interceptors. + ServiceLoader slList = ServiceLoader.load(FHIRPersistenceInterceptor.class); + Iterator iter = slList.iterator(); + if (iter.hasNext()) { + log.fine("Discovered the following persistence interceptors:"); + while (iter.hasNext()) { + FHIRPersistenceInterceptor interceptor = iter.next(); + if (log.isLoggable(Level.FINE)) { + log.fine(">>> " + interceptor.getClass().getName() + '@' + FHIRUtilities.getObjectHandle(interceptor)); + } + interceptors.add(interceptor); + } + } else { + log.fine("No persistence interceptors found..."); + } + } + + /** + * This method can be used to programmatically register an interceptor such that it is added + * at the end of the list of registered interceptors. + * @param interceptor persistence interceptor to be registered + */ + public void addInterceptor(FHIRPersistenceInterceptor interceptor) { + if (log.isLoggable(Level.FINE)) { + log.fine("Registering persistence interceptor: " + interceptor.getClass().getName() + '@' + FHIRUtilities.getObjectHandle(interceptor)); + } + interceptors.add(interceptor); + } + + /** + * This method can be used to programmatically register an interceptor such that it is added + * at the beginning of the list of registered interceptors. + * @param interceptor persistence interceptor to be registered + */ + public void addPrioritizedInterceptor(FHIRPersistenceInterceptor interceptor) { + if (log.isLoggable(Level.FINE)) { + log.fine("Registering persistence interceptor: " + interceptor.getClass().getName() + '@' + FHIRUtilities.getObjectHandle(interceptor)); + } + interceptors.add(0, interceptor); + } + + /** + * The following methods will invoke the respective interceptor methods on each registered interceptor. + */ + public void fireBeforeCreateEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.beforeCreate(event); + } + } + + public void fireAfterCreateEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.afterCreate(event); + } + } + + public void fireBeforeUpdateEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.beforeUpdate(event); + } + } + + public void fireAfterUpdateEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.afterUpdate(event); + } + } + + public void fireBeforePatchEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.beforePatch(event); + } + } + + public void fireAfterPatchEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.afterPatch(event); + } + } + + public void fireBeforeDeleteEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.beforeDelete(event); + } + } + + public void fireAfterDeleteEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.afterDelete(event); + } + } + + public void fireBeforeReadEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.beforeRead(event); + } + } + + public void fireAfterReadEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.afterRead(event); + } + } + + public void fireBeforeVreadEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.beforeVread(event); + } + } + + public void fireAfterVreadEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.afterVread(event); + } + } + + public void fireBeforeHistoryEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.beforeHistory(event); + } + } + + public void fireAfterHistoryEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.afterHistory(event); + } + } + + public void fireBeforeSearchEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.beforeSearch(event); + } + } + + public void fireAfterSearchEvent(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.afterSearch(event); + } + } + + public void fireBeforeInvokeEvent(FHIROperationContext context) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.beforeInvoke(context); + } + } + + public void fireAfterInvokeEvent(FHIROperationContext context) throws FHIRPersistenceInterceptorException { + for (FHIRPersistenceInterceptor interceptor : interceptors) { + interceptor.afterInvoke(context); + } + } +} \ No newline at end of file diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/listener/FHIRServletContextListener.java b/fhir-server/src/main/java/com/ibm/fhir/server/listener/FHIRServletContextListener.java index 3282e770066..15f14b1de49 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/listener/FHIRServletContextListener.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/listener/FHIRServletContextListener.java @@ -52,17 +52,17 @@ import com.ibm.fhir.model.config.FHIRModelConfig; import com.ibm.fhir.model.lang.util.LanguageRegistryUtil; import com.ibm.fhir.model.util.FHIRUtil; -import com.ibm.fhir.notification.websocket.impl.FHIRNotificationServiceEndpointConfig; -import com.ibm.fhir.notifications.kafka.impl.FHIRNotificationKafkaPublisher; -import com.ibm.fhir.notifications.nats.impl.FHIRNotificationNATSPublisher; import com.ibm.fhir.path.function.registry.FHIRPathFunctionRegistry; import com.ibm.fhir.persistence.helper.FHIRPersistenceHelper; import com.ibm.fhir.registry.FHIRRegistry; import com.ibm.fhir.search.util.SearchUtil; +import com.ibm.fhir.server.notification.kafka.FHIRNotificationKafkaPublisher; +import com.ibm.fhir.server.notification.websocket.FHIRNotificationServiceEndpointConfig; +import com.ibm.fhir.server.notifications.nats.FHIRNotificationNATSPublisher; import com.ibm.fhir.server.operation.FHIROperationRegistry; import com.ibm.fhir.server.registry.ServerRegistryResourceProvider; import com.ibm.fhir.server.resolve.ServerResolveFunction; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; import com.ibm.fhir.term.config.FHIRTermConfig; import com.ibm.fhir.term.graph.provider.GraphTermServiceProvider; import com.ibm.fhir.term.remote.provider.RemoteTermServiceProvider; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationEvent.java b/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationEvent.java new file mode 100644 index 00000000000..ac5ef8869f6 --- /dev/null +++ b/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationEvent.java @@ -0,0 +1,93 @@ +/* + * (C) Copyright IBM Corp. 2016, 2021 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.server.notification; + +import com.ibm.fhir.model.resource.Resource; + +public class FHIRNotificationEvent { + private String lastUpdated = null; + private String location = null; + private String operationType = null; + private String resourceId = null; + private String tenantId = null; + private String datasourceId = null; + private Resource resource = null; + + public FHIRNotificationEvent() { + // No Operation + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public String getLastUpdated() { + return lastUpdated; + } + + public void setLastUpdated(String lastUpdated) { + this.lastUpdated = lastUpdated; + } + + public String getLocation() { + return location; + } + + public void setLocation(String location) { + this.location = location; + } + + public String getOperationType() { + return operationType; + } + + public void setOperationType(String operationType) { + this.operationType = operationType; + } + + public String getTenantId() { + return tenantId; + } + + public void setTenantId(String tenantId) { + this.tenantId = tenantId; + } + + public String getDatasourceId() { + return datasourceId; + } + + public void setDatasourceId(String datasourceId) { + this.datasourceId = datasourceId; + } + + public Resource getResource() { + return resource; + } + + public void setResource(Resource resource) { + this.resource = resource; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("FHIRNotificationEvent[" + + "operation=" + getOperationType() + + ", resourceId=" + getResourceId() + + ", location=" + getLocation() + + ", lastUpdated=" + getLastUpdated() + + ", datasourceId=" + getDatasourceId() + + ", tenantId=" + getTenantId() + + "]"); + return sb.toString(); + } +} \ No newline at end of file diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationException.java b/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationException.java new file mode 100644 index 00000000000..6054a75708f --- /dev/null +++ b/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationException.java @@ -0,0 +1,29 @@ +/* + * (C) Copyright IBM Corp. 2016,2019 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.server.notification; + +public class FHIRNotificationException extends Exception { + + private static final long serialVersionUID = 1L; + + /** + * a notification exception + * @param message + */ + public FHIRNotificationException(String message) { + this(message, null); + } + + /** + * a notification exception with a throwable + * @param message + * @param cause + */ + public FHIRNotificationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationService.java b/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationService.java new file mode 100644 index 00000000000..b75408c2a32 --- /dev/null +++ b/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationService.java @@ -0,0 +1,223 @@ +/* + * (C) Copyright IBM Corp. 2016, 2021 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.server.notification; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.ibm.fhir.config.FHIRConfiguration; +import com.ibm.fhir.config.FHIRRequestContext; +import com.ibm.fhir.model.resource.Resource; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; +import com.ibm.fhir.server.interceptor.FHIRPersistenceInterceptorMgr; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptor; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptorException; + +/** + * This class coordinates the activities of the FHIR Server notification service. + */ +public class FHIRNotificationService implements FHIRPersistenceInterceptor { + private static final Logger log = java.util.logging.Logger.getLogger(FHIRNotificationService.class.getName()); + private List subscribers = new CopyOnWriteArrayList<>(); + private static final FHIRNotificationService INSTANCE = new FHIRNotificationService(); + private Set includedResourceTypes = Collections.synchronizedSortedSet(new TreeSet()); + + private FHIRNotificationService() { + log.entering(this.getClass().getName(), "FHIRNotificationService"); + try { + // Register the notification service as an interceptor so we can rely on the + // interceptor methods to trigger the 'publish' of the notification events. + FHIRPersistenceInterceptorMgr.getInstance().addPrioritizedInterceptor(this); + + initNotificationResourceTypes(); + } catch (Throwable t) { + throw new RuntimeException("Unexpected error during initialization.", t); + } + log.exiting(this.getClass().getName(), "FHIRNotificationService"); + } + + private void initNotificationResourceTypes() throws Exception { + List types = FHIRConfiguration.getInstance().loadConfiguration().getStringListProperty(FHIRConfiguration.PROPERTY_NOTIFICATION_RESOURCE_TYPES); + if (types != null) { + for (String type : types) { + includedResourceTypes.add(type); + } + } + + log.info("Notification service, when enabled, will publish events for these resource types: '" + + (includedResourceTypes.isEmpty() ? "ALL" : includedResourceTypes.toString()) + "'"); + } + + public static FHIRNotificationService getInstance() { + return INSTANCE; + } + + /** + * Method for broadcasting message to each subscriber. + * + * @param event + */ + public void publish(FHIRNotificationEvent event) { + log.entering(this.getClass().getName(), "publish"); + for (FHIRNotificationSubscriber subscriber : subscribers) { + try { + subscriber.notify(event); + } catch (FHIRNotificationException e) { + subscribers.remove(subscriber); + log.log(Level.WARNING, FHIRNotificationService.class.getName() + ": unable to publish event", e); + } + } + log.exiting(this.getClass().getName(), "publish"); + } + + /** + * Method to subscribe the target notification implementation + * + * @param subscriber + */ + public void subscribe(FHIRNotificationSubscriber subscriber) { + log.entering(this.getClass().getName(), "subscribe"); + try { + if (!subscribers.contains(subscriber)) { + subscribers.add(subscriber); + } + } finally { + log.exiting(this.getClass().getName(), "subscribe"); + } + } + + /** + * Method to unsubscribe the target notification implementation + * + * @param subscriber + */ + public void unsubscribe(FHIRNotificationSubscriber subscriber) { + log.entering(this.getClass().getName(), "unsubscribe"); + try { + if (subscribers.contains(subscriber)) { + subscribers.remove(subscriber); + } + } finally { + log.exiting(this.getClass().getName(), "unsubscribe"); + } + } + + /** + * Check if this subscriber has subscribed to this service + * + * @param subscriber + * @return + */ + public boolean isSubscribed(FHIRNotificationSubscriber subscriber) { + log.entering(this.getClass().getName(), "isSubscribed"); + try { + return subscribers.contains(subscriber); + } finally { + log.exiting(this.getClass().getName(), "isSubscribed"); + } + } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // The following set of methods are from the FHIRPersistenceInterceptor interface and are implemented here to allow + // the notification service to be registered as a persistence interceptor. All we really need to do in these methods + // is perform the "publish" action. + // Only the 'after' methods are enabled. + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void afterCreate(FHIRPersistenceEvent pEvent) throws FHIRPersistenceInterceptorException { + if (shouldPublish(pEvent)) { + this.publish(buildNotificationEvent("create", pEvent)); + } + } + + @Override + public void afterUpdate(FHIRPersistenceEvent pEvent) throws FHIRPersistenceInterceptorException { + if (shouldPublish(pEvent)) { + this.publish(buildNotificationEvent("update", pEvent)); + } + } + + @Override + public void afterDelete(FHIRPersistenceEvent pEvent) throws FHIRPersistenceInterceptorException { + if (shouldPublish(pEvent)) { + this.publish(buildNotificationEvent("delete", pEvent)); + } + } + + @Override + public void afterPatch(FHIRPersistenceEvent pEvent) throws FHIRPersistenceInterceptorException { + if (shouldPublish(pEvent)) { + this.publish(buildNotificationEvent("patch", pEvent)); + } + } + + /** + * Returns true iff we should publish the specified persistence event as a notification event. + */ + private boolean shouldPublish(FHIRPersistenceEvent pEvent) { + log.entering(this.getClass().getName(), "shouldPublish"); + + try { + // If our resource type filter is empty, then we should publish all notification events. + if (includedResourceTypes == null || includedResourceTypes.isEmpty()) { + log.finer("Resource type filter not specified, publishing all events."); + return true; + } + + // If the resource type filter contains "*", then publish all events. + if (includedResourceTypes.contains("*")) { + log.finer("Resource type filter contains '*', publishing all events."); + return true; + } + + // Retrieve the resource type associated with the event. + String resourceType = (String) pEvent.getProperty(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE); + return (resourceType != null ? includedResourceTypes.contains(resourceType) : false); + } catch (Throwable t) { + throw new IllegalStateException("Unexpected exception while checking notification resource type inclusion.", t); + } finally { + log.exiting(this.getClass().getName(), "shouldPublish"); + } + } + + /** + * Builds a FHIRNotificationEvent object from the specified FHIRPersistenceEvent. + */ + private FHIRNotificationEvent buildNotificationEvent(String operation, FHIRPersistenceEvent pEvent) { + try { + FHIRNotificationEvent event = new FHIRNotificationEvent(); + event.setOperationType(operation); + Resource resource = pEvent.getFhirResource(); + event.setLastUpdated(resource.getMeta().getLastUpdated().getValue().toString()); + String location = (String) pEvent.getProperty(FHIRPersistenceEvent.PROPNAME_RESOURCE_LOCATION_URI); + if (location == null) { + // Must be a delete irrespective of version. + location = pEvent.getFhirResourceType() + "/" + pEvent.getFhirResourceId(); + } + event.setLocation(location); + event.setResourceId(resource.getId()); + event.setResource(resource); + + // Adds the tenant id and datastore id + String tenantId = FHIRRequestContext.get().getTenantId(); + String dsId = FHIRRequestContext.get().getDataStoreId(); + event.setDatasourceId(dsId); + event.setTenantId(tenantId); + + return event; + } catch (Exception e) { + log.log(Level.SEVERE, this.getClass().getName() + ": unable to build notification event", e); + throw e; + } + } +} diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationSubscriber.java b/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationSubscriber.java new file mode 100644 index 00000000000..037f693f8ea --- /dev/null +++ b/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationSubscriber.java @@ -0,0 +1,16 @@ +/* + * (C) Copyright IBM Corp. 2016,2019 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.server.notification; + +public interface FHIRNotificationSubscriber { + /** + * Notify subscriber of an event + * @param event + * @throws FHIRNotificationException + */ + void notify(FHIRNotificationEvent event) throws FHIRNotificationException; +} diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationUtil.java b/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationUtil.java new file mode 100644 index 00000000000..ecec9f23a35 --- /dev/null +++ b/fhir-server/src/main/java/com/ibm/fhir/server/notification/FHIRNotificationUtil.java @@ -0,0 +1,132 @@ +/* + * (C) Copyright IBM Corp. 2016, 2021 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.server.notification; + +import java.io.StringReader; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Logger; + +import com.ibm.fhir.config.FHIRConfigHelper; +import com.ibm.fhir.config.FHIRConfiguration; +import com.ibm.fhir.exception.FHIRException; +import com.ibm.fhir.model.format.Format; +import com.ibm.fhir.model.parser.FHIRJsonParser; +import com.ibm.fhir.model.parser.FHIRParser; +import com.ibm.fhir.model.util.FHIRUtil; +import com.ibm.fhir.model.util.JsonSupport; +import com.ibm.fhir.search.SearchConstants; + +import jakarta.json.Json; +import jakarta.json.JsonBuilderFactory; +import jakarta.json.JsonException; +import jakarta.json.JsonObject; +import jakarta.json.JsonObjectBuilder; +import jakarta.json.JsonReader; +import jakarta.json.JsonReaderFactory; + +/** + * FHIRNotificationUtil supports serializing and deserializing the FHIRNotificationEvent based on conditions. + */ +public class FHIRNotificationUtil { + private static final Logger LOG = Logger.getLogger(FHIRNotificationUtil.class.getSimpleName()); + private static final JsonReaderFactory JSON_READER_FACTORY = Json.createReaderFactory(null); + private static final JsonBuilderFactory JSON_BUILDER_FACTORY = Json.createBuilderFactory(null); + + private static final int DEFAULT_MAX_SIZE = 1000000; + + private FHIRNotificationUtil() { + // No Operation + } + + /** + * serialize the FHIRNotificationEvent + * + * @param jsonString the input string + * @return FHIRNotificationEvent without the Resource + */ + public static FHIRNotificationEvent toNotificationEvent(String jsonString) { + try (JsonReader reader = JSON_READER_FACTORY.createReader(new StringReader(jsonString))) { + JsonObject jsonObject = reader.readObject(); + FHIRNotificationEvent event = new FHIRNotificationEvent(); + event.setLastUpdated(jsonObject.getString("lastUpdated")); + event.setLocation(jsonObject.getString("location")); + event.setOperationType(jsonObject.getString("operationType")); + event.setResourceId(jsonObject.getString("resourceId")); + event.setDatasourceId(jsonObject.getString("datasourceId")); + event.setTenantId(jsonObject.getString("tenantId")); + return event; + } catch (JsonException e) { + LOG.warning("Failed to parse json string: " + e.getLocalizedMessage()); + return null; + } + } + + /** + * Serializes the notification event into a JSON string. + * @param event the FHIRNotificationEvent structure to be serialized + * @param includeResource a flag that controls whether or not the resource object within + * the event structure should be included in the serialized message. + * @return the serialized message as a String + * @throws FHIRException + */ + public static String toJsonString(FHIRNotificationEvent event, boolean includeResource) throws FHIRException { + JsonObjectBuilder builder = JSON_BUILDER_FACTORY.createObjectBuilder(); + builder.add("lastUpdated", event.getLastUpdated()); + builder.add("location", event.getLocation()); + builder.add("operationType", event.getOperationType()); + builder.add("resourceId", event.getResourceId()); + builder.add("datasourceId", event.getDatasourceId()); + builder.add("tenantId", event.getTenantId()); + + // If it's a delete operation, don't add as there is no actual resource in the event. + String jsonString; + if (!"delete".equals(event.getOperationType()) && includeResource && event.getResource() != null) { + builder.add("resource", JsonSupport.toJsonObject(event.getResource())); + JsonObject jsonObject = builder.build(); + + jsonString = jsonObject.toString(); + long length = jsonString.getBytes().length; + + int maxSize = FHIRConfigHelper.getIntProperty(FHIRConfiguration.PROPERTY_NOTIFICATION_MAX_SIZE, DEFAULT_MAX_SIZE); + if (length > maxSize) { + LOG.fine(() -> event.getResource().getClass().getSimpleName() + "/" + event.getResourceId() + " is over the size limit - '" + length + "' > '" + maxSize + "'"); + + // the build method wipes out any preexisting values, we have to add them back. + builder.add("lastUpdated", event.getLastUpdated()); + builder.add("location", event.getLocation()); + builder.add("operationType", event.getOperationType()); + builder.add("resourceId", event.getResourceId()); + builder.add("datasourceId", event.getDatasourceId()); + builder.add("tenantId", event.getTenantId()); + + // If we are including a subset, we'll add here. + String subset = FHIRConfigHelper.getStringProperty(FHIRConfiguration.PROPERTY_NOTIFICATION_NOTIFICATION_SIZE_BEHAVIOR, "subset"); + if ("subset".equals(subset)) { + List elements = Arrays.asList("id", "meta", "resourceType"); + com.ibm.fhir.model.resource.Resource resource = FHIRParser.parser(Format.JSON) + .as(FHIRJsonParser.class) + .parseAndFilter(JsonSupport.toJsonObject(event.getResource()), elements); + // add a SUBSETTED tag to this resource to indicate that its elements have been filtered + if (!FHIRUtil.hasTag(resource, SearchConstants.SUBSETTED_TAG)) { + resource = FHIRUtil.addTag(resource, SearchConstants.SUBSETTED_TAG); + } + builder.add("resource", JsonSupport.toJsonObject(resource)); + } else { + LOG.fine(() -> "Omitting the resource in FHIRNotificationEvent"); + } + + jsonObject = builder.build(); + jsonString = jsonObject.toString(); + } + } else { + JsonObject jsonObject = builder.build(); + jsonString = jsonObject.toString(); + } + return jsonString; + } +} \ No newline at end of file diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/notification/kafka/FHIRNotificationKafkaPublisher.java b/fhir-server/src/main/java/com/ibm/fhir/server/notification/kafka/FHIRNotificationKafkaPublisher.java new file mode 100644 index 00000000000..b76fa9c8e27 --- /dev/null +++ b/fhir-server/src/main/java/com/ibm/fhir/server/notification/kafka/FHIRNotificationKafkaPublisher.java @@ -0,0 +1,186 @@ +/* + * (C) Copyright IBM Corp. 2016, 2021 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.server.notification.kafka; + +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.kafka.clients.producer.Callback; +import org.apache.kafka.clients.producer.KafkaProducer; +import org.apache.kafka.clients.producer.Producer; +import org.apache.kafka.clients.producer.ProducerConfig; +import org.apache.kafka.clients.producer.ProducerRecord; +import org.apache.kafka.clients.producer.RecordMetadata; + +import com.ibm.fhir.config.FHIRConfigHelper; +import com.ibm.fhir.config.FHIRConfiguration; +import com.ibm.fhir.server.notification.FHIRNotificationEvent; +import com.ibm.fhir.server.notification.FHIRNotificationException; +import com.ibm.fhir.server.notification.FHIRNotificationService; +import com.ibm.fhir.server.notification.FHIRNotificationSubscriber; +import com.ibm.fhir.server.notification.FHIRNotificationUtil; + +/** + * This class implements the FHIR server notification service via a Kafka topic. + */ +public class FHIRNotificationKafkaPublisher implements FHIRNotificationSubscriber { + private static final Logger LOG = Logger.getLogger(FHIRNotificationKafkaPublisher.class.getName()); + + private static FHIRNotificationService service = FHIRNotificationService.getInstance(); + + private String topicName = null; + private Producer producer = null; + + private Properties kafkaProps = null; + + // "Hide" the default ctor. + protected FHIRNotificationKafkaPublisher() { + } + + public FHIRNotificationKafkaPublisher(String topicName, Properties kafkaProps) { + LOG.entering(this.getClass().getName(), "ctor"); + try { + init(topicName, kafkaProps); + } finally { + LOG.exiting(this.getClass().getName(), "ctor"); + } + } + + /** + * Performs any required initialization to allow us to publish events to the topic. + */ + private void init(String topicName, Properties kafkaProps) { + LOG.entering(this.getClass().getName(), "init"); + try { + this.topicName = topicName; + this.kafkaProps = kafkaProps; + if (LOG.isLoggable(Level.FINER)) { + LOG.finer("Kafka publisher is configured with the following properties:\n" + this.kafkaProps.toString()); + LOG.finer("Topic name: " + this.topicName); + } + + // We'll hard-code some properties to ensure they are set correctly. + this.kafkaProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer"); + this.kafkaProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringSerializer"); + this.kafkaProps.put(ProducerConfig.CLIENT_ID_CONFIG, "fhir-server"); + + // Make sure that the properties file contains the bootstrap.servers property at a minimum. + String bootstrapServers = this.kafkaProps.getProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG); + if (bootstrapServers == null) { + throw new IllegalStateException("The " + ProducerConfig.BOOTSTRAP_SERVERS_CONFIG + " property was missing from the Kafka connection properties."); + } + + // Create our producer object to be used for publishing. + producer = new KafkaProducer<>(this.kafkaProps); + + // Register this Kafka implementation as a "subscriber" with our Notification Service. + // This means that our "notify" method will be called when the server publishes an event. + service.subscribe(this); + LOG.info("Initialized Kafka publisher for topic '" + topicName + "' using bootstrap servers: " + bootstrapServers + "."); + } catch (Throwable t) { + String msg = "Caught exception while initializing Kafka publisher."; + LOG.log(Level.SEVERE, msg, t); + throw new IllegalStateException(msg, t); + } finally { + LOG.exiting(this.getClass().getName(), "init"); + } + } + + /** + * Performs any necessary "shutdown" logic to disconnect from the topic. + */ + public void shutdown() { + LOG.entering(this.getClass().getName(), "shutdown"); + + try { + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Shutting down Kafka publisher for topic: '" + topicName + "'."); + } + if (producer != null) { + producer.close(); + } + } finally { + LOG.exiting(this.getClass().getName(), "shutdown"); + } + } + + @Override + public void notify(FHIRNotificationEvent event) throws FHIRNotificationException { + LOG.entering(this.getClass().getName(), "notify"); + String topicId = "[" + this.kafkaProps.getProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG) + "]/" + topicName; + String jsonString = null; + try { + jsonString = FHIRNotificationUtil.toJsonString(event, true); + + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Publishing kafka notification event to topic '" + topicId + "',\nmessage: " + jsonString); + } + + boolean sync = FHIRConfigHelper.getBooleanProperty(FHIRConfiguration.PROPERTY_KAFKA_SYNC, false); + if (!sync) { + producer.send(new ProducerRecord(topicName, jsonString), new KafkaPublisherCallback(event, jsonString, topicId)); + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Returned from async kafka send..."); + } + } else { + RecordMetadata metadata = producer.send(new ProducerRecord(topicName, jsonString), new KafkaPublisherCallback(event, jsonString, topicId)).get(); + if (LOG.isLoggable(Level.FINE)) { + LOG.fine("Record Produced to Topic '" + metadata.topic() + "' at time " + metadata.timestamp()); + } + } + } catch (Throwable e) { + String msg = buildNotificationErrorMessage(topicId, (jsonString == null ? "" : jsonString)); + LOG.log(Level.SEVERE, msg , e); + throw new FHIRNotificationException(msg, e); + } finally { + LOG.exiting(this.getClass().getName(), "notify"); + } + } + + public class KafkaPublisherCallback implements Callback { + private FHIRNotificationEvent event; + private String notificationEvent; + private String topicId; + + public KafkaPublisherCallback(FHIRNotificationEvent event, String notificationEvent, String topicId) { + super(); + this.event = event; + this.notificationEvent = notificationEvent; + this.topicId = topicId; + } + + /** + * This method is called by the KafkaProducer API after a successful *or* unsuccessful send. + */ + @Override + public void onCompletion(RecordMetadata recordMetadata, Exception exception) { + LOG.entering(this.getClass().getName(), "onCompletion"); + + try { + // No exception implies that the send operation succeeded, so log an info message. + if (exception == null) { + LOG.info("Successfully published kafka notification event for resource: " + event.getLocation()); + } else { + // If we detected a 'send' failure, then log an error message that includes the notification message + // that we tried to send. + String msg = buildNotificationErrorMessage(topicId, notificationEvent); + LOG.log(Level.SEVERE, msg, exception); + } + } finally { + LOG.exiting(this.getClass().getName(), "onCompletion"); + } + } + } + + /** + * Builds a formatted error message to indicate a notification publication failure. + */ + private String buildNotificationErrorMessage(String topicId, String notificationEvent) { + return String.format("Kafka publication failure; topic '%s'\nNotification event: %s\n.", topicId, notificationEvent); + } +} diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/notification/websocket/FHIRNotificationServiceEndpoint.java b/fhir-server/src/main/java/com/ibm/fhir/server/notification/websocket/FHIRNotificationServiceEndpoint.java new file mode 100644 index 00000000000..b388501f7bd --- /dev/null +++ b/fhir-server/src/main/java/com/ibm/fhir/server/notification/websocket/FHIRNotificationServiceEndpoint.java @@ -0,0 +1,109 @@ +/* + * (C) Copyright IBM Corp. 2016,2019 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.server.notification.websocket; + +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Logger; + +import javax.websocket.CloseReason; +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfig; +import javax.websocket.Session; + +import com.ibm.fhir.server.notification.FHIRNotificationService; +import com.ibm.fhir.server.notification.FHIRNotificationSubscriber; + +/** + * This class serves as our websocket "endpoint". + */ +public class FHIRNotificationServiceEndpoint extends Endpoint { + private static final Logger log = java.util.logging.Logger.getLogger(FHIRNotificationServiceEndpoint.class.getName()); + + /** + * Currently active notification subscribers + */ + private static final Map subscribers = new ConcurrentHashMap(); + + /** + * Singleton instance of the FHIR Notification Service + */ + private final FHIRNotificationService notificationService = FHIRNotificationService.getInstance(); + + /** + * To process new end point client + * + * @param session + */ + @Override + public void onOpen(Session session, EndpointConfig config) { + log.entering(this.getClass().getName(), "onOpen"); + try { + FHIRNotificationSubscriber subscriber = new FHIRNotificationSubscriberImpl(session); + notificationService.subscribe(subscriber); + subscribers.put(session, subscriber); + log.info(String.format("Notification client [sessionId=%s] has registered.", session.getId())); + } finally { + log.exiting(this.getClass().getName(), "onOpen"); + } + } + + /** + * Process incoming message + * + * @param message + * @param session + * @return + */ + public String onMessage(String message, Session session) { + log.entering(this.getClass().getName(), "onMessage"); + try { + String response = "{\"heartbeat\":\"pong\"}"; + return response; + } finally { + log.exiting(this.getClass().getName(), "onMessage"); + } + } + + /** + * process message which is trying to disconnect + * + * @param session + * @param closeReason + */ + public void onClose(Session session, CloseReason closeReason) { + log.entering(this.getClass().getName(), "onClose"); + try { + FHIRNotificationSubscriber subscriber = subscribers.remove(session); + if (subscriber != null) { + notificationService.unsubscribe(subscriber); + } + log.info(String.format("Notification client [sessionId=%s] has disconnected, reason: %s", session.getId(), closeReason)); + cleanup(); + } finally { + log.exiting(this.getClass().getName(), "onClose"); + } + } + + /** + * Clean up method + */ + private void cleanup() { + log.entering(this.getClass().getName(), "cleanup"); + try { + for (Entry entry : subscribers.entrySet()) { + if (!entry.getKey().isOpen()) { + notificationService.unsubscribe(entry.getValue()); + subscribers.remove(entry.getKey()); + } + } + } finally { + log.exiting(this.getClass().getName(), "cleanup"); + } + } +} diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/notification/websocket/FHIRNotificationServiceEndpointConfig.java b/fhir-server/src/main/java/com/ibm/fhir/server/notification/websocket/FHIRNotificationServiceEndpointConfig.java new file mode 100644 index 00000000000..b04ec8bf8fd --- /dev/null +++ b/fhir-server/src/main/java/com/ibm/fhir/server/notification/websocket/FHIRNotificationServiceEndpointConfig.java @@ -0,0 +1,67 @@ +/* + * (C) Copyright IBM Corp. 2016, 2021 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.server.notification.websocket; + +import java.util.List; +import java.util.Map; + +import javax.websocket.Decoder; +import javax.websocket.Encoder; +import javax.websocket.Extension; +import javax.websocket.server.ServerEndpointConfig; + +/** + * This class is registered with the liberty "ServerContainer" (by our servlet context listener) + * for initializing our websocket endpoint to be used for notifications. + */ +public class FHIRNotificationServiceEndpointConfig implements ServerEndpointConfig { + + private static final String endpointURI = "/notification"; + + public FHIRNotificationServiceEndpointConfig() { + } + + @Override + public List> getEncoders() { + return null; + } + + @Override + public List> getDecoders() { + return null; + } + + @Override + public Map getUserProperties() { + return null; + } + + @Override + public Class getEndpointClass() { + return FHIRNotificationServiceEndpoint.class; + } + + @Override + public String getPath() { + return endpointURI; + } + + @Override + public List getSubprotocols() { + return null; + } + + @Override + public List getExtensions() { + return null; + } + + @Override + public Configurator getConfigurator() { + return new ServerEndpointConfig.Configurator(); + } +} diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/notification/websocket/FHIRNotificationSubscriberImpl.java b/fhir-server/src/main/java/com/ibm/fhir/server/notification/websocket/FHIRNotificationSubscriberImpl.java new file mode 100644 index 00000000000..3ac6afc1a95 --- /dev/null +++ b/fhir-server/src/main/java/com/ibm/fhir/server/notification/websocket/FHIRNotificationSubscriberImpl.java @@ -0,0 +1,43 @@ +/* + * (C) Copyright IBM Corp. 2016,2019 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.server.notification.websocket; + +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.websocket.Session; + +import com.ibm.fhir.server.notification.FHIRNotificationEvent; +import com.ibm.fhir.server.notification.FHIRNotificationException; +import com.ibm.fhir.server.notification.FHIRNotificationSubscriber; +import com.ibm.fhir.server.notification.FHIRNotificationUtil; + +public class FHIRNotificationSubscriberImpl implements FHIRNotificationSubscriber { + private static final Logger log = java.util.logging.Logger.getLogger(FHIRNotificationSubscriberImpl.class.getName()); + private Session session = null; + + public FHIRNotificationSubscriberImpl(Session session) { + this.session = session; + } + + public void notify(FHIRNotificationEvent event) throws FHIRNotificationException { + log.entering(this.getClass().getName(), "notify"); + try { + String message = FHIRNotificationUtil.toJsonString(event, false); + if (log.isLoggable(Level.FINE)) { + log.fine("Publishing websocket notification event on session [id=" + session.getId() + "],\nmessage:" + message); + } + session.getAsyncRemote().sendText(message); + log.info("Successfully published websocket notification event for resource: " + event.getLocation()); + } catch (Exception e) { + String msg = "Error publishing websocket notification event to websocket: " + session.getId(); + log.log(Level.SEVERE, msg, e); + throw new FHIRNotificationException(msg, e); + } finally { + log.exiting(this.getClass().getName(), "notify"); + } + } +} diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/notifications/nats/FHIRNotificationNATSPublisher.java b/fhir-server/src/main/java/com/ibm/fhir/server/notifications/nats/FHIRNotificationNATSPublisher.java new file mode 100644 index 00000000000..5ba8fc6f7bd --- /dev/null +++ b/fhir-server/src/main/java/com/ibm/fhir/server/notifications/nats/FHIRNotificationNATSPublisher.java @@ -0,0 +1,220 @@ +/* + * (C) Copyright IBM Corp. 2020 + * + * SPDX-License-Identifier: Apache-2.0 + */ + +package com.ibm.fhir.server.notifications.nats; + +import java.io.BufferedInputStream; +import java.io.FileInputStream; +import java.security.KeyStore; +import java.security.SecureRandom; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; + +import com.ibm.fhir.server.notification.FHIRNotificationEvent; +import com.ibm.fhir.server.notification.FHIRNotificationException; +import com.ibm.fhir.server.notification.FHIRNotificationService; +import com.ibm.fhir.server.notification.FHIRNotificationSubscriber; +import com.ibm.fhir.server.notification.FHIRNotificationUtil; + +import io.nats.client.Connection; +import io.nats.client.Nats; +import io.nats.streaming.AckHandler; +import io.nats.streaming.NatsStreaming; +import io.nats.streaming.Options; +import io.nats.streaming.StreamingConnection; + +/** + * This class implements the FHIR server notification service via a NATS channel. + */ +public class FHIRNotificationNATSPublisher implements FHIRNotificationSubscriber { + private static final Logger log = Logger.getLogger(FHIRNotificationNATSPublisher.class.getName()); + private static FHIRNotificationService service = FHIRNotificationService.getInstance(); + + private StreamingConnection sc = null; + private AckHandler acb = null; + private String channelName = null; + + // "Hide" the default constructor. + protected FHIRNotificationNATSPublisher() { + } + + public FHIRNotificationNATSPublisher(String clusterId, String channelName, String clientId, String servers, Properties tlsProps) { + log.entering(this.getClass().getName(), "constructor"); + try { + init(clusterId, channelName, clientId, servers, tlsProps); + } finally { + log.exiting(this.getClass().getName(), "constructor"); + } + } + + /** + * Performs any required initialization to allow us to publish events to the channel. + */ + private void init(String clusterId, String channelName, String clientId, String servers, Properties tlsProps) { + log.entering(this.getClass().getName(), "init"); + + SSLContext ctx = null; + + try { + this.channelName = channelName; + if (log.isLoggable(Level.FINER)) { + log.finer("ClusterId: " + clusterId); + log.finer("Channel name: " + channelName); + log.finer("ClientId: " + clientId); + log.finer("Servers: " + servers); + } + + // Make sure that the properties file contains the expected properties. + if (clusterId == null || channelName == null || clientId == null || servers == null || servers.length() == 0) { + throw new IllegalStateException("Config property missing from the NATS connection properties."); + } + + if (Boolean.parseBoolean(tlsProps.getProperty("useTLS"))) { + // Make sure that the tls properties are set. + if (tlsProps.getProperty("truststore") == null || tlsProps.getProperty("truststorePass") == null || + tlsProps.getProperty("keystore") == null || tlsProps.getProperty("keystorePass") == null) { + throw new IllegalStateException("TLS config property missing from the NATS connection properties."); + } + + ctx = createSSLContext(tlsProps); + } + + // Create the NATS client connection options + io.nats.client.Options.Builder builder = new io.nats.client.Options.Builder(); + builder.maxReconnects(-1); + builder.connectionName(channelName); + builder.servers(servers.split(",")); + if (ctx != null) { + builder.sslContext(ctx); + } + io.nats.client.Options natsOptions = builder.build(); + + // Create the NATS connection and the streaming connection + Connection nc = Nats.connect(natsOptions); + Options streamingOptions = new Options.Builder().natsConn(nc).build(); + sc = NatsStreaming.connect(clusterId, clientId, streamingOptions); + + // Create the publish callback + acb = new AckHandler() { + @Override + public void onAck(String nuid, Exception ex) { + log.finer("Received ACK for guid: " + nuid); + if (ex != null && log.isLoggable(Level.SEVERE)) { + log.log(Level.SEVERE, "Error in server ack for guid " + nuid + ": " + ex.getMessage(), ex); + } + } + }; + + // Register this NATS implementation as a "subscriber" with our Notification Service. + // This means that our "notify" method will be called when the server publishes an event. + service.subscribe(this); + log.info("Initialized NATS publisher for channel '" + channelName + "' using servers: '" + servers + "'."); + } catch (Throwable t) { + String msg = "Caught exception while initializing NATS publisher."; + log.log(Level.SEVERE, msg, t); + throw new IllegalStateException(msg, t); + } finally { + log.exiting(this.getClass().getName(), "init"); + } + } + + /** + * Performs any necessary "shutdown" logic to disconnect from the channel. + */ + public void shutdown() { + log.entering(this.getClass().getName(), "shutdown"); + + try { + if (log.isLoggable(Level.FINE)) { + log.fine("Shutting down NATS publisher for channel: '" + channelName + "'."); + } + if (sc != null) { + sc.close(); + } + } catch (Throwable t) { + String msg = "Caught exception shutting down NATS publisher for channel: '" + channelName + "'."; + log.log(Level.SEVERE, msg, t); + throw new IllegalStateException(msg, t); + } finally { + log.exiting(this.getClass().getName(), "shutdown"); + } + } + + /** + * Publishes an event to NATS. + */ + @Override + public void notify(FHIRNotificationEvent event) throws FHIRNotificationException { + log.entering(this.getClass().getName(), "notify"); + String jsonString = null; + try { + jsonString = FHIRNotificationUtil.toJsonString(event, true); + + if (log.isLoggable(Level.FINE)) { + log.fine("Publishing NATS notification event to channel '" + channelName + "',\nmessage: '" + jsonString + "'."); + } + + sc.publish("FHIRNotificationEvent", jsonString.getBytes(), acb); + + if (log.isLoggable(Level.FINE)) { + log.fine("Published NATS notification event to channel '" + channelName + "'"); + } + } catch (Throwable e) { + String msg = buildNotificationErrorMessage(channelName, (jsonString == null ? "" : jsonString)); + log.log(Level.SEVERE, msg , e); + throw new FHIRNotificationException(msg, e); + } finally { + log.exiting(this.getClass().getName(), "notify"); + } + } + + /** + * Builds a formatted error message to indicate a notification publication failure. + */ + private String buildNotificationErrorMessage(String channelName, String notificationEvent) { + return String.format("NATS publication failure; channel '%s'\nNotification event: '%s'\n.", channelName, notificationEvent); + } + /* + * Modified from original NATS documentation @ https://docs.nats.io/developing-with-nats/security/tls + * openssl is used to generate a pkcs12 file (.p12) from client-cert.pem and client-key.pem. + * The resulting file is then imported into a java keystore using keytool which is part of java jdk. + * keytool is also used to import the CA certificate rootCA.pem into the truststore. + */ + private static KeyStore loadKeystore(String path, String password) throws Exception { + KeyStore store = KeyStore.getInstance("PKCS12"); + try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(path));) { + store.load(in, password.toCharArray()); + } + return store; + } + + private static KeyManager[] createKeyManagers(Properties tlsProps) throws Exception { + KeyStore store = loadKeystore(tlsProps.getProperty("keystore"), tlsProps.getProperty("keystorePass")); + KeyManagerFactory factory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + factory.init(store, tlsProps.getProperty("keystorePass").toCharArray()); + return factory.getKeyManagers(); + } + + private static TrustManager[] createTrustManagers(Properties tlsProps) throws Exception { + KeyStore store = loadKeystore(tlsProps.getProperty("truststore"), tlsProps.getProperty("truststorePass")); + TrustManagerFactory factory = TrustManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + factory.init(store); + return factory.getTrustManagers(); + } + + private static SSLContext createSSLContext(Properties tlsProps) throws Exception { + SSLContext ctx = SSLContext.getInstance("TLSv1.2"); + ctx.init(createKeyManagers(tlsProps), createTrustManagers(tlsProps), new SecureRandom()); + return ctx; + } +} diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/operation/FHIROperationRegistry.java b/fhir-server/src/main/java/com/ibm/fhir/server/operation/FHIROperationRegistry.java index d36af9ed119..1c588da4e61 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/operation/FHIROperationRegistry.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/operation/FHIROperationRegistry.java @@ -25,7 +25,7 @@ import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.model.type.code.ResourceType; import com.ibm.fhir.model.util.FHIRUtil; -import com.ibm.fhir.server.operation.spi.FHIROperation; +import com.ibm.fhir.server.spi.operation.FHIROperation; import com.ibm.fhir.validation.FHIRValidator; import com.ibm.fhir.validation.exception.FHIRValidationException; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Batch.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Batch.java index aa9e0891d70..7009f6b491c 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Batch.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Batch.java @@ -35,7 +35,7 @@ import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.server.exception.FHIRRestBundledRequestException; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; import com.ibm.fhir.server.util.FHIRRestHelper; import com.ibm.fhir.server.util.RestAuditLogger; 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 0fc88616c71..68781569b66 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 @@ -96,7 +96,7 @@ import com.ibm.fhir.search.util.SearchUtil; import com.ibm.fhir.server.FHIRBuildIdentifier; import com.ibm.fhir.server.operation.FHIROperationRegistry; -import com.ibm.fhir.server.operation.spi.FHIROperation; +import com.ibm.fhir.server.spi.operation.FHIROperation; import com.ibm.fhir.server.util.RestAuditLogger; @Path("/") diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Create.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Create.java index 6f841e7c041..d3a0926f3f5 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Create.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Create.java @@ -30,7 +30,7 @@ import com.ibm.fhir.core.HTTPReturnPreference; import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRRestHelper; import com.ibm.fhir.server.util.RestAuditLogger; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Delete.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Delete.java index 21f990dbfe0..3ce027bb9ef 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Delete.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Delete.java @@ -28,7 +28,7 @@ import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.persistence.exception.FHIRPersistenceNotSupportedException; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRRestHelper; import com.ibm.fhir.server.util.RestAuditLogger; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Operation.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Operation.java index 7f79eb330e9..cedd530322e 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Operation.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Operation.java @@ -6,7 +6,7 @@ package com.ibm.fhir.server.resources; -import static com.ibm.fhir.server.util.FHIROperationUtil.checkAndVerifyOperationAllowed; +import static com.ibm.fhir.server.spi.operation.FHIROperationUtil.checkAndVerifyOperationAllowed; import static com.ibm.fhir.server.util.IssueTypeToHttpStatusMapper.issueListToStatus; import java.net.URI; @@ -36,7 +36,7 @@ import com.ibm.fhir.model.resource.OperationOutcome.Issue; import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.util.FHIRUtil; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; import com.ibm.fhir.server.util.FHIRRestHelper; import com.ibm.fhir.server.util.RestAuditLogger; @@ -72,7 +72,7 @@ public Response invoke(@PathParam("operationName") String operationName) { checkAndVerifyOperationAllowed(operationName); FHIROperationContext operationContext = - FHIROperationContext.createSystemOperationContext(); + FHIROperationContext.createSystemOperationContext(operationName); operationContext.setProperty(FHIROperationContext.PROPNAME_URI_INFO, uriInfo); operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_HEADERS, httpHeaders); operationContext.setProperty(FHIROperationContext.PROPNAME_SECURITY_CONTEXT, securityContext); @@ -80,7 +80,7 @@ public Response invoke(@PathParam("operationName") String operationName) { operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.GET); FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); - Resource result = helper.doInvoke(operationContext, null, null, null, operationName, + Resource result = helper.doInvoke(operationContext, null, null, null, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, null, result); status = Response.Status.fromStatusCode(response.getStatus()); @@ -115,7 +115,7 @@ public Response invoke(@PathParam("operationName") String operationName, Resourc checkAndVerifyOperationAllowed(operationName); FHIROperationContext operationContext = - FHIROperationContext.createSystemOperationContext(); + FHIROperationContext.createSystemOperationContext(operationName); operationContext.setProperty(FHIROperationContext.PROPNAME_URI_INFO, uriInfo); operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_HEADERS, httpHeaders); operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, "POST"); @@ -123,7 +123,7 @@ public Response invoke(@PathParam("operationName") String operationName, Resourc operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_REQUEST, httpServletRequest); FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); - Resource result = helper.doInvoke(operationContext, null, null, null, operationName, + Resource result = helper.doInvoke(operationContext, null, null, null, resource, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, null, result); status = Response.Status.fromStatusCode(response.getStatus()); @@ -159,7 +159,7 @@ public Response invokeDelete(@PathParam("operationName") String operationName) { checkInitComplete(); checkAndVerifyOperationAllowed(operationName); - FHIROperationContext operationContext = FHIROperationContext.createSystemOperationContext(); + FHIROperationContext operationContext = FHIROperationContext.createSystemOperationContext(operationName); operationContext.setProperty(FHIROperationContext.PROPNAME_URI_INFO, uriInfo); operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_HEADERS, httpHeaders); operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, "DELETE"); @@ -168,7 +168,7 @@ public Response invokeDelete(@PathParam("operationName") String operationName) { FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); Resource result = - helper.doInvoke(operationContext, null, null, null, operationName, null, uriInfo.getQueryParameters()); + helper.doInvoke(operationContext, null, null, null, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, null, result); status = Response.Status.fromStatusCode(response.getStatus()); return response; @@ -204,7 +204,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, checkAndVerifyOperationAllowed(operationName); FHIROperationContext operationContext = - FHIROperationContext.createResourceTypeOperationContext(); + FHIROperationContext.createResourceTypeOperationContext(operationName); operationContext.setProperty(FHIROperationContext.PROPNAME_URI_INFO, uriInfo); operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_HEADERS, httpHeaders); operationContext.setProperty(FHIROperationContext.PROPNAME_SECURITY_CONTEXT, securityContext); @@ -212,7 +212,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.GET); FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); - Resource result = helper.doInvoke(operationContext, resourceTypeName, null, null, operationName, + Resource result = helper.doInvoke(operationContext, resourceTypeName, null, null, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); status = Response.Status.fromStatusCode(response.getStatus()); @@ -249,7 +249,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, checkAndVerifyOperationAllowed(operationName); FHIROperationContext operationContext = - FHIROperationContext.createResourceTypeOperationContext(); + FHIROperationContext.createResourceTypeOperationContext(operationName); operationContext.setProperty(FHIROperationContext.PROPNAME_URI_INFO, uriInfo); operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_HEADERS, httpHeaders); operationContext.setProperty(FHIROperationContext.PROPNAME_SECURITY_CONTEXT, securityContext); @@ -257,7 +257,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.POST); FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); - Resource result = helper.doInvoke(operationContext, resourceTypeName, null, null, operationName, + Resource result = helper.doInvoke(operationContext, resourceTypeName, null, null, resource, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); status = Response.Status.fromStatusCode(response.getStatus()); @@ -308,7 +308,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, checkAndVerifyOperationAllowed(operationName); FHIROperationContext operationContext = - FHIROperationContext.createInstanceOperationContext(); + FHIROperationContext.createInstanceOperationContext(operationName); operationContext.setProperty(FHIROperationContext.PROPNAME_URI_INFO, uriInfo); operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_HEADERS, httpHeaders); operationContext.setProperty(FHIROperationContext.PROPNAME_SECURITY_CONTEXT, securityContext); @@ -316,7 +316,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.GET); FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); - Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, null, operationName, + Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, null, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); status = Response.Status.fromStatusCode(response.getStatus()); @@ -354,7 +354,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, checkAndVerifyOperationAllowed(operationName); FHIROperationContext operationContext = - FHIROperationContext.createInstanceOperationContext(); + FHIROperationContext.createInstanceOperationContext(operationName); operationContext.setProperty(FHIROperationContext.PROPNAME_URI_INFO, uriInfo); operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_HEADERS, httpHeaders); operationContext.setProperty(FHIROperationContext.PROPNAME_SECURITY_CONTEXT, securityContext); @@ -362,7 +362,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.POST); FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); - Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, null, operationName, + Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, null, resource, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); status = Response.Status.fromStatusCode(response.getStatus()); @@ -401,7 +401,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, checkAndVerifyOperationAllowed(operationName); FHIROperationContext operationContext = - FHIROperationContext.createInstanceOperationContext(); + FHIROperationContext.createInstanceOperationContext(operationName); operationContext.setProperty(FHIROperationContext.PROPNAME_URI_INFO, uriInfo); operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_HEADERS, httpHeaders); operationContext.setProperty(FHIROperationContext.PROPNAME_SECURITY_CONTEXT, securityContext); @@ -409,7 +409,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.GET); FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); - Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, versionId, operationName, + Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, versionId, null, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); status = Response.Status.fromStatusCode(response.getStatus()); @@ -448,7 +448,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, checkAndVerifyOperationAllowed(operationName); FHIROperationContext operationContext = - FHIROperationContext.createInstanceOperationContext(); + FHIROperationContext.createInstanceOperationContext(operationName); operationContext.setProperty(FHIROperationContext.PROPNAME_URI_INFO, uriInfo); operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_HEADERS, httpHeaders); operationContext.setProperty(FHIROperationContext.PROPNAME_SECURITY_CONTEXT, securityContext); @@ -456,7 +456,7 @@ public Response invoke(@PathParam("resourceTypeName") String resourceTypeName, operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, HttpMethod.POST); FHIRRestHelper helper = new FHIRRestHelper(getPersistenceImpl()); - Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, versionId, operationName, + Resource result = helper.doInvoke(operationContext, resourceTypeName, logicalId, versionId, resource, uriInfo.getQueryParameters()); Response response = buildResponse(operationContext, resourceTypeName, result); status = Response.Status.fromStatusCode(response.getStatus()); diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Patch.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Patch.java index e8fe3514d20..753231f1e67 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Patch.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Patch.java @@ -39,7 +39,7 @@ import com.ibm.fhir.path.patch.FHIRPathPatch; import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceNotFoundException; import com.ibm.fhir.server.annotation.PATCH; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRRestHelper; import com.ibm.fhir.server.util.RestAuditLogger; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Read.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Read.java index 960e533b706..aeb72202e5b 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Read.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Read.java @@ -31,7 +31,7 @@ import com.ibm.fhir.core.FHIRMediaType; import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRRestHelper; import com.ibm.fhir.server.util.RestAuditLogger; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Update.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Update.java index f619c13df5e..86b8fb427c2 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/Update.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/Update.java @@ -34,7 +34,7 @@ import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceNotFoundException; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRRestHelper; import com.ibm.fhir.server.util.RestAuditLogger; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/resources/VRead.java b/fhir-server/src/main/java/com/ibm/fhir/server/resources/VRead.java index 61f4a16b1c8..9ddd123344c 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/resources/VRead.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/resources/VRead.java @@ -28,7 +28,7 @@ import com.ibm.fhir.core.FHIRMediaType; import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRRestHelper; import com.ibm.fhir.server.util.RestAuditLogger; diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionBase.java b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionBase.java index 26d358017a2..e50d104b4d1 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionBase.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionBase.java @@ -3,14 +3,13 @@ * * SPDX-License-Identifier: Apache-2.0 */ - + package com.ibm.fhir.server.rest; import java.util.ArrayList; import java.util.List; import com.ibm.fhir.model.resource.OperationOutcome.Issue; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.server.util.FHIRUrlParser; /** @@ -18,10 +17,10 @@ * common functions */ public abstract class FHIRRestInteractionBase implements FHIRRestInteraction { - + // The index of the entry related to this operation in the original bundle private final int entryIndex; - + // Description of the request for logging private final String requestDescription; @@ -33,7 +32,7 @@ public abstract class FHIRRestInteractionBase implements FHIRRestInteraction { // Any warnings collected when processing this entry private final List warnings; - + /** * Protected constructor * @param entryIndex diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionCreate.java b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionCreate.java index 0a168317302..88c8841596b 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionCreate.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionCreate.java @@ -3,24 +3,24 @@ * * SPDX-License-Identifier: Apache-2.0 */ - + package com.ibm.fhir.server.rest; -import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.model.resource.Bundle.Entry; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.model.resource.Resource; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRUrlParser; /** * Represents a FHIR REST CREATE interaction */ public class FHIRRestInteractionCreate extends FHIRRestInteractionResource { - + private final String type; private final String ifNoneExist; private final String localIdentifier; - + /** * Public constructor * @param entryIndex @@ -43,7 +43,7 @@ public FHIRRestInteractionCreate(int entryIndex, FHIRPersistenceEvent event, Ent @Override public void accept(FHIRRestInteractionVisitor visitor) throws Exception { FHIRRestOperationResponse result = visitor.doCreate(getEntryIndex(), getEvent(), getWarnings(), getValidationResponseEntry(), getRequestDescription(), getRequestURL(), getInitialTime(), type, getNewResource(), ifNoneExist, localIdentifier); - + // update the resource so we can use it when called in the next processing phase if (result != null && result.getResource() != null) { setNewResource(result.getResource()); diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionInvoke.java b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionInvoke.java index 5cc2d5baa09..4b06d499112 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionInvoke.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionInvoke.java @@ -3,16 +3,15 @@ * * SPDX-License-Identifier: Apache-2.0 */ - + package com.ibm.fhir.server.rest; import javax.ws.rs.core.MultivaluedMap; import com.ibm.fhir.config.FHIRRequestContext; -import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.model.resource.Bundle.Entry; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.model.resource.Resource; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; import com.ibm.fhir.server.util.FHIRUrlParser; /** @@ -25,7 +24,6 @@ public class FHIRRestInteractionInvoke extends FHIRRestInteractionResource { private final String resourceTypeName; private String logicalId; private String versionId; - private String operationName; private MultivaluedMap queryParameters; /** @@ -40,21 +38,18 @@ public class FHIRRestInteractionInvoke extends FHIRRestInteractionResource { * @param resourceTypeName * @param logicalId * @param versionId - * @param operationName * @param resource * @param queryParameters */ - public FHIRRestInteractionInvoke(int entryIndex, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, - long initialTime, FHIROperationContext operationContext, String method, - String resourceTypeName, String logicalId, String versionId, String operationName, - Resource resource, MultivaluedMap queryParameters) { + public FHIRRestInteractionInvoke(int entryIndex, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, + long initialTime, FHIROperationContext operationContext, String method, String resourceTypeName, String logicalId, + String versionId, Resource resource, MultivaluedMap queryParameters) { super(entryIndex, null, resource, validationResponseEntry, requestDescription, requestURL, initialTime); this.operationContext = operationContext; this.method = method; this.resourceTypeName = resourceTypeName; this.logicalId = logicalId; this.versionId = versionId; - this.operationName = operationName; this.queryParameters = queryParameters; } @@ -68,7 +63,7 @@ public void accept(FHIRRestInteractionVisitor visitor) throws Exception { operationContext.setProperty(FHIROperationContext.PROPNAME_SECURITY_CONTEXT, requestContext.getExtendedOperationProperties(FHIROperationContext.PROPNAME_SECURITY_CONTEXT)); operationContext.setProperty(FHIROperationContext.PROPNAME_HTTP_REQUEST, requestContext.getExtendedOperationProperties(FHIROperationContext.PROPNAME_HTTP_REQUEST)); operationContext.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, method); - - visitor.doInvoke(this.method, getEntryIndex(), getValidationResponseEntry(), getRequestDescription(), getRequestURL(), getInitialTime(), operationContext, resourceTypeName, logicalId, versionId, operationName, getNewResource(), queryParameters); + + visitor.doInvoke(this.method, getEntryIndex(), getValidationResponseEntry(), getRequestDescription(), getRequestURL(), getInitialTime(), operationContext, resourceTypeName, logicalId, versionId, getNewResource(), queryParameters); } } \ No newline at end of file diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionPatch.java b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionPatch.java index ee3549240ef..7285b4c34c7 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionPatch.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionPatch.java @@ -3,13 +3,13 @@ * * SPDX-License-Identifier: Apache-2.0 */ - + package com.ibm.fhir.server.rest; import com.ibm.fhir.model.patch.FHIRPatch; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRUrlParser; /** @@ -24,7 +24,7 @@ public class FHIRRestInteractionPatch extends FHIRRestInteractionResource { private final String searchQueryString; private final boolean skippableUpdate; private final String localIdentifier; - + /** * Public constructor * @param entryIndex @@ -51,10 +51,10 @@ public FHIRRestInteractionPatch(int entryIndex, FHIRPersistenceEvent event, Stri this.skippableUpdate = skippableUpdate; this.localIdentifier = localIdentifier; } - + @Override public void accept(FHIRRestInteractionVisitor visitor) throws Exception { - FHIRRestOperationResponse result = visitor.doPatch(getEntryIndex(), getEvent(), getValidationResponseEntry(), getRequestDescription(), + FHIRRestOperationResponse result = visitor.doPatch(getEntryIndex(), getEvent(), getValidationResponseEntry(), getRequestDescription(), getRequestURL(), getInitialTime(), type, id, getNewResource(), getPrevResource(), patch, ifMatchValue, searchQueryString, skippableUpdate, getWarnings(), localIdentifier); // If the response includes a resource, update our copy so that we can pass to the @@ -63,7 +63,7 @@ public void accept(FHIRRestInteractionVisitor visitor) throws Exception { if (result.getResource() != null) { setNewResource(result.getResource()); } - + if (result.getPrevResource() != null) { setPrevResource(result.getPrevResource()); } diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionResource.java b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionResource.java index 614b347d1b8..3081fc9cd4a 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionResource.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionResource.java @@ -3,21 +3,21 @@ * * SPDX-License-Identifier: Apache-2.0 */ - + package com.ibm.fhir.server.rest; import com.ibm.fhir.model.resource.Bundle.Entry; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.model.resource.Resource; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.server.util.FHIRUrlParser; /** * Base for resource-oriented {@link FHIRRestInteraction} implementations which include a validationResponseEntry. */ public abstract class FHIRRestInteractionResource extends FHIRRestInteractionBase { - + private final Entry validationResponseEntry; - + // The event dispatched at various points while processing this interaction private final FHIRPersistenceEvent event; @@ -50,7 +50,7 @@ protected FHIRRestInteractionResource(int entryIndex, FHIRPersistenceEvent event public Entry getValidationResponseEntry() { return validationResponseEntry; } - + /** * Setter for updatedResource * @param resource @@ -66,7 +66,7 @@ public void setNewResource(Resource resource) { public Resource getNewResource() { return newResource; } - + /** * Setter for prevResource * @param prevResource @@ -74,7 +74,7 @@ public Resource getNewResource() { public void setPrevResource(Resource prevResource) { this.prevResource = prevResource; } - + /** * Get the previous resource * @return diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionUpdate.java b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionUpdate.java index 55f617f2829..4ff28558806 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionUpdate.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionUpdate.java @@ -3,13 +3,13 @@ * * SPDX-License-Identifier: Apache-2.0 */ - + package com.ibm.fhir.server.rest; -import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.model.resource.Bundle.Entry; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.model.resource.Resource; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRUrlParser; /** @@ -19,14 +19,14 @@ public class FHIRRestInteractionUpdate extends FHIRRestInteractionResource { private final String type; private final String id; - private final String ifMatchValue; + private final String ifMatchValue; private final String searchQueryString; private final boolean skippableUpdate; private final String localIdentifier; - + // The deleted flag status when we read the current resource value private boolean deleted; - + /** * Public constructor * @param entryIndex @@ -43,7 +43,7 @@ public class FHIRRestInteractionUpdate extends FHIRRestInteractionResource { * @param skippableUpdate * @param localIdentifier */ - public FHIRRestInteractionUpdate(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, + public FHIRRestInteractionUpdate(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, Resource newResource, String ifMatchValue, String searchQueryString, boolean skippableUpdate, String localIdentifier) { super(entryIndex, event, newResource, validationResponseEntry, requestDescription, requestURL, initialTime); @@ -54,21 +54,21 @@ public FHIRRestInteractionUpdate(int entryIndex, FHIRPersistenceEvent event, Ent this.skippableUpdate = skippableUpdate; this.localIdentifier = localIdentifier; } - + @Override public void accept(FHIRRestInteractionVisitor visitor) throws Exception { - - FHIRRestOperationResponse result = visitor.doUpdate(getEntryIndex(), getEvent(), getValidationResponseEntry(), getRequestDescription(), getRequestURL(), + + FHIRRestOperationResponse result = visitor.doUpdate(getEntryIndex(), getEvent(), getValidationResponseEntry(), getRequestDescription(), getRequestURL(), getInitialTime(), type, id, getNewResource(), getPrevResource(), ifMatchValue, searchQueryString, skippableUpdate, localIdentifier, getWarnings(), deleted); - + // update the resource so we can use it when called in the next processing phase if (result != null) { if (result.getResource() != null) { setNewResource(result.getResource()); this.deleted = result.isDeleted(); } - + if (result.getPrevResource() != null) { setPrevResource(result.getPrevResource()); } diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitor.java b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitor.java index 10dd2dbb84e..c207adde8ce 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitor.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitor.java @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ - + package com.ibm.fhir.server.rest; import java.util.List; @@ -15,9 +15,9 @@ import com.ibm.fhir.model.resource.Bundle.Entry; import com.ibm.fhir.model.resource.OperationOutcome.Issue; import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRUrlParser; /** @@ -48,7 +48,7 @@ public interface FHIRRestInteractionVisitor { FHIRRestOperationResponse doSearch(int entryIndex, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String compartment, String compartmentId, MultivaluedMap queryParameters, String requestUri, Resource contextResource, boolean checkInteractionAllowed) throws Exception; - + /** * Performs a 'vread' operation by retrieving the specified version of a Resource with no query parameters * @@ -62,7 +62,7 @@ FHIRRestOperationResponse doSearch(int entryIndex, String requestDescription, FH * @throws Exception */ FHIRRestOperationResponse doVRead(int entryIndex, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, String versionId, MultivaluedMap queryParameters) throws Exception; - + /** * Performs a 'read' operation to retrieve a Resource. * @@ -83,7 +83,7 @@ FHIRRestOperationResponse doSearch(int entryIndex, String requestDescription, FH */ FHIRRestOperationResponse doRead(int entryIndex, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, boolean throwExcOnNull, boolean includeDeleted, Resource contextResource, MultivaluedMap queryParameters, boolean checkInteractionAllowed) throws Exception; - + /** * Performs the work of retrieving versions of a Resource. * @@ -98,7 +98,7 @@ FHIRRestOperationResponse doRead(int entryIndex, String requestDescription, FHIR * @throws Exception */ FHIRRestOperationResponse doHistory(int entryIndex, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, MultivaluedMap queryParameters, String requestUri) throws Exception; - + /** * Performs the heavy lifting associated with a 'create' interaction. * @@ -116,7 +116,7 @@ FHIRRestOperationResponse doRead(int entryIndex, String requestDescription, FHIR * @throws Exception */ FHIRRestOperationResponse doCreate(int entryIndex, FHIRPersistenceEvent event, List warnings, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, Resource resource, String ifNoneExist, String localIdentifier) throws Exception; - + /** * Performs an update operation (a new version of the Resource will be stored). * @@ -146,10 +146,10 @@ FHIRRestOperationResponse doRead(int entryIndex, String requestDescription, FHIR * @return a FHIRRestOperationResponse that contains the results of the operation * @throws Exception */ - FHIRRestOperationResponse doUpdate(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, + FHIRRestOperationResponse doUpdate(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, Resource newResource, Resource prevResource, String ifMatchValue, String searchQueryString, boolean skippableUpdate, String localIdentifier, List warnings, boolean isDeleted) throws Exception; - + /** * Performs a patch operation (a new version of the Resource will be stored). * @@ -175,14 +175,20 @@ FHIRRestOperationResponse doUpdate(int entryIndex, FHIRPersistenceEvent event, E * @return a FHIRRestOperationResponse that contains the results of the operation * @throws Exception */ - FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, + FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, Resource newResource, Resource prevResource, FHIRPatch patch, String ifMatchValue, String searchQueryString, boolean skippableUpdate, List warnings, String localIdentifier) throws Exception; - + /** * Helper method which invokes a custom operation. * + * @param method + * @param entryIndex + * @param validationResponseEntry + * @param requestDescription + * @param requestUrl + * @param initialTime * @param operationContext * the FHIROperationContext associated with the request * @param resourceTypeName @@ -191,8 +197,6 @@ FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent event, En * the resource logical id associated with the request * @param versionId * the resource version id associated with the request - * @param operationName - * the name of the custom operation to be invoked * @param resource * the input resource associated with the custom operation to be invoked * @param queryParameters @@ -201,10 +205,10 @@ FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent event, En * @return a Resource that represents the response to the custom operation * @throws Exception */ - FHIRRestOperationResponse doInvoke(String method, int entryIndex, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, FHIROperationContext operationContext, String resourceTypeName, - String logicalId, String versionId, String operationName, - Resource resource, MultivaluedMap queryParameters) throws Exception; - + FHIRRestOperationResponse doInvoke(String method, int entryIndex, Entry validationResponseEntry, String requestDescription, + FHIRUrlParser requestURL, long initialTime, FHIROperationContext operationContext, String resourceTypeName, + String logicalId, String versionId, Resource resource, MultivaluedMap queryParameters) throws Exception; + /** * Performs a 'delete' operation on the specified resource. * diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorBase.java b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorBase.java index dedf0980ddf..ab6062636e3 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorBase.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorBase.java @@ -24,10 +24,10 @@ import com.ibm.fhir.model.resource.Bundle.Entry; import com.ibm.fhir.model.type.Uri; import com.ibm.fhir.model.util.ModelSupport; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.model.resource.OperationOutcome; import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; /** * Abstract base class of the {@link FHIRRestInteractionVisitor}. Manages access to the diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorMeta.java b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorMeta.java index 264f82907a5..196ecd616b2 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorMeta.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorMeta.java @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ - + package com.ibm.fhir.server.rest; import static com.ibm.fhir.model.type.String.string; @@ -22,7 +22,11 @@ import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.patch.FHIRPatch; +import com.ibm.fhir.model.resource.Bundle; import com.ibm.fhir.model.resource.Bundle.Entry; +import com.ibm.fhir.model.resource.OperationOutcome; +import com.ibm.fhir.model.resource.OperationOutcome.Issue; +import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.type.CodeableConcept; import com.ibm.fhir.model.type.Instant; import com.ibm.fhir.model.type.Reference; @@ -30,38 +34,34 @@ import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.model.util.CollectingVisitor; import com.ibm.fhir.model.util.FHIRUtil; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceDeletedException; import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceNotFoundException; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.search.SearchConstants; import com.ibm.fhir.search.exception.FHIRSearchException; -import com.ibm.fhir.model.resource.Bundle; -import com.ibm.fhir.model.resource.OperationOutcome; -import com.ibm.fhir.model.resource.OperationOutcome.Issue; -import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.server.exception.FHIRRestBundledRequestException; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers.Interaction; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRUrlParser; import com.ibm.fhir.server.util.IssueTypeToHttpStatusMapper; -import com.ibm.fhir.server.util.FHIRRestHelper.Interaction; /** * Used to prepare bundle entries before they hit the persistence layer. For write operations - * (CREATE, UPDATE, PATCH), the meta phase is responsible for establishing the identity of any + * (CREATE, UPDATE, PATCH), the meta phase is responsible for establishing the identity of any * incoming resource, and updating the meta element accordingly. For other operations it is * currently a NOP, but in the future may be used for optimistic async reads when the payload * has been offloaded to another system. */ public class FHIRRestInteractionVisitorMeta extends FHIRRestInteractionVisitorBase { private static final Logger log = Logger.getLogger(FHIRRestInteractionVisitorPersist.class.getName()); - + private static final boolean DO_VALIDATION = true; - + // True if the bundle is a transaction bundle, false if it's a batch bundle final boolean transaction; - + /** * Public constructor * @param helpers @@ -106,18 +106,18 @@ public FHIRRestOperationResponse doHistory(int entryIndex, String requestDescrip @Override public FHIRRestOperationResponse doCreate(int entryIndex, FHIRPersistenceEvent event, List warnings, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, Resource resource, String ifNoneExist, String localIdentifier) throws Exception { logStart(entryIndex, requestDescription, requestURL); - + // Skip CREATE if validation failed // TODO the logic in the old buildLocalRefMap uses SC_OK_STRING if (validationResponseEntry != null && !validationResponseEntry.getResponse().getStatus().equals(SC_ACCEPTED_STRING)) { return null; } - + // Use doInteraction so we can implement common exception handling in one place return doInteraction(entryIndex, requestDescription, initialTime, () -> { // Validate that interaction is allowed for given resource type helpers.validateInteraction(Interaction.CREATE, type); - + // Inject the meta to the resource after optionally checking for ifNoneExist FHIRRestOperationResponse prepResponse = helpers.doCreateMeta(event, warnings, type, resource, ifNoneExist); if (prepResponse != null) { @@ -125,37 +125,37 @@ public FHIRRestOperationResponse doCreate(int entryIndex, FHIRPersistenceEvent e // means this entry is complete. Entry entry = buildResponseBundleEntry(prepResponse, null, requestDescription, initialTime); setEntryComplete(entryIndex, entry, requestDescription, initialTime); - + if (localIdentifier != null && !localRefMap.containsKey(localIdentifier)) { addLocalRefMapping(localIdentifier, prepResponse.getResource()); } - + return null; } - + // Get the updated resource with the meta info Resource resourceWithMeta = event.getFhirResource(); - + if (this.transaction) { resolveConditionalReferences(resourceWithMeta); } - + final int newVersionNumber = Integer.parseInt(resourceWithMeta.getMeta().getVersionId().getValue()); final Instant lastUpdated = resourceWithMeta.getMeta().getLastUpdated(); final String logicalId = resourceWithMeta.getId(); - + // Add the mapping between localIdentifier and the new logicalId from the resource if (localIdentifier != null && !localRefMap.containsKey(localIdentifier)) { addLocalRefMapping(localIdentifier, resourceWithMeta); } - + // Pass back the updated resource so it can be used in the next phase if required - return new FHIRRestOperationResponse(resourceWithMeta, logicalId, newVersionNumber, lastUpdated, null); + return new FHIRRestOperationResponse(resourceWithMeta, logicalId, null); }); } @Override - public FHIRRestOperationResponse doUpdate(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, + public FHIRRestOperationResponse doUpdate(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, Resource resource, Resource prevResource, String ifMatchValue, String searchQueryString, boolean skippableUpdate, String localIdentifier, List warnings, boolean isDeleted) throws Exception { logStart(entryIndex, requestDescription, requestURL); @@ -176,32 +176,33 @@ public FHIRRestOperationResponse doUpdate(int entryIndex, FHIRPersistenceEvent e Entry entry = buildResponseBundleEntry(metaResponse, null, requestDescription, initialTime); setEntryComplete(entryIndex, entry, requestDescription, initialTime); } - + // Get the updated resource with the meta info Resource resourceWithMeta = metaResponse.getResource(); - + if (this.transaction) { resolveConditionalReferences(resourceWithMeta); } - + // final int newVersionNumber = Integer.parseInt(resourceWithMeta.getMeta().getVersionId().getValue()); // final Instant lastUpdated = resourceWithMeta.getMeta().getLastUpdated(); // final String logicalId = resourceWithMeta.getId(); - + // Add the mapping between localIdentifier and the new logicalId from the resource if (localIdentifier != null && !localRefMap.containsKey(localIdentifier)) { addLocalRefMapping(localIdentifier, resourceWithMeta); } - + // Pass back the updated resource so it can be used in the next phase if required return metaResponse; }); } @Override - public FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, - String type, String id, Resource newResource, Resource prevResource, FHIRPatch patch, String ifMatchValue, String searchQueryString, - boolean skippableUpdate, List warnings, String localIdentifier) throws Exception { + public FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, + String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, Resource newResource, + Resource prevResource, FHIRPatch patch, String ifMatchValue, String searchQueryString, + boolean skippableUpdate, List warnings, String localIdentifier) throws Exception { logStart(entryIndex, requestDescription, requestURL); // Skip PATCH if validation failed // TODO the logic in the old buildLocalRefMap uses SC_OK_STRING @@ -220,27 +221,28 @@ public FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent ev Entry entry = buildResponseBundleEntry(metaResponse, null, requestDescription, initialTime); setEntryComplete(entryIndex, entry, requestDescription, initialTime); } - + // Get the updated resource with the meta info Resource resourceWithMeta = metaResponse.getResource(); - + if (this.transaction) { resolveConditionalReferences(resourceWithMeta); } - + // Add the mapping between localIdentifier and the new logicalId from the resource if (localIdentifier != null && !localRefMap.containsKey(localIdentifier)) { addLocalRefMapping(localIdentifier, resourceWithMeta); } - + // Pass back the updated resource so it can be used in the next phase if required return metaResponse; }); } @Override - public FHIRRestOperationResponse doInvoke(String method, int entryIndex, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, FHIROperationContext operationContext, String resourceTypeName, String logicalId, - String versionId, String operationName, Resource resource, MultivaluedMap queryParameters) throws Exception { + public FHIRRestOperationResponse doInvoke(String method, int entryIndex, Entry validationResponseEntry, String requestDescription, + FHIRUrlParser requestURL, long initialTime, FHIROperationContext operationContext, String resourceTypeName, String logicalId, + String versionId, Resource resource, MultivaluedMap queryParameters) throws Exception { logStart(entryIndex, requestDescription, requestURL); // NOP return null; @@ -258,7 +260,7 @@ public FHIRRestOperationResponse validationResponse(int entryIndex, Entry valida // NOP return null; } - + private void logStart(int entryIndex, String requestDescription, FHIRUrlParser requestURL) { // Log our initial info message for this request. if (log.isLoggable(Level.FINE)) { @@ -276,7 +278,7 @@ public FHIRRestOperationResponse issue(int entryIndex, String requestDescription // NOP return null; } - + /** * Scan the resource and collect any conditional references * @param resource @@ -300,7 +302,7 @@ private Set getConditionalReferences(Resource resource) { } return conditionalReferences; } - + /** * Scan the resource for any conditional references. For each one we find, perform the search * and add the result to the localRefMap. @@ -312,7 +314,7 @@ private void resolveConditionalReferences(Resource resource) throws Exception { if (localRefMap.containsKey(conditionalReference)) { continue; } - + FHIRUrlParser parser = new FHIRUrlParser(conditionalReference); String type = parser.getPathTokens()[0]; @@ -324,7 +326,7 @@ private void resolveConditionalReferences(Resource resource) throws Exception { if (queryParameters.keySet().stream().anyMatch(key -> SearchConstants.SEARCH_RESULT_PARAMETER_NAMES.contains(key))) { throw buildRestException("Invalid conditional reference: only filtering parameters are allowed", IssueType.INVALID); } - + queryParameters.add("_summary", "true"); queryParameters.add("_count", "1"); @@ -344,7 +346,7 @@ private void resolveConditionalReferences(Resource resource) throws Exception { localRefMap.put(conditionalReference, type + "/" + bundle.getEntry().get(0).getResource().getId()); } } - + private FHIROperationException buildRestException(String msg, IssueType issueType) { return buildRestException(msg, issueType, IssueSeverity.FATAL); } @@ -363,7 +365,7 @@ private OperationOutcome.Issue buildOperationOutcomeIssue(IssueSeverity severity .details(CodeableConcept.builder().text(string(msg)).build()) .build(); } - + /** * Get the current time which can be used for the lastUpdated field * @return current time in UTC @@ -435,7 +437,7 @@ private FHIRRestOperationResponse doInteraction(int entryIndex, String requestDe .build(); setEntryComplete(entryIndex, entry, requestDescription, initialTime); } - + return null; } } \ No newline at end of file diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorPersist.java b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorPersist.java index 747a91ab88b..0b9ac0d9b5c 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorPersist.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorPersist.java @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ - + package com.ibm.fhir.server.rest; import static com.ibm.fhir.model.type.String.string; @@ -18,20 +18,20 @@ import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.patch.FHIRPatch; import com.ibm.fhir.model.resource.Bundle; -import com.ibm.fhir.model.resource.OperationOutcome; import com.ibm.fhir.model.resource.Bundle.Entry; +import com.ibm.fhir.model.resource.OperationOutcome; import com.ibm.fhir.model.resource.OperationOutcome.Issue; -import com.ibm.fhir.model.util.FHIRUtil; import com.ibm.fhir.model.resource.Resource; +import com.ibm.fhir.model.util.FHIRUtil; import com.ibm.fhir.persistence.SingleResourceResult; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceDeletedException; import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceNotFoundException; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.search.exception.FHIRSearchException; import com.ibm.fhir.server.exception.FHIRRestBundledRequestException; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRUrlParser; import com.ibm.fhir.server.util.IssueTypeToHttpStatusMapper; @@ -47,7 +47,7 @@ public class FHIRRestInteractionVisitorPersist extends FHIRRestInteractionVisito // True if the bundle type is TRANSACTION final boolean transaction; - + /** * Public constructor * @param helpers @@ -63,10 +63,10 @@ public FHIRRestInteractionVisitorPersist(FHIRResourceHelpers helpers, Map queryParameters, String requestUri, Resource contextResource, boolean checkInteractionAllowed) throws Exception { - + doInteraction(entryIndex, requestDescription, initialTime, () -> { Bundle searchResults = helpers.doSearch(type, compartment, compartmentId, queryParameters, requestUri, contextResource, checkInteractionAllowed); - + return Bundle.Entry.builder() .resource(searchResults) .response(Entry.Response.builder() @@ -74,14 +74,14 @@ public FHIRRestOperationResponse doSearch(int entryIndex, String requestDescript .build()) .build(); }); - + return null; } @Override public FHIRRestOperationResponse doVRead(int entryIndex, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, String versionId, MultivaluedMap queryParameters) throws Exception { - + doInteraction(entryIndex, requestDescription, initialTime, () -> { // Perform the VREAD and return the result entry we want in the response bundle Resource resource = helpers.doVRead(type, id, versionId, queryParameters); @@ -92,7 +92,7 @@ public FHIRRestOperationResponse doVRead(int entryIndex, String requestDescripti .resource(resource) .build(); }); - + return null; } @@ -116,7 +116,7 @@ public FHIRRestOperationResponse doRead(int entryIndex, String requestDescriptio @Override public FHIRRestOperationResponse doHistory(int entryIndex, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, MultivaluedMap queryParameters, String requestUri) throws Exception { - + doInteraction(entryIndex, requestDescription, initialTime, () -> { Bundle bundle = helpers.doHistory(type, id, queryParameters, requestUri); return Entry.builder() @@ -134,39 +134,39 @@ public FHIRRestOperationResponse doCreate(int entryIndex, FHIRPersistenceEvent e doInteraction(entryIndex, requestDescription, initialTime, () -> { FHIRRestOperationResponse ior = helpers.doCreatePersist(event, warnings, resource); - + OperationOutcome validationOutcome = null; if (validationResponseEntry != null && validationResponseEntry.getResponse() != null) { validationOutcome = validationResponseEntry.getResponse().getOutcome().as(OperationOutcome.class); } - + return buildResponseBundleEntry(ior, validationOutcome, requestDescription, initialTime); }); - + // No need to return anything. doOperation injects the entry to the responseBundle return null; } @Override - public FHIRRestOperationResponse doUpdate(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, + public FHIRRestOperationResponse doUpdate(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, Resource newResource, Resource prevResource, String ifMatchValue, String searchQueryString, boolean skippableUpdate, String localIdentifier, List warnings, boolean isDeleted) throws Exception { doInteraction(entryIndex, requestDescription, initialTime, () -> { - + FHIRRestOperationResponse ior = helpers.doPatchOrUpdatePersist(event, type, id, false, newResource, prevResource, warnings, isDeleted); OperationOutcome validationOutcome = null; if (validationResponseEntry != null && validationResponseEntry.getResponse() != null) { validationOutcome = validationResponseEntry.getResponse().getOutcome().as(OperationOutcome.class); } - return buildResponseBundleEntry(ior, validationOutcome, requestDescription, initialTime); + return buildResponseBundleEntry(ior, validationOutcome, requestDescription, initialTime); }); - + return null; } @Override - public FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, + public FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, Resource newResource, Resource prevResource, FHIRPatch patch, String ifMatchValue, String searchQueryString, boolean skippableUpdate, List warnings, String localIdentifier) throws Exception { @@ -174,7 +174,7 @@ public FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent ev // Note that the patch will have already been applied to the resource...so this is // really just an update as far as the persistence layer is concerned doInteraction(entryIndex, requestDescription, initialTime, () -> { - FHIRRestOperationResponse ior = helpers.doPatchOrUpdatePersist(event, type, id, true, newResource, prevResource, + FHIRRestOperationResponse ior = helpers.doPatchOrUpdatePersist(event, type, id, true, newResource, prevResource, warnings, false); OperationOutcome validationOutcome = null; if (validationResponseEntry != null && validationResponseEntry.getResponse() != null) { @@ -182,16 +182,17 @@ public FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent ev } return buildResponseBundleEntry(ior, validationOutcome, requestDescription, initialTime); }); - + return null; } @Override - public FHIRRestOperationResponse doInvoke(String method, int entryIndex, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, FHIROperationContext operationContext, String resourceTypeName, String logicalId, - String versionId, String operationName, Resource resource, MultivaluedMap queryParameters) throws Exception { - + public FHIRRestOperationResponse doInvoke(String method, int entryIndex, Entry validationResponseEntry, String requestDescription, + FHIRUrlParser requestURL, long initialTime, FHIROperationContext operationContext, String resourceTypeName, String logicalId, + String versionId, Resource resource, MultivaluedMap queryParameters) throws Exception { + doInteraction(entryIndex, requestDescription, initialTime, () -> { - Resource response = helpers.doInvoke(operationContext, resourceTypeName, logicalId, versionId, operationName, resource, queryParameters); + Resource response = helpers.doInvoke(operationContext, resourceTypeName, logicalId, versionId, resource, queryParameters); return Entry.builder() .response(Entry.Response.builder() .status(SC_OK_STRING) @@ -199,7 +200,7 @@ public FHIRRestOperationResponse doInvoke(String method, int entryIndex, Entry v .resource(response) .build(); }); - + return null; } @@ -208,10 +209,10 @@ public FHIRRestOperationResponse doDelete(int entryIndex, String requestDescript doInteraction(entryIndex, requestDescription, initialTime, () -> { FHIRRestOperationResponse ior = helpers.doDelete(type, id, searchQueryString); - + int httpStatus = ior.getStatus().getStatusCode(); OperationOutcome oo = ior.getOperationOutcome(); - + return Entry.builder() .response(Entry.Response.builder() .status(string(Integer.toString(httpStatus))) @@ -219,7 +220,7 @@ public FHIRRestOperationResponse doDelete(int entryIndex, String requestDescript .build()) .build(); }); - + return null; } @@ -228,7 +229,7 @@ public FHIRRestOperationResponse validationResponse(int entryIndex, Entry valida setEntryComplete(entryIndex, validationResponseEntry, requestDescription, initialTime); return null; } - + @Override public FHIRRestOperationResponse issue(int entryIndex, String requestDescription, long initialTime, Status status, Entry responseEntry) throws Exception { setEntryComplete(entryIndex, responseEntry, requestDescription, initialTime); @@ -248,7 +249,7 @@ private void doInteraction(int entryIndex, String requestDescription, long initi // To make it clear, we fail immediately on exception if we're processing bundleType == TRANSACTION final boolean failFast = transaction; try { - // If we already have a response entry for the given index then the entry has already + // If we already have a response entry for the given index then the entry has already // completed or failed in a previous phase so we skip it here if (getResponseEntry(entryIndex) == null) { Entry entry = v.call(); diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorReferenceMapping.java b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorReferenceMapping.java index 683a9d8757e..c8b6baac7f1 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorReferenceMapping.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/rest/FHIRRestInteractionVisitorReferenceMapping.java @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: Apache-2.0 */ - + package com.ibm.fhir.server.rest; import static com.ibm.fhir.model.type.String.string; @@ -19,20 +19,19 @@ import com.ibm.fhir.exception.FHIROperationException; import com.ibm.fhir.model.patch.FHIRPatch; import com.ibm.fhir.model.resource.Bundle.Entry; -import com.ibm.fhir.model.type.Instant; +import com.ibm.fhir.model.resource.OperationOutcome.Issue; +import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.util.FHIRUtil; import com.ibm.fhir.model.util.ReferenceMappingVisitor; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceDeletedException; import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceNotFoundException; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.persistence.payload.PayloadKey; import com.ibm.fhir.search.exception.FHIRSearchException; -import com.ibm.fhir.model.resource.OperationOutcome.Issue; -import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.server.exception.FHIRRestBundledRequestException; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRUrlParser; import com.ibm.fhir.server.util.IssueTypeToHttpStatusMapper; @@ -40,10 +39,10 @@ * Visitor used to update references in an incoming resource prior to persistence */ public class FHIRRestInteractionVisitorReferenceMapping extends FHIRRestInteractionVisitorBase { - + // True if there's a bundle-level transaction, null otherwise final boolean transaction; - + /** * Public constructor * @param helpers @@ -83,67 +82,66 @@ public FHIRRestOperationResponse doHistory(int entryIndex, String requestDescrip @Override public FHIRRestOperationResponse doCreate(int entryIndex, FHIRPersistenceEvent event, List warnings, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, Resource resource, String ifNoneExist, String localIdentifier) throws Exception { - + // Use doOperation so we can implement common exception handling in one place return doOperation(entryIndex, requestDescription, initialTime, () -> { - + // Convert any local references found within the resource to their corresponding external reference. ReferenceMappingVisitor visitor = new ReferenceMappingVisitor(localRefMap); resource.accept(visitor); final Resource finalResource = visitor.getResult(); // finalResource immutable - + // Try offloading storage of the payload. The offloadResponse will be null if not supported int newVersionNumber = Integer.parseInt(finalResource.getMeta().getVersionId().getValue()); - Instant lastUpdated = finalResource.getMeta().getLastUpdated(); Future offloadResponse = storePayload(finalResource, finalResource.getId(), newVersionNumber); - + // Pass back the updated resource so it can be used in the next phase if required - return new FHIRRestOperationResponse(finalResource, finalResource.getId(), newVersionNumber, lastUpdated, offloadResponse); + return new FHIRRestOperationResponse(finalResource, finalResource.getId(), offloadResponse); }); } @Override - public FHIRRestOperationResponse doUpdate(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, + public FHIRRestOperationResponse doUpdate(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, Resource resource, Resource prevResource, String ifMatchValue, String searchQueryString, boolean skippableUpdate, String localIdentifier, List warnings, boolean isDeleted) throws Exception { // Use doOperation for common exception handling return doOperation(entryIndex, requestDescription, initialTime, () -> { - + // Convert any local references found within the resource to their corresponding external reference. ReferenceMappingVisitor visitor = new ReferenceMappingVisitor(localRefMap); resource.accept(visitor); Resource newResource = visitor.getResult(); - + if (localIdentifier != null && localRefMap.get(localIdentifier) == null) { addLocalRefMapping(localIdentifier, newResource); } - + // TODO support payload offload here - + // Pass back the updated resource so it can be used in the next phase return new FHIRRestOperationResponse(null, null, newResource); }); } @Override - public FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, + public FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent event, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, String type, String id, Resource resource, Resource prevResource, FHIRPatch patch, String ifMatchValue, String searchQueryString, boolean skippableUpdate, List warnings, String localIdentifier) throws Exception { // Use doOperation for common exception handling return doOperation(entryIndex, requestDescription, initialTime, () -> { - + // Convert any local references found within the resource to their corresponding external reference. ReferenceMappingVisitor visitor = new ReferenceMappingVisitor(localRefMap); resource.accept(visitor); Resource newResource = visitor.getResult(); - + if (localIdentifier != null && localRefMap.get(localIdentifier) == null) { addLocalRefMapping(localIdentifier, newResource); } - + // TODO support payload offload here - + // Pass back the updated resource so it can be used in the next phase return new FHIRRestOperationResponse(null, null, newResource); }); @@ -151,7 +149,7 @@ public FHIRRestOperationResponse doPatch(int entryIndex, FHIRPersistenceEvent ev @Override public FHIRRestOperationResponse doInvoke(String method, int entryIndex, Entry validationResponseEntry, String requestDescription, FHIRUrlParser requestURL, long initialTime, FHIROperationContext operationContext, String resourceTypeName, String logicalId, - String versionId, String operationName, Resource resource, MultivaluedMap queryParameters) throws Exception { + String versionId, Resource resource, MultivaluedMap queryParameters) throws Exception { // NOP return null; } @@ -167,13 +165,13 @@ public FHIRRestOperationResponse validationResponse(int entryIndex, Entry valida // NOP return null; } - + @Override public FHIRRestOperationResponse issue(int entryIndex, String requestDescription, long initialTime, Status status, Entry responseEntry) throws Exception { // NOP return null; } - + /** * If payload offloading is supported by the persistence layer, store the given resource. This * can be an async operation which we resolve at the end just prior to the transaction being @@ -185,9 +183,9 @@ public FHIRRestOperationResponse issue(int entryIndex, String requestDescription * @return */ protected Future storePayload(Resource resource, String logicalId, int newVersionNumber) throws Exception { - return helpers.storePayload(resource, logicalId, newVersionNumber); + return helpers.storePayload(resource, logicalId, newVersionNumber); } - + /** * Unified exception handling for each of the operation calls * @param entryIndex @@ -249,7 +247,7 @@ private FHIRRestOperationResponse doOperation(int entryIndex, String requestDesc .build(); setEntryComplete(entryIndex, entry, requestDescription, initialTime); } - + return null; } } \ No newline at end of file diff --git a/fhir-server/src/main/java/com/ibm/fhir/server/util/FHIRRestBundleHelper.java b/fhir-server/src/main/java/com/ibm/fhir/server/util/FHIRRestBundleHelper.java index d9f56a8891c..12245f784d7 100644 --- a/fhir-server/src/main/java/com/ibm/fhir/server/util/FHIRRestBundleHelper.java +++ b/fhir-server/src/main/java/com/ibm/fhir/server/util/FHIRRestBundleHelper.java @@ -44,11 +44,9 @@ import com.ibm.fhir.model.util.FHIRUtil; import com.ibm.fhir.model.util.ModelSupport; import com.ibm.fhir.path.patch.FHIRPathPatch; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.search.exception.FHIRSearchException; import com.ibm.fhir.server.exception.FHIRRestBundledRequestException; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; import com.ibm.fhir.server.rest.FHIRRestInteraction; import com.ibm.fhir.server.rest.FHIRRestInteractionCreate; import com.ibm.fhir.server.rest.FHIRRestInteractionDelete; @@ -61,6 +59,8 @@ import com.ibm.fhir.server.rest.FHIRRestInteractionUpdate; import com.ibm.fhir.server.rest.FHIRRestInteractionVRead; import com.ibm.fhir.server.rest.FHIRRestInteractionValidationResponse; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; /** * Helper for processing bundle entries. Does not perform any persistence operations, @@ -73,7 +73,7 @@ public class FHIRRestBundleHelper { private static final String LOCAL_REF_PREFIX = "urn:"; private static final com.ibm.fhir.model.type.String SC_ACCEPTED_STRING = string(Integer.toString(SC_ACCEPTED)); - + // Constant for indicating the need to validate a resource public static final boolean DO_VALIDATION = true; // Constant for indicating whether an update can be skipped when the requested update resource matches the existing one @@ -92,7 +92,7 @@ public class FHIRRestBundleHelper { // Access to helper functions for creating event objects private final FHIRResourceHelpers helpers; - + /** * Public constructor * @param helpers @@ -134,7 +134,7 @@ private FHIROperationException buildRestException(String msg, IssueType issueTyp /** * Construct a FHIROperationException configured with an issue defined - * using the severity, issueType and msg parameters + * using the severity, issueType and msg parameters * @param msg * @param issueType * @param severity @@ -180,7 +180,7 @@ private OperationOutcome.Issue buildOperationOutcomeIssue(IssueSeverity severity public List translateBundleEntries(Bundle requestBundle, Map validationResponseEntries, boolean failFast, String bundleRequestCorrelationId, boolean skippableUpdates) throws Exception { log.entering(this.getClass().getName(), "translateBundleEntries"); - + // The list of operations to execute, in the order we want to execute them List result = new ArrayList<>(requestBundle.getEntry().size()); try { @@ -229,7 +229,7 @@ public List translateBundleEntries(Bundle requestBundle, Ma "PATCH" + requestEntriesByMethod.get(HTTPVerb.Value.PATCH) + ", " + "HEAD" + requestEntriesByMethod.get(HTTPVerb.Value.HEAD)); } - + // Translate the individual bundle entry requests into corresponding FHIRRestOperation implementations for (Map.Entry> methodIndices : requestEntriesByMethod.entrySet()) { HTTPVerb.Value httpMethod = methodIndices.getKey(); @@ -295,7 +295,7 @@ public List translateBundleEntries(Bundle requestBundle, Ma // Internal error, should not get here! throw new IllegalStateException("Internal Server Error: reached an unexpected code location."); } - + result.add(operation); } catch (FHIROperationException e) { if (failFast) { @@ -311,7 +311,7 @@ public List translateBundleEntries(Bundle requestBundle, Ma } else { status = IssueTypeToHttpStatusMapper.issueListToStatus(e.getIssues()); } - + Entry issue = Entry.builder() .resource(FHIRUtil.buildOperationOutcome(e, false)) @@ -319,7 +319,7 @@ public List translateBundleEntries(Bundle requestBundle, Ma .status(string(Integer.toString(status.getStatusCode()))) .build()) .build(); - + // Record the issue so that it can be added to the response bundle later result.add(new FHIRRestInteractionIssue(entryIndex, initialTime, status, issue)); } @@ -328,11 +328,11 @@ public List translateBundleEntries(Bundle requestBundle, Ma log.finer("Finished translation for method: " + httpMethod); } } // end foreach method - + } finally { log.exiting(this.getClass().getName(), "processEntriesForMethod"); } - + return result; } @@ -386,14 +386,14 @@ private FHIRRestInteraction processEntryForPatch(Entry requestEntry, FHIRUrlPars Parameters parameters = requestEntry.getResource().as(Parameters.class); FHIRPatch patch = FHIRPathPatch.from(parameters); - + // Extract the local identifier which may be used by other resources in the bundle to reference this resource String localIdentifier = retrieveLocalIdentifier(requestEntry); // Build the event we'll use when executing the interaction command // - the resource gets injected later when we have it FHIRPersistenceEvent event = new FHIRPersistenceEvent(null, helpers.buildPersistenceEventProperties(resourceType, resourceId, null, null)); - + // We don't perform the actual operation here, just generate the command // we want to execute later return new FHIRRestInteractionPatch(entryIndex, event, requestDescription, requestURL, initialTime, resourceType, resourceId, patch, null, null, skippableUpdate, localIdentifier); @@ -435,18 +435,18 @@ private FHIRRestInteraction processEntryForGet(int entryIndex, Entry.Request ent FHIROperationContext operationContext; switch (pathTokens.length) { case 1: - operationContext = FHIROperationContext.createSystemOperationContext(); - result = new FHIRRestInteractionInvoke(entryIndex, validationResponseEntry, requestDescription, requestURL, initialTime, operationContext, GET, null, null, null, operationName, null, queryParams); + operationContext = FHIROperationContext.createSystemOperationContext(operationName); + result = new FHIRRestInteractionInvoke(entryIndex, validationResponseEntry, requestDescription, requestURL, initialTime, operationContext, GET, null, null, null, null, queryParams); break; case 2: checkResourceType(pathTokens[0]); - operationContext = FHIROperationContext.createResourceTypeOperationContext(); - result = new FHIRRestInteractionInvoke(entryIndex, validationResponseEntry, requestDescription, requestURL, initialTime, operationContext, GET, pathTokens[0], null, null, operationName, null, queryParams); + operationContext = FHIROperationContext.createResourceTypeOperationContext(operationName); + result = new FHIRRestInteractionInvoke(entryIndex, validationResponseEntry, requestDescription, requestURL, initialTime, operationContext, GET, pathTokens[0], null, null, null, queryParams); break; case 3: checkResourceType(pathTokens[0]); - operationContext = FHIROperationContext.createInstanceOperationContext(); - result = new FHIRRestInteractionInvoke(entryIndex, validationResponseEntry, requestDescription, requestURL, initialTime, operationContext, GET, pathTokens[0], pathTokens[1], null, operationName, null, queryParams); + operationContext = FHIROperationContext.createInstanceOperationContext(operationName); + result = new FHIRRestInteractionInvoke(entryIndex, validationResponseEntry, requestDescription, requestURL, initialTime, operationContext, GET, pathTokens[0], pathTokens[1], null, null, queryParams); break; default: String msg = "Invalid URL for custom operation '" + pathTokens[pathTokens.length - 1] + "'"; @@ -531,18 +531,18 @@ private FHIRRestInteraction processEntryForPost(Entry requestEntry, Entry valida final String POST = "POST"; switch (pathTokens.length) { case 1: - operationContext = FHIROperationContext.createSystemOperationContext(); - result = new FHIRRestInteractionInvoke(entryIndex, validationResponseEntry, requestDescription, requestURL, initialTime, operationContext, POST, null, null, null, operationName, resource, queryParams); + operationContext = FHIROperationContext.createSystemOperationContext(operationName); + result = new FHIRRestInteractionInvoke(entryIndex, validationResponseEntry, requestDescription, requestURL, initialTime, operationContext, POST, null, null, null, resource, queryParams); break; case 2: checkResourceType(pathTokens[0]); - operationContext = FHIROperationContext.createResourceTypeOperationContext(); - result = new FHIRRestInteractionInvoke(entryIndex, validationResponseEntry, requestDescription, requestURL, initialTime, operationContext, POST, pathTokens[0], null, null, operationName, resource, queryParams); + operationContext = FHIROperationContext.createResourceTypeOperationContext(operationName); + result = new FHIRRestInteractionInvoke(entryIndex, validationResponseEntry, requestDescription, requestURL, initialTime, operationContext, POST, pathTokens[0], null, null, resource, queryParams); break; case 3: checkResourceType(pathTokens[0]); - operationContext = FHIROperationContext.createInstanceOperationContext(); - result = new FHIRRestInteractionInvoke(entryIndex, validationResponseEntry, requestDescription, requestURL, initialTime, operationContext, POST, pathTokens[0], pathTokens[1], null, operationName, resource, queryParams); + operationContext = FHIROperationContext.createInstanceOperationContext(operationName); + result = new FHIRRestInteractionInvoke(entryIndex, validationResponseEntry, requestDescription, requestURL, initialTime, operationContext, POST, pathTokens[0], pathTokens[1], null, resource, queryParams); break; default: String msg = "Invalid URL for custom operation '" + pathTokens[pathTokens.length - 1] + "'"; @@ -569,20 +569,20 @@ private FHIRRestInteraction processEntryForPost(Entry requestEntry, Entry valida // Since 1869 we no longer use pre-generated identifiers. Identifiers are always // assigned during the meta-processing loop. // String resourceId = retrieveGeneratedIdentifier(localRefMap, localIdentifier); - + // Build the CREATE interaction Entry.Request request = requestEntry.getRequest(); String ifNoneExist = request.getIfNoneExist() != null && request.getIfNoneExist().getValue() != null && !request.getIfNoneExist().getValue().isEmpty() ? request.getIfNoneExist().getValue() : null; - + if (log.isLoggable(Level.FINE)) { log.fine("Creating CREATE interaction for bundle entry[" + entryIndex + "]: " + requestDescription + "; validationResponseEntry: " + validationResponseEntry); } - - // Create the event + + // Create the event FHIRPersistenceEvent event = new FHIRPersistenceEvent(resource, helpers.buildPersistenceEventProperties(resource.getClass().getSimpleName(), null, null, null)); @@ -591,7 +591,7 @@ private FHIRRestInteraction processEntryForPost(Entry requestEntry, Entry valida String msg = "Request URL for bundled create requests should have a path with exactly one token ()."; throw buildRestException(msg, IssueType.NOT_FOUND); } - + return result; } @@ -661,16 +661,16 @@ private FHIRRestInteraction processEntryForPut(Entry requestEntry, Entry validat // Extract the local identifier which may be used by other resources in the bundle to reference this resource String localIdentifier = retrieveLocalIdentifier(requestEntry); - + // Build the UPDATE interaction command if (log.isLoggable(Level.FINE)) { log.fine("Creating UPDATE interaction for bundle entry[" + entryIndex + "]: " + requestDescription + "; validationResponseEntry: " + validationResponseEntry); } - + // Create the event we'll use for this resource interaction FHIRPersistenceEvent event = new FHIRPersistenceEvent(resource, helpers.buildPersistenceEventProperties(type, id, null, null)); - result = new FHIRRestInteractionUpdate(entryIndex, event, validationResponseEntry, requestDescription, requestURL, initialTime, + result = new FHIRRestInteractionUpdate(entryIndex, event, validationResponseEntry, requestDescription, requestURL, initialTime, type, id, resource, ifMatchBundleValue, requestURL.getQuery(), skippableUpdate, localIdentifier); return result; @@ -721,7 +721,7 @@ private FHIRRestInteraction processEntryForDelete(int entryIndex, FHIRUrlParser result = new FHIRRestInteractionDelete(entryIndex, requestDescription, requestURL, initialTime, type, id, requestURL.getQuery()); return result; } - + /** * Check that the resource type is a valid type * @param type the resource type name 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 cefc0d5b64c..307ab6eb562 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 @@ -90,14 +90,13 @@ import com.ibm.fhir.persistence.context.FHIRHistoryContext; import com.ibm.fhir.persistence.context.FHIRPersistenceContext; import com.ibm.fhir.persistence.context.FHIRPersistenceContextFactory; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.persistence.context.FHIRSystemHistoryContext; import com.ibm.fhir.persistence.erase.EraseDTO; import com.ibm.fhir.persistence.exception.FHIRPersistenceException; import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceDeletedException; import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceNotFoundException; import com.ibm.fhir.persistence.helper.FHIRTransactionHelper; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; -import com.ibm.fhir.persistence.interceptor.impl.FHIRPersistenceInterceptorMgr; import com.ibm.fhir.persistence.jdbc.exception.FHIRPersistenceDataAccessException; import com.ibm.fhir.persistence.payload.PayloadKey; import com.ibm.fhir.persistence.payload.PayloadPersistenceHelper; @@ -112,15 +111,17 @@ import com.ibm.fhir.search.util.ReferenceValue; import com.ibm.fhir.search.util.ReferenceValue.ReferenceType; import com.ibm.fhir.search.util.SearchUtil; +import com.ibm.fhir.server.interceptor.FHIRPersistenceInterceptorMgr; import com.ibm.fhir.server.operation.FHIROperationRegistry; -import com.ibm.fhir.server.operation.spi.FHIROperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; import com.ibm.fhir.server.rest.FHIRRestInteraction; import com.ibm.fhir.server.rest.FHIRRestInteractionVisitorMeta; import com.ibm.fhir.server.rest.FHIRRestInteractionVisitorPersist; import com.ibm.fhir.server.rest.FHIRRestInteractionVisitorReferenceMapping; +import com.ibm.fhir.server.spi.operation.FHIROperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.validation.FHIRValidator; import com.ibm.fhir.validation.exception.FHIRValidationException; @@ -166,11 +167,11 @@ public class FHIRRestHelper implements FHIRResourceHelpers { public FHIRRestHelper(FHIRPersistence persistence) { this.persistence = persistence; } - + @Override public FHIRRestOperationResponse doCreate(String type, Resource resource, String ifNoneExist, boolean doValidation) throws Exception { - + // Validate that interaction is allowed for given resource type validateInteraction(Interaction.CREATE, type); @@ -186,19 +187,19 @@ public FHIRRestOperationResponse doCreate(String type, Resource resource, String // Prepare the persistence event FHIRPersistenceEvent event = new FHIRPersistenceEvent(resource, buildPersistenceEventProperties(type, null, null, null)); - + // Run the meta phase to handle ifNoneExist and update the resource meta-data response = doCreateMeta(event, warnings, type, resource, ifNoneExist); - + // If we get a response back from doCreateMeta it means conditional create found // a match so we can skip further processing if (response == null) { // Persistence event processing may modify the resource, so make sure we have the latest value resource = event.getFhirResource(); - + int newVersionNumber = Integer.parseInt(resource.getMeta().getVersionId().getValue()); Future offloadResponse = storePayload(resource, resource.getId(), newVersionNumber); - + // Resolve the future so that we know the payload has been stored // TODO tie this into the transaction data so that we can clean up // more if there's a rollback for another reason. @@ -209,7 +210,7 @@ public FHIRRestOperationResponse doCreate(String type, Resource resource, String throw new FHIRPersistenceException("Payload offload failure. Check server logs for details."); } } - + // At this point, we can be sure the transaction must have been started, so always commit txn.commit(); txn = null; @@ -220,12 +221,12 @@ public FHIRRestOperationResponse doCreate(String type, Resource resource, String txn.rollback(); } } - + return response; } @Override - public FHIRRestOperationResponse doCreateMeta(FHIRPersistenceEvent event, List warnings, String type, Resource resource, + public FHIRRestOperationResponse doCreateMeta(FHIRPersistenceEvent event, List warnings, String type, Resource resource, String ifNoneExist) throws Exception { log.entering(this.getClass().getName(), "doCreateMeta"); @@ -284,7 +285,7 @@ public FHIRRestOperationResponse doCreateMeta(FHIRPersistenceEvent event, List warnings, Resource resource) throws Exception { log.entering(this.getClass().getName(), "doCreatePersist"); @@ -332,7 +333,7 @@ public FHIRRestOperationResponse doCreatePersist(FHIRPersistenceEvent event, Lis // Save the current request context. FHIRRequestContext requestContext = FHIRRequestContext.get(); - + // We'll only start a new transaction here if we don't have one. We'll only // commit at the end if we started one here FHIRTransactionHelper txn = new FHIRTransactionHelper(getTransaction()); @@ -383,7 +384,7 @@ public FHIRRestOperationResponse doPatch(String type, String id, FHIRPatch patch // Validate that interaction is allowed for given resource type validateInteraction(Interaction.PATCH, type); - + try { return doPatchOrUpdate(type, id, patch, null, ifMatchValue, searchQueryString, skippableUpdate, DO_VALIDATION); } finally { @@ -395,7 +396,7 @@ public FHIRRestOperationResponse doPatch(String type, String id, FHIRPatch patch public FHIRRestOperationResponse doUpdate(String type, String id, Resource newResource, String ifMatchValue, String searchQueryString, boolean skippableUpdate, boolean doValidation) throws Exception { log.entering(this.getClass().getName(), "doUpdate"); - + // Validate that interaction is allowed for given resource type validateInteraction(Interaction.UPDATE, type); @@ -422,7 +423,7 @@ private FHIRRestOperationResponse doPatchOrUpdate(String type, String id, FHIRPa String searchQueryString, boolean skippableUpdate, boolean doValidation) throws Exception { FHIRTransactionHelper txn = new FHIRTransactionHelper(getTransaction()); txn.begin(); - + // Save the current request context. FHIRRequestContext requestContext = FHIRRequestContext.get(); try { @@ -436,13 +437,13 @@ private FHIRRestOperationResponse doPatchOrUpdate(String type, String id, FHIRPa txn = null; return metaResponse; } - + // Persist the resource FHIRRestOperationResponse ior = doPatchOrUpdatePersist(event, type, id, patch != null, metaResponse.getResource(), metaResponse.getPrevResource(), warnings, metaResponse.isDeleted()); - + txn.commit(); txn = null; - + return ior; } finally { // Restore the original request context. @@ -455,15 +456,15 @@ private FHIRRestOperationResponse doPatchOrUpdate(String type, String id, FHIRPa log.exiting(this.getClass().getName(), "doUpdate"); } - + } @Override - public FHIRRestOperationResponse doUpdateMeta(FHIRPersistenceEvent event, String type, String id, FHIRPatch patch, Resource newResource, - String ifMatchValue, String searchQueryString, boolean skippableUpdate, boolean doValidation, + public FHIRRestOperationResponse doUpdateMeta(FHIRPersistenceEvent event, String type, String id, FHIRPatch patch, Resource newResource, + String ifMatchValue, String searchQueryString, boolean skippableUpdate, boolean doValidation, List warnings) throws Exception { log.entering(this.getClass().getName(), "doUpdateMeta"); - + // Do everything we need to get the resource ready for storage. This includes handling // update-or-create, conditionals, and the update to the resource meta fields. This // is all part of the first phase - for bundle-processing, the second phase will handle @@ -544,7 +545,7 @@ public FHIRRestOperationResponse doUpdateMeta(FHIRPersistenceEvent event, String // If we found a single match, then we'll perform a normal update on the matched resource. ior.setPrevResource(responseBundle.getEntry().get(0).getResource()); id = ior.getPrevResource().getId(); - + if (id == null) { // This should never happen, but we protect against it to avoid propagating the issue String msg = "Search result resource 'id' attribute is null."; @@ -691,10 +692,10 @@ public FHIRRestOperationResponse doUpdateMeta(FHIRPersistenceEvent event, String final com.ibm.fhir.model.type.Instant lastUpdated = com.ibm.fhir.model.type.Instant.now(ZoneOffset.UTC); final int newVersionNumber = updateCreate ? 1 : Integer.parseInt(ior.getPrevResource().getMeta().getVersionId().getValue()) + 1; newResource = FHIRPersistenceUtil.copyAndSetResourceMetaFields(newResource, newResource.getId(), newVersionNumber, lastUpdated); - + ior.setResource(newResource); ior.setDeleted(isDeleted); - + // That's it for now - persistence is done later return ior; } finally { @@ -704,7 +705,7 @@ public FHIRRestOperationResponse doUpdateMeta(FHIRPersistenceEvent event, String log.exiting(this.getClass().getName(), "doUpdateMeta"); } } - + @Override public FHIRRestOperationResponse doPatchOrUpdatePersist(FHIRPersistenceEvent event, String type, String id, boolean isPatch, Resource newResource, Resource prevResource, @@ -728,7 +729,7 @@ public FHIRRestOperationResponse doPatchOrUpdatePersist(FHIRPersistenceEvent eve // Remember, update now doesn't mutate the resource in any way, and nor should the event checkIdAndMeta(newResource); - + FHIRPersistenceContext persistenceContext = FHIRPersistenceContextFactory.createPersistenceContext(event); boolean updateCreate = (prevResource == null); @@ -739,11 +740,11 @@ public FHIRRestOperationResponse doPatchOrUpdatePersist(FHIRPersistenceEvent eve } else { result = persistence.updateWithMeta(persistenceContext, newResource); } - + if (result.isSuccess() && result.getOutcome() != null) { warnings.addAll(result.getOutcome().getIssue()); } - + // Since 1869 the persistence layer no longer modifies the resource, so we use the original value here ior.setResource(newResource); ior.setOperationOutcome(FHIRUtil.buildOperationOutcome(warnings)); @@ -771,7 +772,7 @@ public FHIRRestOperationResponse doPatchOrUpdatePersist(FHIRPersistenceEvent eve if (isDeleted && ior.getStatus() == Response.Status.OK) { ior.setStatus(Response.Status.CREATED); } - + // Commit our transaction if we started one before. txn.commit(); txn = null; @@ -789,7 +790,7 @@ public FHIRRestOperationResponse doPatchOrUpdatePersist(FHIRPersistenceEvent eve log.exiting(this.getClass().getName(), "doPatchOrUpdatePersist"); } } - + /** * Check that the id and meta fields in the resource have been set up * @param resource @@ -799,16 +800,16 @@ private void checkIdAndMeta(Resource resource) throws FHIRPersistenceException { if (resource.getId() == null || resource.getId().isEmpty()) { throw new FHIRPersistenceException("resource id field not set"); } - + if (resource.getMeta() == null) { throw new FHIRPersistenceException("resource meta is missing"); } - + if (resource.getMeta().getVersionId() == null || resource.getMeta().getVersionId().getValue() == null || resource.getMeta().getVersionId().getValue().isEmpty()) { throw new FHIRPersistenceException("resource meta.versionId not set"); } - + if (resource.getMeta().getLastUpdated() == null) { throw new FHIRPersistenceException("resource meta.lastUpdated not set"); } @@ -1328,8 +1329,6 @@ public Bundle doSearch(String type, String compartment, String compartmentId, * the resource logical id associated with the request * @param versionId * the resource version id associated with the request - * @param operationName - * the name of the custom operation to be invoked * @param resource * the input resource associated with the custom operation to be invoked * @param queryParameters @@ -1339,13 +1338,13 @@ public Bundle doSearch(String type, String compartment, String compartmentId, * @throws Exception */ @Override - public Resource doInvoke(FHIROperationContext operationContext, String resourceTypeName, - String logicalId, String versionId, String operationName, - Resource resource, MultivaluedMap queryParameters) throws Exception { + public Resource doInvoke(FHIROperationContext operationContext, String resourceTypeName, String logicalId, + String versionId, Resource resource, MultivaluedMap queryParameters) throws Exception { log.entering(this.getClass().getName(), "doInvoke"); // Save the current request context. FHIRRequestContext requestContext = FHIRRequestContext.get(); + String operationName = operationContext.getOperationCode(); try { Class resourceType = null; @@ -1371,7 +1370,9 @@ public Resource doInvoke(FHIROperationContext operationContext, String resourceT } // Add properties to the FHIR operation context - setOperationContextProperties(operationContext, resourceTypeName); + setOperationContextProperties(operationContext, resourceTypeName, parameters); + + getInterceptorMgr().fireBeforeInvokeEvent(operationContext); if (log.isLoggable(Level.FINE)) { log.fine("Invoking operation '" + operationName + "', context=\n" @@ -1379,10 +1380,16 @@ public Resource doInvoke(FHIROperationContext operationContext, String resourceT } Parameters result = operation.invoke(operationContext, resourceType, logicalId, versionId, parameters, this); + operationContext.setProperty(FHIROperationContext.PROPNAME_RESPONSE_PARAMETERS, result); if (log.isLoggable(Level.FINE)) { log.fine("Returned from invocation of operation '" + operationName + "'..."); } + getInterceptorMgr().fireAfterInvokeEvent(operationContext); + + // Grab the result from the operationContext in case an interceptor modified it + result = (Parameters) operationContext.getProperty(FHIROperationContext.PROPNAME_RESPONSE_PARAMETERS); + // if single resource output parameter, return the resource if (FHIROperationUtil.hasSingleResourceOutputParameter(result)) { return FHIROperationUtil.getSingleResourceOutputParameter(result); @@ -1788,7 +1795,7 @@ private Bundle processBundleEntries(Bundle requestBundle, Map va // Process entries. BundleType.Value bundleType = requestBundle.getType().getValueAsEnum(); - + // Translate the entries in the bundle to a list of FHIRRestOperation commands which we // then process in order final boolean isTransactionBundle = bundleType == BundleType.Value.TRANSACTION; @@ -1819,7 +1826,7 @@ private Bundle processBundleEntries(Bundle requestBundle, Map va log.exiting(this.getClass().getName(), "processBundleEntries"); } } - + /** * Process the given list of FHIRRestInteraction in order * @param bundleInteractions @@ -1832,7 +1839,7 @@ private List processBundleInteractions(List bundleIn assert(bundleInteractions.size() > 0); Entry[] responseEntries = new Entry[bundleInteractions.size()]; Map localRefMap = new HashMap<>(); - + // Run the prepare for all the bundle operations first. This allows us to perform // some async operations which we can fetch the results for later, which can // significantly reduce the overall request response time - especially important @@ -1843,7 +1850,7 @@ private List processBundleInteractions(List bundleIn // during the phase 1 loop. This is read-only txn = new FHIRTransactionHelper(getTransaction()); txn.begin(); - + // Phase 1: Do any ifNoneExist search and assign new id and meta info. If there is an // ifNoneExist hit, it is added to the corresponding responseEntries slot FHIRRestInteractionVisitorMeta meta = new FHIRRestInteractionVisitorMeta(transaction, this, localRefMap, responseEntries); @@ -1868,7 +1875,7 @@ private List processBundleInteractions(List bundleIn interaction.accept(refMapper); } } - + // Phase 3: Now run all the persistence operations in the correct order, injecting each result into the // appropriate position in the responseEntries array. At the end of the loop, each slot will be filled. FHIRRestInteractionVisitorPersist persist = new FHIRRestInteractionVisitorPersist(this, localRefMap, responseEntries, transaction); @@ -1891,7 +1898,7 @@ private List processBundleInteractions(List bundleIn txn = null; } } - + return Arrays.asList(responseEntries); } @@ -2402,10 +2409,14 @@ public Map buildPersistenceEventProperties(String type, String i * * @param operationContext * the FHIROperationContext on which to set the properties + * @param resourceTypeName + * @param requestParameters * @throws Exception */ - private void setOperationContextProperties(FHIROperationContext operationContext, String resourceTypeName) throws Exception { + private void setOperationContextProperties(FHIROperationContext operationContext, String resourceTypeName, Parameters requestParameters) + throws Exception { operationContext.setProperty(FHIROperationContext.PROPNAME_REQUEST_BASE_URI, getRequestBaseUri(resourceTypeName)); + operationContext.setProperty(FHIROperationContext.PROPNAME_REQUEST_PARAMETERS, requestParameters); operationContext.setProperty(FHIROperationContext.PROPNAME_PERSISTENCE_IMPL, persistence); } @@ -2716,36 +2727,6 @@ public void validateInteraction(Interaction interaction, String resourceType) th } } - 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); - } - } - @Override public Bundle doHistory(MultivaluedMap queryParameters, String requestUri) throws Exception { log.entering(this.getClass().getName(), "doHistory"); @@ -2944,7 +2925,7 @@ public List doRetrieveIndex(FHIROperationContext operationContext, String public String generateResourceId() { return persistence.generateResourceId(); } - + /** * Get the current time which can be used for the lastUpdated field * @return current time in UTC @@ -2955,8 +2936,8 @@ protected com.ibm.fhir.model.type.Instant getCurrentInstant() { @Override public Future storePayload(Resource resource, String logicalId, int newVersionNumber) throws Exception { - + // Delegate to the persistence layer. Result will be null if offloading is not supported return persistence.storePayload(resource, logicalId, newVersionNumber); } -} \ No newline at end of file +} diff --git a/fhir-persistence/src/test/java/com/ibm/fhir/test/InterceptorTest.java b/fhir-server/src/test/java/com/ibm/fhir/server/interceptor/test/InterceptorTest.java similarity index 95% rename from fhir-persistence/src/test/java/com/ibm/fhir/test/InterceptorTest.java rename to fhir-server/src/test/java/com/ibm/fhir/server/interceptor/test/InterceptorTest.java index b7b69a92095..91141aa1f21 100644 --- a/fhir-persistence/src/test/java/com/ibm/fhir/test/InterceptorTest.java +++ b/fhir-server/src/test/java/com/ibm/fhir/server/interceptor/test/InterceptorTest.java @@ -1,10 +1,11 @@ +package com.ibm.fhir.server.interceptor.test; /* * (C) Copyright IBM Corp. 2016, 2021 * * SPDX-License-Identifier: Apache-2.0 */ -package com.ibm.fhir.test; + import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; @@ -22,9 +23,9 @@ import com.ibm.fhir.model.test.TestUtil; import com.ibm.fhir.model.type.HumanName; import com.ibm.fhir.model.type.code.IssueType; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptorException; -import com.ibm.fhir.persistence.interceptor.impl.FHIRPersistenceInterceptorMgr; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; +import com.ibm.fhir.server.interceptor.FHIRPersistenceInterceptorMgr; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptorException; /** * This class tests the persistence interceptor feature. The MyInterceptor class is our test interceptor implementation @@ -36,6 +37,8 @@ public class InterceptorTest { protected static Patient patient = null; protected static Patient badPatient = null; + protected static String PATIENT_ID = "interceptorTest"; + public InterceptorTest() { } @@ -44,7 +47,9 @@ public void setUp() throws Exception { mgr = FHIRPersistenceInterceptorMgr.getInstance(); assertNotNull(mgr); - patient = TestUtil.getMinimalResource(Patient.class); + patient = TestUtil.getMinimalResource(Patient.class).toBuilder() + .id(PATIENT_ID) + .build(); // Inject a new family name of "Exception" to trigger errors from MyInterceptor badPatient = patient.toBuilder() @@ -86,7 +91,7 @@ public void testAfterCreate() throws Exception { public void testBeforeRead() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Patient"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); FHIRPersistenceEvent event = new FHIRPersistenceEvent(patient, properties); mgr.fireBeforeReadEvent(event); @@ -99,7 +104,7 @@ public void testBeforeRead() throws Exception { public void testAfterRead() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Patient1"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); FHIRPersistenceEvent event = new FHIRPersistenceEvent(patient, properties); mgr.fireAfterReadEvent(event); @@ -115,7 +120,7 @@ public void testAfterRead() throws Exception { public void testBeforeVread() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Patient"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); properties.put(FHIRPersistenceEvent.PROPNAME_VERSION_ID, "1"); FHIRPersistenceEvent event = new FHIRPersistenceEvent(patient, properties); @@ -129,7 +134,7 @@ public void testBeforeVread() throws Exception { public void testAfterVread() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Patient1"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); properties.put(FHIRPersistenceEvent.PROPNAME_VERSION_ID, "1"); FHIRPersistenceEvent event = new FHIRPersistenceEvent(patient, properties); @@ -144,7 +149,7 @@ public void testAfterVread() throws Exception { public void testBeforeHistory() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Patient"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); FHIRPersistenceEvent event = new FHIRPersistenceEvent(patient, properties); mgr.fireBeforeHistoryEvent(event); @@ -157,7 +162,7 @@ public void testBeforeHistory() throws Exception { public void testAfterHistory() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Patient1"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); FHIRPersistenceEvent event = new FHIRPersistenceEvent(patient, properties); mgr.fireAfterHistoryEvent(event); @@ -172,6 +177,7 @@ public void testAfterHistory() throws Exception { public void testBeforeSearch() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Patient"); + properties.put("dummyProp", PATIENT_ID); FHIRPersistenceEvent event = new FHIRPersistenceEvent(patient, properties); mgr.fireBeforeSearchEvent(event); @@ -184,6 +190,7 @@ public void testBeforeSearch() throws Exception { public void testAfterSearch() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Patient1"); + properties.put("dummyProp", PATIENT_ID); FHIRPersistenceEvent event = new FHIRPersistenceEvent(patient, properties); mgr.fireAfterSearchEvent(event); @@ -198,7 +205,7 @@ public void testAfterSearch() throws Exception { public void testBeforeUpdate() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Patient"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); FHIRPersistenceEvent event = new FHIRPersistenceEvent(patient, properties); mgr.fireBeforeUpdateEvent(event); @@ -211,7 +218,7 @@ public void testBeforeUpdate() throws Exception { public void testAfterUpdate() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Patient"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); FHIRPersistenceEvent event = new FHIRPersistenceEvent(patient, properties); mgr.fireAfterUpdateEvent(event); @@ -271,7 +278,7 @@ public void testAfterUpdateException() throws Exception { public void testBeforeReadException() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Exception"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); FHIRPersistenceEvent event = new FHIRPersistenceEvent(null, properties); mgr.fireBeforeReadEvent(event); @@ -281,7 +288,7 @@ public void testBeforeReadException() throws Exception { public void testAfterReadException() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Exception"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); FHIRPersistenceEvent event = new FHIRPersistenceEvent(badPatient, properties); mgr.fireAfterReadEvent(event); @@ -291,7 +298,7 @@ public void testAfterReadException() throws Exception { public void testBeforeVreadException() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Exception"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); properties.put(FHIRPersistenceEvent.PROPNAME_VERSION_ID, "1"); FHIRPersistenceEvent event = new FHIRPersistenceEvent(null, properties); @@ -302,7 +309,7 @@ public void testBeforeVreadException() throws Exception { public void testAfterVreadException() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Exception"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); properties.put(FHIRPersistenceEvent.PROPNAME_VERSION_ID, "1"); FHIRPersistenceEvent event = new FHIRPersistenceEvent(badPatient, properties); @@ -313,7 +320,7 @@ public void testAfterVreadException() throws Exception { public void testBeforeHistoryException() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Exception"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); FHIRPersistenceEvent event = new FHIRPersistenceEvent(null, properties); mgr.fireBeforeHistoryEvent(event); @@ -323,7 +330,7 @@ public void testBeforeHistoryException() throws Exception { public void testAfterHistoryException() throws Exception { Map properties = new HashMap(); properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_TYPE, "Exception"); - properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, "123"); + properties.put(FHIRPersistenceEvent.PROPNAME_RESOURCE_ID, PATIENT_ID); FHIRPersistenceEvent event = new FHIRPersistenceEvent(badPatient, properties); mgr.fireAfterHistoryEvent(event); diff --git a/fhir-persistence/src/test/java/com/ibm/fhir/test/MyInterceptor.java b/fhir-server/src/test/java/com/ibm/fhir/server/interceptor/test/MyInterceptor.java similarity index 78% rename from fhir-persistence/src/test/java/com/ibm/fhir/test/MyInterceptor.java rename to fhir-server/src/test/java/com/ibm/fhir/server/interceptor/test/MyInterceptor.java index 910c80f9a13..f30ef10d5e3 100644 --- a/fhir-persistence/src/test/java/com/ibm/fhir/test/MyInterceptor.java +++ b/fhir-server/src/test/java/com/ibm/fhir/server/interceptor/test/MyInterceptor.java @@ -1,10 +1,11 @@ +package com.ibm.fhir.server.interceptor.test; /* * (C) Copyright IBM Corp. 2016, 2021 * * SPDX-License-Identifier: Apache-2.0 */ -package com.ibm.fhir.test; + import com.ibm.fhir.model.resource.OperationOutcome; import com.ibm.fhir.model.resource.Patient; @@ -12,9 +13,9 @@ import com.ibm.fhir.model.type.HumanName; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.model.util.FHIRUtil; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptor; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptorException; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptor; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptorException; /** * This class serves as a test implementation of the FHIRPersistenceInterceptor interface. @@ -39,84 +40,108 @@ public MyInterceptor() { @Override public void beforeCreate(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { if (event.getFhirResource() != null && event.getFhirResourceType() != null) { - beforeCreateCount++; + if ("interceptorTest".equals(event.getFhirResource().getId())) { + beforeCreateCount++; + } } possiblyThrowException(event.getFhirResource(), IssueType.FORBIDDEN); } @Override public void afterCreate(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { - afterCreateCount++; + if ("interceptorTest".equals(event.getFhirResource().getId())) { + afterCreateCount++; + } possiblyThrowException(event.getFhirResource(), IssueType.CODE_INVALID); } @Override public void beforeUpdate(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { if (event.getFhirResource() != null && event.getFhirResourceType() != null && event.getFhirResourceId() != null) { - beforeUpdateCount++; + if ("interceptorTest".equals(event.getFhirResourceId())) { + beforeUpdateCount++; + } } possiblyThrowException(event.getFhirResource(), IssueType.CONFLICT); } @Override public void afterUpdate(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { - afterUpdateCount++; + if ("interceptorTest".equals(event.getFhirResourceId())) { + afterUpdateCount++; + } possiblyThrowException(event.getFhirResource(), IssueType.EXPIRED); } @Override public void beforeRead(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { if (event.getFhirResourceType() != null && event.getFhirResourceId() != null) { - beforeReadCount++; + if ("interceptorTest".equals(event.getFhirResourceId())) { + beforeReadCount++; + } } possiblyThrowException(event.getFhirResourceType()); } @Override public void afterRead(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { - afterReadCount++; + if ("interceptorTest".equals(event.getFhirResourceId())) { + afterReadCount++; + } possiblyThrowException(event.getFhirResourceType()); } @Override public void beforeVread(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { if (event.getFhirResourceType() != null && event.getFhirResourceId() != null && event.getFhirVersionId() != null) { - beforeVreadCount++; + if ("interceptorTest".equals(event.getFhirResourceId())) { + beforeVreadCount++; + } } possiblyThrowException(event.getFhirResourceType()); } @Override public void afterVread(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { - afterVreadCount++; + if ("interceptorTest".equals(event.getFhirResourceId())) { + afterVreadCount++; + } possiblyThrowException(event.getFhirResourceType()); } @Override public void beforeHistory(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { if (event.getFhirResourceType() != null && event.getFhirResourceId() != null) { - beforeHistoryCount++; + if ("interceptorTest".equals(event.getFhirResourceId())) { + beforeHistoryCount++; + } } possiblyThrowException(event.getFhirResourceType()); } @Override public void afterHistory(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { - afterHistoryCount++; + if ("interceptorTest".equals(event.getFhirResourceId())) { + afterHistoryCount++; + } possiblyThrowException(event.getFhirResourceType()); } @Override public void beforeSearch(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { if (event.getFhirResourceType() != null) { - beforeSearchCount++; + if ("interceptorTest".equals(event.getProperty("dummyProp"))) { + beforeSearchCount++; + } } possiblyThrowException(event.getFhirResourceType()); } @Override public void afterSearch(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { - afterSearchCount++; + if ("interceptorTest".equals(event.getProperty("dummyProp"))) { + afterSearchCount++; + } possiblyThrowException(event.getFhirResourceType()); } diff --git a/fhir-server/src/test/java/com/ibm/fhir/server/test/InteractionValidationConfigTest.java b/fhir-server/src/test/java/com/ibm/fhir/server/test/InteractionValidationConfigTest.java index 62ad5d1b26b..788e5a0aa59 100644 --- a/fhir-server/src/test/java/com/ibm/fhir/server/test/InteractionValidationConfigTest.java +++ b/fhir-server/src/test/java/com/ibm/fhir/server/test/InteractionValidationConfigTest.java @@ -48,7 +48,7 @@ import com.ibm.fhir.model.type.code.ProcedureStatus; import com.ibm.fhir.persistence.FHIRPersistence; import com.ibm.fhir.registry.FHIRRegistry; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRRestHelper; public class InteractionValidationConfigTest { diff --git a/fhir-server/src/test/java/com/ibm/fhir/server/test/ProfileValidationConfigTest.java b/fhir-server/src/test/java/com/ibm/fhir/server/test/ProfileValidationConfigTest.java index c50b7789306..3fb90111439 100644 --- a/fhir-server/src/test/java/com/ibm/fhir/server/test/ProfileValidationConfigTest.java +++ b/fhir-server/src/test/java/com/ibm/fhir/server/test/ProfileValidationConfigTest.java @@ -44,7 +44,7 @@ import com.ibm.fhir.model.type.code.ProcedureStatus; import com.ibm.fhir.persistence.FHIRPersistence; import com.ibm.fhir.registry.FHIRRegistry; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; import com.ibm.fhir.server.util.FHIRRestHelper; import com.ibm.fhir.validation.exception.FHIRValidationException; diff --git a/fhir-server/src/test/java/com/ibm/fhir/server/util/FHIRRestHelperTest.java b/fhir-server/src/test/java/com/ibm/fhir/server/util/FHIRRestHelperTest.java index fb858606a31..3817bcfe3bd 100644 --- a/fhir-server/src/test/java/com/ibm/fhir/server/util/FHIRRestHelperTest.java +++ b/fhir-server/src/test/java/com/ibm/fhir/server/util/FHIRRestHelperTest.java @@ -8,7 +8,6 @@ import static com.ibm.fhir.model.type.String.string; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.when; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotEquals; @@ -56,13 +55,12 @@ import com.ibm.fhir.model.type.code.ProcedureStatus; import com.ibm.fhir.persistence.FHIRPersistence; import com.ibm.fhir.persistence.SingleResourceResult; -import com.ibm.fhir.persistence.exception.FHIRPersistenceException; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptor; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptorException; -import com.ibm.fhir.persistence.interceptor.impl.FHIRPersistenceInterceptorMgr; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.search.context.FHIRSearchContext; import com.ibm.fhir.search.context.FHIRSearchContextFactory; +import com.ibm.fhir.server.interceptor.FHIRPersistenceInterceptorMgr; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptor; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptorException; import com.ibm.fhir.server.test.MockPersistenceImpl; import com.ibm.fhir.server.test.MockTransactionAdapter; @@ -2117,4 +2115,4 @@ public void testBatchBundleResourceUrlMismatch() throws Exception { Bundle.Entry.Response response = entry.getResponse(); assertEquals(response.getStatus().getValue(), "400"); } -} \ No newline at end of file +} diff --git a/fhir-server/src/test/resources/META-INF/services/com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptor b/fhir-server/src/test/resources/META-INF/services/com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptor new file mode 100644 index 00000000000..8048f2c60a0 --- /dev/null +++ b/fhir-server/src/test/resources/META-INF/services/com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptor @@ -0,0 +1 @@ +com.ibm.fhir.server.interceptor.test.MyInterceptor diff --git a/fhir-smart/pom.xml b/fhir-smart/pom.xml index e4b2fae1002..1c6475da77f 100644 --- a/fhir-smart/pom.xml +++ b/fhir-smart/pom.xml @@ -20,21 +20,26 @@ + + jakarta.ws.rs + jakarta.ws.rs-api + provided + ${project.groupId} - fhir-core + fhir-server-spi ${project.version} provided ${project.groupId} - fhir-config + fhir-persistence ${project.version} provided ${project.groupId} - fhir-persistence + fhir-registry ${project.version} provided diff --git a/fhir-smart/src/main/java/com/ibm/fhir/smart/AuthzPolicyEnforcementPersistenceInterceptor.java b/fhir-smart/src/main/java/com/ibm/fhir/smart/AuthzPolicyEnforcementPersistenceInterceptor.java index b116785d74e..c4e0fceba4a 100644 --- a/fhir-smart/src/main/java/com/ibm/fhir/smart/AuthzPolicyEnforcementPersistenceInterceptor.java +++ b/fhir-smart/src/main/java/com/ibm/fhir/smart/AuthzPolicyEnforcementPersistenceInterceptor.java @@ -6,20 +6,27 @@ package com.ibm.fhir.smart; +import java.io.StringReader; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.ws.rs.core.Response; import com.ibm.fhir.config.FHIRRequestContext; import com.ibm.fhir.model.resource.Bundle; +import com.ibm.fhir.model.resource.Parameters; +import com.ibm.fhir.model.resource.Parameters.Parameter; import com.ibm.fhir.model.resource.Provenance; import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.resource.SearchParameter; @@ -37,12 +44,10 @@ import com.ibm.fhir.persistence.SingleResourceResult; import com.ibm.fhir.persistence.context.FHIRPersistenceContext; import com.ibm.fhir.persistence.context.FHIRPersistenceContextFactory; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.persistence.exception.FHIRPersistenceException; import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceDeletedException; import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceNotFoundException; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptor; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptorException; import com.ibm.fhir.search.compartment.CompartmentUtil; import com.ibm.fhir.search.context.FHIRSearchContext; import com.ibm.fhir.search.exception.FHIRSearchException; @@ -50,22 +55,153 @@ import com.ibm.fhir.search.util.ReferenceUtil; import com.ibm.fhir.search.util.ReferenceValue; import com.ibm.fhir.search.util.SearchUtil; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptor; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptorException; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; import com.ibm.fhir.smart.JWT.Claim; import com.ibm.fhir.smart.JWT.DecodedJWT; import com.ibm.fhir.smart.Scope.ContextType; import com.ibm.fhir.smart.Scope.Permission; +import jakarta.json.Json; +import jakarta.json.JsonObject; +import jakarta.json.JsonReader; +import jakarta.json.JsonReaderFactory; +import jakarta.json.JsonValue; + public class AuthzPolicyEnforcementPersistenceInterceptor implements FHIRPersistenceInterceptor { + private static final Logger log = Logger.getLogger(AuthzPolicyEnforcementPersistenceInterceptor.class.getName()); + private static final String DAVINCI_DRUG_FORMULARY_COVERAGE_PLAN = "http://hl7.org/fhir/us/davinci-drug-formulary/StructureDefinition/usdf-CoveragePlan"; - private static final Logger log = Logger.getLogger(AuthzPolicyEnforcementPersistenceInterceptor.class.getName()); - private static final String BEARER_TOKEN_PREFIX = "Bearer"; private static final String PATIENT = "Patient"; private static final String REQUEST_NOT_PERMITTED = "Requested interaction is not permitted by any of the passed scopes."; + private static final JsonReaderFactory JSON_READER_FACTORY = Json.createReaderFactory(null); + + @Override + public void beforeInvoke(FHIROperationContext context) throws FHIRPersistenceInterceptorException { + Permission neededPermission; + List resourceTypes; + if ("import".equals(context.getOperationCode())) { + neededPermission = Permission.WRITE; + resourceTypes = computeImportResourceTypes(context); + } else if ("export".equals(context.getOperationCode())) { + neededPermission = Permission.READ; + resourceTypes = computeExportResourceTypes(context); + } else { + return; + } + + if (resourceTypes.isEmpty()) { + throw new IllegalStateException("The set of resource types was unexpectedly empty"); + } + + DecodedJWT jwt = JWT.decode(getAccessToken()); + List scopesFromToken = getScopesFromToken(jwt).stream() + .filter(s -> s.getContextType() == ContextType.SYSTEM) + .collect(Collectors.toList()); + + for (String resourceType : resourceTypes) { + checkScopes(resourceType, neededPermission, scopesFromToken); + } + } + + @Override + public void afterInvoke(FHIROperationContext context) throws FHIRPersistenceInterceptorException { + Permission neededPermission = Permission.READ; + Set resourceTypes = new HashSet<>(); + + if (!"bulkdata-status".equals(context.getOperationCode())) { + return; + } + + Parameters parameters = (Parameters) context.getProperty(FHIROperationContext.PROPNAME_REQUEST_PARAMETERS); + + // bulkdata-status has a special JSON response that is not a Parameters object + Response response = (Response) context.getProperty(FHIROperationContext.PROPNAME_RESPONSE); + + if (response.hasEntity()) { + Object entity = response.getEntity(); + if (entity instanceof String) { + JsonReader responseReader = JSON_READER_FACTORY.createReader(new StringReader((String)entity)); + JsonObject responseObj = responseReader.readObject(); + String request = responseObj.getJsonString("request").getString(); + if (request.contains("$export")) { + for (JsonValue output : responseObj.getJsonArray("output")) { + resourceTypes.add(output.asJsonObject().getString("type")); + } + } else if (request.contains("$import")) { + // nothing much to check for bulk-import status; the only output is a set of OperationOutcome + } else { + String jobId = parameters.getParameter().stream() + .filter(p -> "job".equals(p.getName().getValue())) + .map(p -> p.getValue().as(ModelSupport.FHIR_STRING).getValue()) + .findFirst() + .get(); + throw new IllegalStateException("Bulk data request for job '" + jobId + "' is neither '$import' nor '$export'!"); + } + } else { + throw new IllegalStateException("Encountered unexpected response entity of type " + entity.getClass().getName()); + } + } + + DecodedJWT jwt = JWT.decode(getAccessToken()); + List scopesFromToken = getScopesFromToken(jwt).stream() + .filter(s -> s.getContextType() == ContextType.SYSTEM) + .collect(Collectors.toList()); + + for (String resourceType : resourceTypes) { + checkScopes(resourceType, neededPermission, scopesFromToken); + } + } + + private List computeImportResourceTypes(FHIROperationContext context) { + Parameters parameters = (Parameters) context.getProperty(FHIROperationContext.PROPNAME_REQUEST_PARAMETERS); + Set types = parameters.getParameter().stream() + .filter(p -> "input".equals(p.getName().getValue())) + .map(p -> p.getPart()) + .flatMap(part -> part.stream() + .filter(pp -> "type".equals(pp.getName().getValue())) + .map(pp -> pp.getValue().as(ModelSupport.FHIR_STRING).getValue())) + .collect(Collectors.toSet()); + + return new ArrayList<>(types); + } + + private List computeExportResourceTypes(FHIROperationContext context) { + List resourceTypes = new ArrayList<>(); + switch (context.getType()) { + case INSTANCE: // Group/:id/$export + case RESOURCE_TYPE: // Patient/$export + // Either way, the set resourceTypes to export are those from the Patient compartment + try { + resourceTypes = CompartmentUtil.getCompartmentResourceTypes("Patient"); + } catch (FHIRSearchException e) { + throw new IllegalStateException("Unexpected error while computing the resource types for the export", e); + } + break; + case SYSTEM: + Parameters parameters = (Parameters) context.getProperty(FHIROperationContext.PROPNAME_REQUEST_PARAMETERS); + Optional typesParam = parameters.getParameter().stream().filter(p -> "_types".equals(p.getName().getValue())).findFirst(); + if (typesParam.isPresent()) { + String typesString = typesParam.get().getValue().as(ModelSupport.FHIR_STRING).getValue(); + resourceTypes = Arrays.asList(typesString.split(",")); + } else { + // "Resource" is used as a placeholder for the set of all resource types; equivalent to "*" in SMART + resourceTypes = Collections.singletonList("Resource"); + } + break; + default: + log.warning("Unexpected export of type " + context.getType()); + break; + } + return resourceTypes; + } + @Override public void beforeRead(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException { String resourceType = event.getFhirResourceType(); @@ -439,6 +575,14 @@ private boolean isAllowed(Resource resource, Set contextIds, Permission // Then group the scopes by their context type .collect(Collectors.groupingBy(s -> s.getContextType())); + if (approvedScopeMap.containsKey(ContextType.SYSTEM)) { + if (log.isLoggable(Level.FINE)) { + log.fine(requiredPermission.value() + " permission for '" + resourceType + "/" + resource.getId() + + "' is granted via scope " + approvedScopeMap.get(ContextType.SYSTEM)); + } + return true; + } + if (approvedScopeMap.containsKey(ContextType.USER)) { // For `user` scopes, we grant access to all resources of the requested type. // Implementers that use these scopes are encouraged to layer on their own permissions model beyond this. diff --git a/fhir-smart/src/main/java/com/ibm/fhir/smart/Scope.java b/fhir-smart/src/main/java/com/ibm/fhir/smart/Scope.java index 0b7da5feaec..4c750d932fb 100644 --- a/fhir-smart/src/main/java/com/ibm/fhir/smart/Scope.java +++ b/fhir-smart/src/main/java/com/ibm/fhir/smart/Scope.java @@ -15,7 +15,7 @@ * SMART App Launch: Scopes and Launch Context */ public class Scope { - public static final String SCOPE_STRING_REGEX = "(user|patient)/" + "([a-zA-Z]+|\\*)" + "\\." + "(read|write|\\*)"; + public static final String SCOPE_STRING_REGEX = "(user|patient|system)/" + "([a-zA-Z]+|\\*)" + "\\." + "(read|write|\\*)"; private final ContextType contextType; private final ResourceType.Value resourceType; @@ -73,7 +73,7 @@ public Permission getPermission() { } /** - * @return a scopeString of the form {@code (user|patient)/:resourceType.(read|write|*)} + * @return a scopeString of the form {@code (user|patient|system)/:resourceType.(read|write|*)} */ @Override public String toString() { @@ -84,7 +84,8 @@ public String toString() { public static enum ContextType { PATIENT("patient"), - USER("user"); + USER("user"), + SYSTEM("system"); private final String value; diff --git a/fhir-smart/src/main/resources/META-INF/services/com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptor b/fhir-smart/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptor similarity index 100% rename from fhir-smart/src/main/resources/META-INF/services/com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptor rename to fhir-smart/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptor diff --git a/fhir-smart/src/test/java/com/ibm/fhir/smart/test/AuthzPolicyEnforcementTest.java b/fhir-smart/src/test/java/com/ibm/fhir/smart/test/AuthzPolicyEnforcementTest.java index d1ae6359e55..a6361a4344e 100644 --- a/fhir-smart/src/test/java/com/ibm/fhir/smart/test/AuthzPolicyEnforcementTest.java +++ b/fhir-smart/src/test/java/com/ibm/fhir/smart/test/AuthzPolicyEnforcementTest.java @@ -45,12 +45,12 @@ import com.ibm.fhir.model.type.code.BundleType; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.model.type.code.ResourceType; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptorException; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.search.SearchConstants.Type; import com.ibm.fhir.search.context.impl.FHIRSearchContextImpl; import com.ibm.fhir.search.parameters.QueryParameter; import com.ibm.fhir.search.parameters.QueryParameterValue; +import com.ibm.fhir.server.spi.interceptor.FHIRPersistenceInterceptorException; import com.ibm.fhir.smart.AuthzPolicyEnforcementPersistenceInterceptor; import com.ibm.fhir.smart.Scope.Permission; @@ -824,6 +824,10 @@ public static Object[][] scopeStrings() { {"user/Patient.read", CONTEXT_IDS, patient, Permission.READ}, {"user/Observation.write", CONTEXT_IDS, observation, Permission.WRITE}, + {"system/*.*", CONTEXT_IDS, all_resources, Permission.ALL}, + {"system/Patient.read", CONTEXT_IDS, patient, Permission.READ}, + {"system/Observation.write", CONTEXT_IDS, observation, Permission.WRITE}, + {"openid profile", CONTEXT_IDS, Collections.EMPTY_SET, null}, }; } diff --git a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/ExportOperation.java b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/ExportOperation.java index aa009432e03..40c603f9773 100644 --- a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/ExportOperation.java +++ b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/ExportOperation.java @@ -26,9 +26,9 @@ import com.ibm.fhir.operation.bulkdata.processor.BulkDataFactory; import com.ibm.fhir.operation.bulkdata.util.BulkDataExportUtil; import com.ibm.fhir.operation.bulkdata.util.CommonUtil; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; /** * BulkDataAccess V1.0.0: STU1 - diff --git a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/ImportOperation.java b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/ImportOperation.java index 89bbd745003..a1c3e71f64b 100644 --- a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/ImportOperation.java +++ b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/ImportOperation.java @@ -23,9 +23,9 @@ import com.ibm.fhir.operation.bulkdata.processor.BulkDataFactory; import com.ibm.fhir.operation.bulkdata.util.BulkDataImportUtil; import com.ibm.fhir.operation.bulkdata.util.CommonUtil; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; /** * BulkData Specification Proposal: diff --git a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/StatusOperation.java b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/StatusOperation.java index 4a36d85790e..613b74d2f61 100644 --- a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/StatusOperation.java +++ b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/StatusOperation.java @@ -18,9 +18,9 @@ import com.ibm.fhir.operation.bulkdata.processor.BulkDataFactory; import com.ibm.fhir.operation.bulkdata.util.BulkDataExportUtil; import com.ibm.fhir.operation.bulkdata.util.CommonUtil; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; /** * BulkDataAccess IG: STU1 - Polling Response
diff --git a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/config/OperationContextAdapter.java b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/config/OperationContextAdapter.java index f74a39f80c9..11116a245e2 100644 --- a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/config/OperationContextAdapter.java +++ b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/config/OperationContextAdapter.java @@ -6,7 +6,7 @@ package com.ibm.fhir.operation.bulkdata.config; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; /** * Adapts the OperationContext to the output type. diff --git a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/config/preflight/PreflightFactory.java b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/config/preflight/PreflightFactory.java index 875fad957be..b92cfdd067d 100644 --- a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/config/preflight/PreflightFactory.java +++ b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/config/preflight/PreflightFactory.java @@ -19,7 +19,7 @@ import com.ibm.fhir.operation.bulkdata.config.preflight.impl.S3Preflight; import com.ibm.fhir.operation.bulkdata.model.type.Input; import com.ibm.fhir.operation.bulkdata.model.type.StorageType; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; /** * Generates a Preflight ConfigurationTest Object based on the storage type diff --git a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/processor/BulkDataFactory.java b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/processor/BulkDataFactory.java index 856c68f82a4..e345d462394 100644 --- a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/processor/BulkDataFactory.java +++ b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/processor/BulkDataFactory.java @@ -8,7 +8,7 @@ import com.ibm.fhir.operation.bulkdata.config.OperationContextAdapter; import com.ibm.fhir.operation.bulkdata.processor.impl.ExportImportImpl; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; /** * The BulkData Factory simplifies the creation of an instance of the Import and Export feature. diff --git a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/processor/ExportImportBulkData.java b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/processor/ExportImportBulkData.java index 7f39f496c35..af9b70eb494 100644 --- a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/processor/ExportImportBulkData.java +++ b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/processor/ExportImportBulkData.java @@ -16,7 +16,7 @@ import com.ibm.fhir.operation.bulkdata.OperationConstants; import com.ibm.fhir.operation.bulkdata.model.type.Input; import com.ibm.fhir.operation.bulkdata.model.type.StorageDetail; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; /** * The interfaces for the Backend Implementation of: diff --git a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/processor/impl/ExportImportImpl.java b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/processor/impl/ExportImportImpl.java index 3008a5b5781..9460c8bd701 100644 --- a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/processor/impl/ExportImportImpl.java +++ b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/processor/impl/ExportImportImpl.java @@ -28,8 +28,8 @@ import com.ibm.fhir.operation.bulkdata.model.type.Input; import com.ibm.fhir.operation.bulkdata.model.type.StorageDetail; import com.ibm.fhir.operation.bulkdata.processor.ExportImportBulkData; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; /** * Import from or export to IBM Cloud Object Storage (COS) or similar S3-compatible object stores diff --git a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/util/BulkDataExportUtil.java b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/util/BulkDataExportUtil.java index 4a8a272a2f2..f3bc26c99da 100644 --- a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/util/BulkDataExportUtil.java +++ b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/util/BulkDataExportUtil.java @@ -32,7 +32,7 @@ import com.ibm.fhir.operation.bulkdata.model.transformer.JobIdEncodingTransformer; import com.ibm.fhir.search.compartment.CompartmentUtil; import com.ibm.fhir.search.exception.FHIRSearchException; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; /** * BulkData Util captures common methods diff --git a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/util/BulkDataImportUtil.java b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/util/BulkDataImportUtil.java index 5178bb0491e..8264371db5c 100644 --- a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/util/BulkDataImportUtil.java +++ b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/util/BulkDataImportUtil.java @@ -28,7 +28,7 @@ import com.ibm.fhir.path.evaluator.FHIRPathEvaluator; import com.ibm.fhir.path.evaluator.FHIRPathEvaluator.EvaluationContext; import com.ibm.fhir.path.exception.FHIRPathException; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; /** * BulkData Import Util captures common methods diff --git a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/util/CommonUtil.java b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/util/CommonUtil.java index b3e8836edfe..3118db7b1a8 100644 --- a/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/util/CommonUtil.java +++ b/operation/fhir-operation-bulkdata/src/main/java/com/ibm/fhir/operation/bulkdata/util/CommonUtil.java @@ -13,8 +13,8 @@ import com.ibm.fhir.operation.bulkdata.config.ConfigurationFactory; import com.ibm.fhir.operation.bulkdata.config.OperationContextAdapter; import com.ibm.fhir.operation.bulkdata.model.type.StorageType; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; /** * Common Util captures common methods diff --git a/operation/fhir-operation-bulkdata/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/operation/fhir-operation-bulkdata/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from operation/fhir-operation-bulkdata/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to operation/fhir-operation-bulkdata/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation diff --git a/operation/fhir-operation-bulkdata/src/test/java/com/ibm/fhir/operation/bulkdata/OperationTest.java b/operation/fhir-operation-bulkdata/src/test/java/com/ibm/fhir/operation/bulkdata/OperationTest.java index d762eb6ef23..f6ea58c6d26 100644 --- a/operation/fhir-operation-bulkdata/src/test/java/com/ibm/fhir/operation/bulkdata/OperationTest.java +++ b/operation/fhir-operation-bulkdata/src/test/java/com/ibm/fhir/operation/bulkdata/OperationTest.java @@ -21,7 +21,7 @@ import com.ibm.fhir.model.resource.OperationDefinition; import com.ibm.fhir.operation.bulkdata.processor.BulkDataFactory; import com.ibm.fhir.operation.bulkdata.processor.ExportImportBulkData; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; public class OperationTest { @BeforeClass @@ -83,7 +83,7 @@ public void testPatientExportOperation() { @Test public void testBulkDataFactory() { - FHIROperationContext operationContext = FHIROperationContext.createInstanceOperationContext(); + FHIROperationContext operationContext = FHIROperationContext.createInstanceOperationContext("test"); ExportImportBulkData eibd = BulkDataFactory.getInstance(operationContext); assertNotNull(eibd); } diff --git a/operation/fhir-operation-bulkdata/src/test/java/com/ibm/fhir/operation/bulkdata/util/BulkDataExportUtilTest.java b/operation/fhir-operation-bulkdata/src/test/java/com/ibm/fhir/operation/bulkdata/util/BulkDataExportUtilTest.java index 0b0317a9871..ba8678b798f 100644 --- a/operation/fhir-operation-bulkdata/src/test/java/com/ibm/fhir/operation/bulkdata/util/BulkDataExportUtilTest.java +++ b/operation/fhir-operation-bulkdata/src/test/java/com/ibm/fhir/operation/bulkdata/util/BulkDataExportUtilTest.java @@ -46,8 +46,8 @@ import com.ibm.fhir.operation.bulkdata.OperationConstants; import com.ibm.fhir.operation.bulkdata.OperationConstants.ExportType; import com.ibm.fhir.operation.bulkdata.model.PollingLocationResponse; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIROperationContext.Type; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext.Type; /** * Test Export util diff --git a/operation/fhir-operation-bulkdata/src/test/java/com/ibm/fhir/operation/bulkdata/util/BulkDataImportUtilTest.java b/operation/fhir-operation-bulkdata/src/test/java/com/ibm/fhir/operation/bulkdata/util/BulkDataImportUtilTest.java index ece27371c2b..4ee09659807 100644 --- a/operation/fhir-operation-bulkdata/src/test/java/com/ibm/fhir/operation/bulkdata/util/BulkDataImportUtilTest.java +++ b/operation/fhir-operation-bulkdata/src/test/java/com/ibm/fhir/operation/bulkdata/util/BulkDataImportUtilTest.java @@ -38,7 +38,7 @@ import com.ibm.fhir.model.parser.FHIRParser; import com.ibm.fhir.model.parser.exception.FHIRParserException; import com.ibm.fhir.model.resource.Parameters; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; public class BulkDataImportUtilTest { @@ -164,7 +164,7 @@ public Parameters loadTestFile(String file) throws FHIRParserException, IOExcept } private FHIROperationContext getContext() { - FHIROperationContext ctx = FHIROperationContext.createInstanceOperationContext(); + FHIROperationContext ctx = FHIROperationContext.createInstanceOperationContext("import"); HttpHeaders httpHeaders = new HttpHeaders() { @Override diff --git a/operation/fhir-operation-convert/pom.xml b/operation/fhir-operation-convert/pom.xml index d868e55cb7d..78857513260 100644 --- a/operation/fhir-operation-convert/pom.xml +++ b/operation/fhir-operation-convert/pom.xml @@ -15,8 +15,21 @@ ${project.groupId} - fhir-server + fhir-server-spi ${project.version} + provided + + + ${project.groupId} + fhir-model + ${project.version} + provided + + + ${project.groupId} + fhir-registry + ${project.version} + provided diff --git a/operation/fhir-operation-convert/src/main/java/com/ibm/fhir/operation/convert/ConvertOperation.java b/operation/fhir-operation-convert/src/main/java/com/ibm/fhir/operation/convert/ConvertOperation.java index 4fdf7d6e484..30d82ce4c7c 100644 --- a/operation/fhir-operation-convert/src/main/java/com/ibm/fhir/operation/convert/ConvertOperation.java +++ b/operation/fhir-operation-convert/src/main/java/com/ibm/fhir/operation/convert/ConvertOperation.java @@ -13,10 +13,10 @@ import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.registry.FHIRRegistry; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class ConvertOperation extends AbstractOperation { @Override diff --git a/operation/fhir-operation-convert/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/operation/fhir-operation-convert/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from operation/fhir-operation-convert/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to operation/fhir-operation-convert/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation diff --git a/operation/fhir-operation-document/pom.xml b/operation/fhir-operation-document/pom.xml index 239f0cf787a..abc2bf00edb 100644 --- a/operation/fhir-operation-document/pom.xml +++ b/operation/fhir-operation-document/pom.xml @@ -15,8 +15,21 @@ ${project.groupId} - fhir-server + fhir-server-spi ${project.version} + provided + + + ${project.groupId} + fhir-persistence + ${project.version} + provided + + + ${project.groupId} + fhir-registry + ${project.version} + provided diff --git a/operation/fhir-operation-document/src/main/java/com/ibm/fhir/operation/document/DocumentOperation.java b/operation/fhir-operation-document/src/main/java/com/ibm/fhir/operation/document/DocumentOperation.java index 3d18dd0cfdf..59203a8d044 100644 --- a/operation/fhir-operation-document/src/main/java/com/ibm/fhir/operation/document/DocumentOperation.java +++ b/operation/fhir-operation-document/src/main/java/com/ibm/fhir/operation/document/DocumentOperation.java @@ -31,11 +31,11 @@ import com.ibm.fhir.model.type.code.BundleType; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.registry.FHIRRegistry; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; public class DocumentOperation extends AbstractOperation { @Override diff --git a/operation/fhir-operation-document/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/operation/fhir-operation-document/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from operation/fhir-operation-document/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to operation/fhir-operation-document/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation diff --git a/operation/fhir-operation-erase/pom.xml b/operation/fhir-operation-erase/pom.xml index d06fe457d90..59fc9468235 100644 --- a/operation/fhir-operation-erase/pom.xml +++ b/operation/fhir-operation-erase/pom.xml @@ -14,6 +14,12 @@ fhir-operation-erase + + ${project.groupId} + fhir-server + ${project.version} + provided + jakarta.ws.rs jakarta.ws.rs-api @@ -24,12 +30,6 @@ jakarta.servlet-api provided - - ${project.groupId} - fhir-server - ${project.version} - provided - org.testng testng diff --git a/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/EraseOperation.java b/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/EraseOperation.java index 0f0c3003cd8..e58e8cf9162 100644 --- a/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/EraseOperation.java +++ b/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/EraseOperation.java @@ -21,10 +21,10 @@ import com.ibm.fhir.operation.erase.impl.EraseRestFactory; import com.ibm.fhir.persistence.ResourceEraseRecord; import com.ibm.fhir.persistence.erase.EraseDTO; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; /** * Custom Operation to Erase a specific instance or instance-version of the FHIR diff --git a/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/adapter/ResourceEraseRecordAdapter.java b/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/adapter/ResourceEraseRecordAdapter.java index 8e62d74da4e..8e66f6372fe 100644 --- a/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/adapter/ResourceEraseRecordAdapter.java +++ b/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/adapter/ResourceEraseRecordAdapter.java @@ -17,7 +17,7 @@ import com.ibm.fhir.persistence.ResourceEraseRecord; import com.ibm.fhir.persistence.ResourceEraseRecord.Status; import com.ibm.fhir.persistence.erase.EraseDTO; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; /** * Adapts the ResourceEraseRecord and EraseDTO to a Parameters object diff --git a/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/audit/EraseOperationAuditLogger.java b/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/audit/EraseOperationAuditLogger.java index f82f00023d0..bf0f51e1c3f 100644 --- a/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/audit/EraseOperationAuditLogger.java +++ b/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/audit/EraseOperationAuditLogger.java @@ -27,8 +27,8 @@ import com.ibm.fhir.model.resource.Resource; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.persistence.erase.EraseDTO; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; import com.ibm.fhir.server.util.RestAuditLogger; /** diff --git a/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/impl/EraseRestFactory.java b/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/impl/EraseRestFactory.java index f7269e6d261..afd940cd0c4 100644 --- a/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/impl/EraseRestFactory.java +++ b/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/impl/EraseRestFactory.java @@ -10,7 +10,7 @@ import com.ibm.fhir.model.resource.Parameters; import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; /** * Selects the single instance of Erase for a given request. diff --git a/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/impl/EraseRestImpl.java b/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/impl/EraseRestImpl.java index dd87fea4bd4..189e24497de 100644 --- a/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/impl/EraseRestImpl.java +++ b/operation/fhir-operation-erase/src/main/java/com/ibm/fhir/operation/erase/impl/EraseRestImpl.java @@ -32,7 +32,7 @@ import com.ibm.fhir.persistence.erase.EraseDTO; import com.ibm.fhir.search.compartment.CompartmentUtil; import com.ibm.fhir.search.exception.FHIRSearchException; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; /** * Erase Rest Implementation implements the parameters processing, authorization diff --git a/operation/fhir-operation-erase/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/operation/fhir-operation-erase/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from operation/fhir-operation-erase/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to operation/fhir-operation-erase/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation diff --git a/operation/fhir-operation-erase/src/test/java/com/ibm/fhir/operation/erase/EraseTest.java b/operation/fhir-operation-erase/src/test/java/com/ibm/fhir/operation/erase/EraseTest.java index ffa66fa09c5..4a25b6587dc 100644 --- a/operation/fhir-operation-erase/src/test/java/com/ibm/fhir/operation/erase/EraseTest.java +++ b/operation/fhir-operation-erase/src/test/java/com/ibm/fhir/operation/erase/EraseTest.java @@ -56,7 +56,7 @@ import com.ibm.fhir.persistence.ResourceEraseRecord; import com.ibm.fhir.persistence.ResourceEraseRecord.Status; import com.ibm.fhir.persistence.erase.EraseDTO; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; /** * Tests the Java code for the EraseOperation @@ -154,7 +154,7 @@ public void verifyDto(EraseDTO dto, Class resourceType, Stri * @return */ private FHIROperationContext generateContext(String method, String checkRole) { - FHIROperationContext ctx = FHIROperationContext.createInstanceOperationContext(); + FHIROperationContext ctx = FHIROperationContext.createInstanceOperationContext("erase"); ctx.setProperty(FHIROperationContext.PROPNAME_METHOD_TYPE, method); ctx.setProperty(FHIROperationContext.PROPNAME_SECURITY_CONTEXT, new MockSecurityContext(checkRole)); ctx.setProperty(FHIROperationContext.PROPNAME_HTTP_REQUEST, new MockHttpServletRequest()); diff --git a/operation/fhir-operation-erase/src/test/java/com/ibm/fhir/operation/erase/mock/MockFHIRResourceHelpers.java b/operation/fhir-operation-erase/src/test/java/com/ibm/fhir/operation/erase/mock/MockFHIRResourceHelpers.java index 16269cc4593..be603464642 100644 --- a/operation/fhir-operation-erase/src/test/java/com/ibm/fhir/operation/erase/mock/MockFHIRResourceHelpers.java +++ b/operation/fhir-operation-erase/src/test/java/com/ibm/fhir/operation/erase/mock/MockFHIRResourceHelpers.java @@ -25,17 +25,15 @@ import com.ibm.fhir.persistence.ResourceEraseRecord; import com.ibm.fhir.persistence.ResourceEraseRecord.Status; import com.ibm.fhir.persistence.SingleResourceResult; +import com.ibm.fhir.persistence.context.FHIRPersistenceEvent; import com.ibm.fhir.persistence.erase.EraseDTO; import com.ibm.fhir.persistence.exception.FHIRPersistenceException; -import com.ibm.fhir.persistence.helper.FHIRTransactionHelper; -import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent; import com.ibm.fhir.persistence.payload.PayloadKey; import com.ibm.fhir.search.context.FHIRSearchContext; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.operation.spi.FHIRRestOperationResponse; -import com.ibm.fhir.server.util.FHIROperationUtil; -import com.ibm.fhir.server.util.FHIRRestHelper.Interaction; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.FHIRRestOperationResponse; /** * Helper for Mocking failure tests with the FHIR Resource Helpers @@ -159,7 +157,7 @@ public Bundle doSearch(String type, String compartment, String compartmentId, Mu } @Override - public Resource doInvoke(FHIROperationContext operationContext, String resourceTypeName, String logicalId, String versionId, String operationName, + public Resource doInvoke(FHIROperationContext operationContext, String resourceTypeName, String logicalId, String versionId, Resource resource, MultivaluedMap queryParameters) throws Exception { return null; @@ -198,7 +196,7 @@ public void validateInteraction(Interaction interaction, String resourceType) th } @Override - public FHIRRestOperationResponse doCreateMeta(FHIRPersistenceEvent event, List warnings, String type, Resource resource, + public FHIRRestOperationResponse doCreateMeta(FHIRPersistenceEvent event, List warnings, String type, Resource resource, String ifNoneExist) throws Exception { return null; } diff --git a/operation/fhir-operation-everything/pom.xml b/operation/fhir-operation-everything/pom.xml index 6d7c158854f..243dd4c4466 100644 --- a/operation/fhir-operation-everything/pom.xml +++ b/operation/fhir-operation-everything/pom.xml @@ -15,8 +15,27 @@ ${project.groupId} - fhir-server + fhir-server-spi ${project.version} + provided + + + ${project.groupId} + fhir-model + ${project.version} + provided + + + ${project.groupId} + fhir-search + ${project.version} + provided + + + ${project.groupId} + fhir-persistence + ${project.version} + provided @@ -28,6 +47,6 @@ org.testng testng test - + diff --git a/operation/fhir-operation-everything/src/main/java/com/ibm/fhir/operation/everything/EverythingOperation.java b/operation/fhir-operation-everything/src/main/java/com/ibm/fhir/operation/everything/EverythingOperation.java index 49fecd992c1..757c7277533 100644 --- a/operation/fhir-operation-everything/src/main/java/com/ibm/fhir/operation/everything/EverythingOperation.java +++ b/operation/fhir-operation-everything/src/main/java/com/ibm/fhir/operation/everything/EverythingOperation.java @@ -45,10 +45,10 @@ import com.ibm.fhir.search.compartment.CompartmentUtil; import com.ibm.fhir.search.exception.FHIRSearchException; import com.ibm.fhir.search.exception.SearchExceptionUtil; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; /** diff --git a/operation/fhir-operation-everything/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/operation/fhir-operation-everything/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from operation/fhir-operation-everything/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to operation/fhir-operation-everything/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation diff --git a/operation/fhir-operation-healthcheck/pom.xml b/operation/fhir-operation-healthcheck/pom.xml index 065d7f3ce30..e583bdd6ff3 100644 --- a/operation/fhir-operation-healthcheck/pom.xml +++ b/operation/fhir-operation-healthcheck/pom.xml @@ -15,8 +15,21 @@ ${project.groupId} - fhir-server + fhir-server-spi ${project.version} + provided + + + ${project.groupId} + fhir-persistence + ${project.version} + provided + + + ${project.groupId} + fhir-model + ${project.version} + provided diff --git a/operation/fhir-operation-healthcheck/src/main/java/com/ibm/fhir/operation/healthcheck/HealthcheckOperation.java b/operation/fhir-operation-healthcheck/src/main/java/com/ibm/fhir/operation/healthcheck/HealthcheckOperation.java index 3ac4e4a7cd8..0d47ddf6486 100644 --- a/operation/fhir-operation-healthcheck/src/main/java/com/ibm/fhir/operation/healthcheck/HealthcheckOperation.java +++ b/operation/fhir-operation-healthcheck/src/main/java/com/ibm/fhir/operation/healthcheck/HealthcheckOperation.java @@ -22,10 +22,10 @@ import com.ibm.fhir.model.type.code.IssueSeverity; import com.ibm.fhir.persistence.FHIRPersistence; import com.ibm.fhir.persistence.FHIRPersistenceTransaction; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class HealthcheckOperation extends AbstractOperation { diff --git a/operation/fhir-operation-healthcheck/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/operation/fhir-operation-healthcheck/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from operation/fhir-operation-healthcheck/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to operation/fhir-operation-healthcheck/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation diff --git a/operation/fhir-operation-reindex/pom.xml b/operation/fhir-operation-reindex/pom.xml index 3de92db8916..577b34cff08 100644 --- a/operation/fhir-operation-reindex/pom.xml +++ b/operation/fhir-operation-reindex/pom.xml @@ -15,8 +15,15 @@ ${project.groupId} - fhir-server + fhir-server-spi ${project.version} + provided + + + ${project.groupId} + fhir-model + ${project.version} + provided diff --git a/operation/fhir-operation-reindex/src/main/java/com/ibm/fhir/operation/reindex/ReindexOperation.java b/operation/fhir-operation-reindex/src/main/java/com/ibm/fhir/operation/reindex/ReindexOperation.java index 484f0696982..53d222d04b2 100644 --- a/operation/fhir-operation-reindex/src/main/java/com/ibm/fhir/operation/reindex/ReindexOperation.java +++ b/operation/fhir-operation-reindex/src/main/java/com/ibm/fhir/operation/reindex/ReindexOperation.java @@ -30,10 +30,10 @@ import com.ibm.fhir.model.type.code.IssueSeverity; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.model.util.ModelSupport; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; /** * Custom operation to invoke the persistence layer reindexing of resources diff --git a/operation/fhir-operation-reindex/src/main/java/com/ibm/fhir/operation/reindex/RetrieveIndexOperation.java b/operation/fhir-operation-reindex/src/main/java/com/ibm/fhir/operation/reindex/RetrieveIndexOperation.java index 3310c06ac5a..1a43084fa3d 100644 --- a/operation/fhir-operation-reindex/src/main/java/com/ibm/fhir/operation/reindex/RetrieveIndexOperation.java +++ b/operation/fhir-operation-reindex/src/main/java/com/ibm/fhir/operation/reindex/RetrieveIndexOperation.java @@ -25,10 +25,10 @@ import com.ibm.fhir.model.resource.OperationDefinition; import com.ibm.fhir.model.resource.Parameters; import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; /** * Custom operation to invoke the persistence layer to retrieve a list of index IDs. diff --git a/operation/fhir-operation-reindex/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/operation/fhir-operation-reindex/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from operation/fhir-operation-reindex/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to operation/fhir-operation-reindex/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation diff --git a/operation/fhir-operation-test/pom.xml b/operation/fhir-operation-test/pom.xml index e25dd0215ef..1da180a7488 100644 --- a/operation/fhir-operation-test/pom.xml +++ b/operation/fhir-operation-test/pom.xml @@ -19,8 +19,15 @@ ${project.groupId} - fhir-server + fhir-server-spi ${project.version} + provided + + + ${project.groupId} + fhir-model + ${project.version} + provided org.testng diff --git a/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/BadOperation.java b/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/BadOperation.java index 99ce09a2cb5..cd960101688 100644 --- a/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/BadOperation.java +++ b/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/BadOperation.java @@ -14,9 +14,9 @@ import com.ibm.fhir.model.resource.OperationDefinition; import com.ibm.fhir.model.resource.Parameters; import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; /** * This class will test what happens if there is a bad OperationDefinition defined for a custom operation.
diff --git a/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/CrashingOperation.java b/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/CrashingOperation.java index 0d82c6543f9..6be531d6551 100644 --- a/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/CrashingOperation.java +++ b/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/CrashingOperation.java @@ -10,9 +10,9 @@ import com.ibm.fhir.model.resource.OperationDefinition; import com.ibm.fhir.model.resource.Parameters; import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; /** * This class will test what happens if there is an Operation that fails to initialize. diff --git a/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/FHIROperationUtilTest.java b/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/FHIROperationUtilTest.java index 29179125067..a82ea14df5f 100644 --- a/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/FHIROperationUtilTest.java +++ b/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/FHIROperationUtilTest.java @@ -27,7 +27,7 @@ import com.ibm.fhir.model.resource.OperationDefinition; import com.ibm.fhir.model.resource.Parameters; import com.ibm.fhir.model.resource.Resource; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; /** * @author pbastide diff --git a/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/MyOperation.java b/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/MyOperation.java index c106289d233..0d32c6e8778 100644 --- a/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/MyOperation.java +++ b/operation/fhir-operation-test/src/test/java/com/ibm/fhir/operation/test/MyOperation.java @@ -25,9 +25,9 @@ import com.ibm.fhir.model.type.code.OperationKind; import com.ibm.fhir.model.type.code.OperationParameterUse; import com.ibm.fhir.model.type.code.PublicationStatus; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; public class MyOperation extends AbstractOperation { diff --git a/operation/fhir-operation-test/src/test/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/operation/fhir-operation-test/src/test/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from operation/fhir-operation-test/src/test/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to operation/fhir-operation-test/src/test/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation diff --git a/operation/fhir-operation-validate/pom.xml b/operation/fhir-operation-validate/pom.xml index ff67c287b61..5d28a59b213 100644 --- a/operation/fhir-operation-validate/pom.xml +++ b/operation/fhir-operation-validate/pom.xml @@ -15,8 +15,15 @@ ${project.groupId} - fhir-server + fhir-server-spi ${project.version} + provided + + + ${project.groupId} + fhir-validation + ${project.version} + provided diff --git a/operation/fhir-operation-validate/src/main/java/com/ibm/fhir/operation/validate/ValidateOperation.java b/operation/fhir-operation-validate/src/main/java/com/ibm/fhir/operation/validate/ValidateOperation.java index 741b62eb65a..97e216d9d0a 100644 --- a/operation/fhir-operation-validate/src/main/java/com/ibm/fhir/operation/validate/ValidateOperation.java +++ b/operation/fhir-operation-validate/src/main/java/com/ibm/fhir/operation/validate/ValidateOperation.java @@ -26,10 +26,10 @@ import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.model.type.code.NarrativeStatus; import com.ibm.fhir.registry.FHIRRegistry; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; import com.ibm.fhir.validation.FHIRValidator; public class ValidateOperation extends AbstractOperation { diff --git a/operation/fhir-operation-validate/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/operation/fhir-operation-validate/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from operation/fhir-operation-validate/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to operation/fhir-operation-validate/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation diff --git a/term/operation/fhir-operation-term-cache/pom.xml b/term/operation/fhir-operation-term-cache/pom.xml index 1315c344d62..c01fefdb65a 100644 --- a/term/operation/fhir-operation-term-cache/pom.xml +++ b/term/operation/fhir-operation-term-cache/pom.xml @@ -14,11 +14,13 @@ ${project.groupId} fhir-term ${project.version} + provided
${project.groupId} fhir-server ${project.version} + provided ${project.groupId} diff --git a/term/operation/fhir-operation-term-cache/src/main/java/com/ibm/fhir/operation/term/cache/CodeSystemClearCacheOperation.java b/term/operation/fhir-operation-term-cache/src/main/java/com/ibm/fhir/operation/term/cache/CodeSystemClearCacheOperation.java index 5cf46c7d125..65692a9d309 100644 --- a/term/operation/fhir-operation-term-cache/src/main/java/com/ibm/fhir/operation/term/cache/CodeSystemClearCacheOperation.java +++ b/term/operation/fhir-operation-term-cache/src/main/java/com/ibm/fhir/operation/term/cache/CodeSystemClearCacheOperation.java @@ -26,10 +26,10 @@ import com.ibm.fhir.model.type.code.IssueSeverity; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.operation.term.AbstractTermOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; import com.ibm.fhir.server.registry.ServerRegistryResourceProvider; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; import com.ibm.fhir.term.util.CodeSystemSupport; public class CodeSystemClearCacheOperation extends AbstractTermOperation { diff --git a/term/operation/fhir-operation-term-cache/src/main/java/com/ibm/fhir/operation/term/cache/ValueSetClearCacheOperation.java b/term/operation/fhir-operation-term-cache/src/main/java/com/ibm/fhir/operation/term/cache/ValueSetClearCacheOperation.java index 774cdeecbac..3407d0a2a4b 100644 --- a/term/operation/fhir-operation-term-cache/src/main/java/com/ibm/fhir/operation/term/cache/ValueSetClearCacheOperation.java +++ b/term/operation/fhir-operation-term-cache/src/main/java/com/ibm/fhir/operation/term/cache/ValueSetClearCacheOperation.java @@ -24,9 +24,9 @@ import com.ibm.fhir.model.type.code.IssueSeverity; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.operation.term.AbstractTermOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; -import com.ibm.fhir.server.util.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIROperationUtil; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; import com.ibm.fhir.term.util.ValueSetSupport; public class ValueSetClearCacheOperation extends AbstractTermOperation { diff --git a/term/operation/fhir-operation-term-cache/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/term/operation/fhir-operation-term-cache/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from term/operation/fhir-operation-term-cache/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to term/operation/fhir-operation-term-cache/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation diff --git a/term/operation/fhir-operation-term/pom.xml b/term/operation/fhir-operation-term/pom.xml index 862a29b70df..7d39874d6d1 100644 --- a/term/operation/fhir-operation-term/pom.xml +++ b/term/operation/fhir-operation-term/pom.xml @@ -14,11 +14,19 @@ ${project.groupId} fhir-term ${project.version} + provided ${project.groupId} - fhir-server + fhir-server-spi ${project.version} + provided + + + ${project.groupId} + fhir-persistence + ${project.version} + provided
diff --git a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/AbstractTermOperation.java b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/AbstractTermOperation.java index e4d7b49297e..26a6a70ff5b 100644 --- a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/AbstractTermOperation.java +++ b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/AbstractTermOperation.java @@ -20,9 +20,9 @@ import com.ibm.fhir.model.type.Uri; import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.registry.FHIRRegistry; -import com.ibm.fhir.server.operation.spi.AbstractOperation; -import com.ibm.fhir.server.operation.spi.FHIROperationContext; -import com.ibm.fhir.server.operation.spi.FHIRResourceHelpers; +import com.ibm.fhir.server.spi.operation.AbstractOperation; +import com.ibm.fhir.server.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; import com.ibm.fhir.term.service.FHIRTermService; public abstract class AbstractTermOperation extends AbstractOperation { diff --git a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/ClosureOperation.java b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/ClosureOperation.java index 6160a058dca..eac093bef52 100644 --- a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/ClosureOperation.java +++ b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/ClosureOperation.java @@ -8,7 +8,7 @@ import static com.ibm.fhir.model.type.String.string; import static com.ibm.fhir.model.util.ModelSupport.FHIR_STRING; -import static com.ibm.fhir.server.util.FHIROperationUtil.getOutputParameters; +import static com.ibm.fhir.server.spi.operation.FHIROperationUtil.getOutputParameters; import java.time.ZoneOffset; import java.util.LinkedHashSet; @@ -35,8 +35,8 @@ import com.ibm.fhir.model.type.code.IssueType; import com.ibm.fhir.model.type.code.PublicationStatus; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; import com.ibm.fhir.term.service.exception.FHIRTermServiceException; /** diff --git a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/CodeSystemValidateCodeOperation.java b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/CodeSystemValidateCodeOperation.java index 1ef6240b122..c92b07f3be2 100644 --- a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/CodeSystemValidateCodeOperation.java +++ b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/CodeSystemValidateCodeOperation.java @@ -16,8 +16,8 @@ import com.ibm.fhir.model.type.Element; import com.ibm.fhir.model.type.code.IssueType; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; import com.ibm.fhir.term.service.ValidationOutcome; import com.ibm.fhir.term.service.ValidationParameters; import com.ibm.fhir.term.service.exception.FHIRTermServiceException; diff --git a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/ExpandOperation.java b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/ExpandOperation.java index 7fc21f993c7..6ff616baa2f 100644 --- a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/ExpandOperation.java +++ b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/ExpandOperation.java @@ -6,7 +6,7 @@ package com.ibm.fhir.operation.term; -import static com.ibm.fhir.server.util.FHIROperationUtil.getOutputParameters; +import static com.ibm.fhir.server.spi.operation.FHIROperationUtil.getOutputParameters; import static com.ibm.fhir.term.util.ValueSetSupport.isExpanded; import com.ibm.fhir.exception.FHIROperationException; @@ -16,8 +16,8 @@ import com.ibm.fhir.model.resource.ValueSet; import com.ibm.fhir.model.type.code.IssueType; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; import com.ibm.fhir.term.service.ExpansionParameters; import com.ibm.fhir.term.service.exception.FHIRTermServiceException; diff --git a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/LookupOperation.java b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/LookupOperation.java index d09ba95f189..611b5b28012 100644 --- a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/LookupOperation.java +++ b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/LookupOperation.java @@ -22,8 +22,8 @@ import com.ibm.fhir.model.type.code.IssueSeverity; import com.ibm.fhir.model.type.code.IssueType; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; import com.ibm.fhir.term.service.LookupOutcome; import com.ibm.fhir.term.service.LookupParameters; import com.ibm.fhir.term.service.exception.FHIRTermServiceException; diff --git a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/SubsumesOperation.java b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/SubsumesOperation.java index cdb12b6f3c4..9defad3d840 100644 --- a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/SubsumesOperation.java +++ b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/SubsumesOperation.java @@ -18,8 +18,8 @@ import com.ibm.fhir.model.type.code.ConceptSubsumptionOutcome; import com.ibm.fhir.model.type.code.IssueType; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; import com.ibm.fhir.term.service.exception.FHIRTermServiceException; public class SubsumesOperation extends AbstractTermOperation { diff --git a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/TranslateOperation.java b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/TranslateOperation.java index 0f884075db9..e7aba0c2545 100644 --- a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/TranslateOperation.java +++ b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/TranslateOperation.java @@ -16,8 +16,8 @@ import com.ibm.fhir.model.type.Element; import com.ibm.fhir.model.type.code.IssueType; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; import com.ibm.fhir.term.service.TranslationOutcome; import com.ibm.fhir.term.service.TranslationParameters; import com.ibm.fhir.term.service.exception.FHIRTermServiceException; diff --git a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/ValueSetValidateCodeOperation.java b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/ValueSetValidateCodeOperation.java index 2264a3b2767..215b18dd754 100644 --- a/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/ValueSetValidateCodeOperation.java +++ b/term/operation/fhir-operation-term/src/main/java/com/ibm/fhir/operation/term/ValueSetValidateCodeOperation.java @@ -16,8 +16,8 @@ import com.ibm.fhir.model.type.Element; import com.ibm.fhir.model.type.code.IssueType; 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.spi.operation.FHIROperationContext; +import com.ibm.fhir.server.spi.operation.FHIRResourceHelpers; import com.ibm.fhir.term.service.ValidationOutcome; import com.ibm.fhir.term.service.ValidationParameters; import com.ibm.fhir.term.service.exception.FHIRTermServiceException; diff --git a/term/operation/fhir-operation-term/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation b/term/operation/fhir-operation-term/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation similarity index 100% rename from term/operation/fhir-operation-term/src/main/resources/META-INF/services/com.ibm.fhir.server.operation.spi.FHIROperation rename to term/operation/fhir-operation-term/src/main/resources/META-INF/services/com.ibm.fhir.server.spi.operation.FHIROperation