diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/repository/Repository.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/repository/Repository.java new file mode 100644 index 00000000000..a95de2b450e --- /dev/null +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/repository/Repository.java @@ -0,0 +1,677 @@ +package ca.uhn.fhir.repository; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.i18n.Msg; +import ca.uhn.fhir.model.api.IQueryParameterType; +import ca.uhn.fhir.rest.api.MethodOutcome; +import ca.uhn.fhir.rest.server.exceptions.AuthenticationException; +import ca.uhn.fhir.rest.server.exceptions.ForbiddenOperationException; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import ca.uhn.fhir.rest.server.exceptions.NotImplementedOperationException; +import com.google.common.annotations.Beta; +import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseConformance; +import org.hl7.fhir.instance.model.api.IBaseParameters; +import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.instance.model.api.IIdType; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + *

+ * This API is under-going active development, so it should be considered beta-level. + *

+ * + *

+ * This interface is a Java rendition of the FHIR REST API. All FHIR operations are defined at the + * HTTP level, which is convenient from the specification point-of-view since FHIR is built on top + * of web standards. This does mean that a few HTTP specific considerations, such as transmitting + * side-band information through the HTTP headers, bleeds into this API. + *

+ * + *

+ * One particularly odd case are FHIR Bundle links. The specification describes these as opaque to + * the end-user, so a given FHIR repository implementation must be able to resolve those directly. + * See {@link Repository#link(Class, String)} + *

+ * + *

+ * This interface also chooses to ignore return headers for most cases, preferring to return the + * Java objects directly. In cases where this is not possible, or the additional headers are crucial + * information, HAPI's {@link MethodOutcome} is used. + *

+ * + *

+ * Implementations of this interface should prefer to throw the exceptions derived from + * {@link ca.uhn.fhir.rest.server.exceptions.BaseServerResponseException} + * + * All operations may throw {@link AuthenticationException}, {@link ForbiddenOperationException}, or + * {@link InternalErrorException} in addition to operation-specific exceptions. + *

+ * + *

+ * If a given operation is not supported, implementations should throw an + * {@link NotImplementedOperationException}. The capabilities operation, if supported, should return + * the set of supported interactions. If capabilities is not supported, the components in this + * repository will try to invoke operations with "sensible" defaults. For example, by using the + * standard FHIR search parameters. Discussion is on-going to determine what a "sensible" minimal + * level of support for interactions should be. + *

+ * + * @see FHIR REST API + */ +@Beta +public interface Repository { + + // CRUD starts here + + /** + * Reads a resource from the repository + * + * @see FHIR read + * @see FHIR vRead + * + * @param a Resource type + * @param an Id type + * @param resourceType the class of the Resource type to read + * @param id the id of the Resource to read + * @return the Resource + */ + default T read(Class resourceType, I id) { + return this.read(resourceType, id, Collections.emptyMap()); + } + + /** + * Reads a Resource from the repository + * + * @see FHIR read + * @see FHIR vRead + * + * @param a Resource type + * @param an Id type + * @param resourceType the class of the Resource type to read + * @param id the id of the Resource to read + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return the Resource + */ + T read(Class resourceType, I id, Map headers); + + /** + * Creates a Resource in the repository + * + * @see FHIR create + * + * @param a Resource type + * @param resource the Resource to create + * @return a MethodOutcome with the id of the created Resource + */ + default MethodOutcome create(T resource) { + return this.create(resource, Collections.emptyMap()); + } + + /** + * Creates a Resource in the repository + * + * @see FHIR create + * + * @param a Resource type + * @param resource the Resource to create + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a MethodOutcome with the id of the created Resource + */ + MethodOutcome create(T resource, Map headers); + + /** + * Patches a Resource in the repository + * + * @see FHIR patch + * + * @param an Id type + * @param

a Parameters type + * @param id the id of the Resource to patch + * @param patchParameters parameters describing the patches to apply + * @return a MethodOutcome with the id of the patched resource + */ + default MethodOutcome patch(I id, P patchParameters) { + return this.patch(id, patchParameters, Collections.emptyMap()); + } + + /** + * Patches a Resource in the repository + * + * @see FHIR patch + * + * @param an Id type + * @param

a Parameters type + * @param id the id of the Resource to patch + * @param patchParameters parameters describing the patches to apply + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a MethodOutcome with the id of the patched resource + */ + default MethodOutcome patch( + I id, P patchParameters, Map headers) { + return throwNotImplementedOperationException("patch is not supported by this repository"); + } + + /** + * Updates a Resource in the repository + * + * @see FHIR update + * + * @param a Resource type + * @param resource the Resource to update + * @return a MethodOutcome with the id of the updated Resource + */ + default MethodOutcome update(T resource) { + return this.update(resource, Collections.emptyMap()); + } + + /** + * Updates a Resource in the repository + * + * @see FHIR update + * + * @param a Resource type + * @param resource the Resource to update + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a MethodOutcome with the id of the updated Resource + */ + MethodOutcome update(T resource, Map headers); + + /** + * Deletes a Resource in the repository + * + * @see FHIR delete + * + * @param a Resource type + * @param an Id type + * @param resourceType the class of the Resource type to delete + * @param id the id of the Resource to delete + * @return a MethodOutcome with the id of the deleted resource + */ + default MethodOutcome delete(Class resourceType, I id) { + return this.delete(resourceType, id, Collections.emptyMap()); + } + + /** + * Deletes a Resource in the repository + * + * @see FHIR delete + * + * @param a Resource type + * @param an Id type + * @param resourceType the class of the Resource type to delete + * @param id the id of the Resource to delete + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a MethodOutcome with the id of the deleted resource + */ + MethodOutcome delete( + Class resourceType, I id, Map headers); + + // Querying starts here + + /** + * Searches this repository + * + * @see FHIR search + * + * @param a Bundle type + * @param a Resource type + * @param bundleType the class of the Bundle type to return + * @param resourceType the class of the Resource type to search + * @param searchParameters the searchParameters for this search + * @return a Bundle with the results of the search + */ + default B search( + Class bundleType, Class resourceType, Map> searchParameters) { + return this.search(bundleType, resourceType, searchParameters, Collections.emptyMap()); + } + + /** + * Searches this repository + * + * @see FHIR search + * + * @param a Bundle type + * @param a Resource type + * @param bundleType the class of the Bundle type to return + * @param resourceType the class of the Resource type to search + * @param searchParameters the searchParameters for this search + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a Bundle with the results of the search + */ + B search( + Class bundleType, + Class resourceType, + Map> searchParameters, + Map headers); + + // Paging starts here + + /** + * Reads a Bundle from a link on this repository + * + * This is typically used for paging during searches + * + * @see FHIR Bundle + * link + * + * @param a Bundle type + * @param url the url of the Bundle to load + * @return a Bundle + */ + default B link(Class bundleType, String url) { + return this.link(bundleType, url, Collections.emptyMap()); + } + + /** + * Reads a Bundle from a link on this repository + * + * This is typically used for paging during searches + * + * @see FHIR Bundle + * link + * + * @param a Bundle type + * @param url the url of the Bundle to load + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a Bundle + */ + default B link(Class bundleType, String url, Map headers) { + return throwNotImplementedOperationException("link is not supported by this repository"); + } + + // Metadata starts here + + /** + * Returns the CapabilityStatement/Conformance metadata for this repository + * + * @see FHIR capabilities + * + * @param a CapabilityStatement/Conformance type + * @param resourceType the class of the CapabilityStatement/Conformance to return + * @return a CapabilityStatement/Conformance with the repository's metadata + */ + default C capabilities(Class resourceType) { + return this.capabilities(resourceType, Collections.emptyMap()); + } + + /** + * Returns the CapabilityStatement/Conformance metadata for this repository + * + * @see FHIR capabilities + * + * @param a CapabilityStatement/Conformance type + * @param resourceType the class of the CapabilityStatement/Conformance to return + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a CapabilityStatement/Conformance with the repository's metadata + */ + default C capabilities(Class resourceType, Map headers) { + return throwNotImplementedOperationException("capabilities is not supported by this repository"); + } + + // Transactions starts here + + /** + * Performs a transaction or batch on this repository + * + * @see FHIR transaction + * + * @param a Bundle type + * @param transaction a Bundle with the transaction/batch + * @return a Bundle with the results of the transaction/batch + */ + default B transaction(B transaction) { + return this.transaction(transaction, Collections.emptyMap()); + } + + /** + * Performs a transaction or batch on this repository + * + * @see FHIR transaction + * + * @param a Bundle type + * @param transaction a Bundle with the transaction/batch + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a Bundle with the results of the transaction/batch + */ + default B transaction(B transaction, Map headers) { + return throwNotImplementedOperationException("transaction is not supported by this repository"); + } + + // Operations starts here + + /** + * Invokes a server-level operation on this repository that returns a Resource + * + * @see FHIR operations + * + * @param a Resource type to return + * @param

a Parameters type for operation parameters + * @param name the name of the operation to invoke + * @param parameters the operation parameters + * @param returnType the class of the Resource the operation returns + * @return the results of the operation + */ + default R invoke( + String name, P parameters, Class returnType) { + return this.invoke(name, parameters, returnType, Collections.emptyMap()); + } + + /** + * Invokes a server-level operation on this repository that returns a Resource + * + * @see FHIR operations + * + * @param a Resource type to return + * @param

a Parameters type for operation parameters + * @param name the name of the operation to invoke + * @param parameters the operation parameters + * @param returnType the class of the Resource the operation returns + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return the results of the operation + */ + default R invoke( + String name, P parameters, Class returnType, Map headers) { + return throwNotImplementedOperationException("server-level invoke is not supported by this repository"); + } + + /** + * Invokes a server-level operation on this repository + * + * @see FHIR operations + * + * @param

a Parameters type for operation parameters + * @param name the name of the operation to invoke + * @param parameters the operation parameters + * @return a MethodOutcome with a status code + */ + default

MethodOutcome invoke(String name, P parameters) { + return this.invoke(name, parameters, Collections.emptyMap()); + } + + /** + * Invokes a server-level operation on this repository + * + * @see FHIR operations + * + * @param

a Parameters type for operation parameters + * @param name the name of the operation to invoke + * @param parameters the operation parameters + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a MethodOutcome with a status code + */ + default

MethodOutcome invoke(String name, P parameters, Map headers) { + return throwNotImplementedOperationException("server-level invoke is not supported by this repository"); + } + + /** + * Invokes a type-level operation on this repository that returns a Resource + * + * @see FHIR operations + * + * @param a Resource type to return + * @param

a Parameters type for operation parameters + * @param a Resource type to do the invocation for + * @param resourceType the class of the Resource to do the invocation for + * @param name the name of the operation to invoke + * @param parameters the operation parameters + * @param returnType the class of the Resource the operation returns + * @return the results of the operation + */ + default R invoke( + Class resourceType, String name, P parameters, Class returnType) { + return this.invoke(resourceType, name, parameters, returnType, Collections.emptyMap()); + } + + /** + * Invokes a type-level operation on this repository that returns a Resource + * + * @see FHIR operations + * + * @param a Resource type to return + * @param

a Parameters type for operation parameters + * @param a Resource type to do the invocation for + * @param resourceType the class of the Resource to do the invocation for + * @param name the name of the operation to invoke + * @param parameters the operation parameters + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @param returnType the class of the Resource the operation returns + * @return the results of the operation + */ + R invoke( + Class resourceType, String name, P parameters, Class returnType, Map headers); + + /** + * Invokes a type-level operation on this repository + * + * @see FHIR operations + * + * @param

a Parameters type for operation parameters + * @param a Resource type to do the invocation for + * @param resourceType the class of the Resource to do the invocation for + * @param name the name of the operation to invoke + * @param parameters the operation parameters + * @return a MethodOutcome with a status code + */ + default

MethodOutcome invoke( + Class resourceType, String name, P parameters) { + return this.invoke(resourceType, name, parameters, Collections.emptyMap()); + } + + /** + * Invokes a type-level operation on this repository + * + * @see FHIR operations + * + * @param

a Parameters type for operation parameters + * @param a Resource type to do the invocation for + * @param resourceType the class of the Resource to do the invocation for + * @param name the name of the operation to invoke + * @param parameters the operation parameters + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a MethodOutcome with a status code + */ + default

MethodOutcome invoke( + Class resourceType, String name, P parameters, Map headers) { + return throwNotImplementedOperationException("type-level invoke is not supported by this repository"); + } + + /** + * Invokes an instance-level operation on this repository that returns a Resource + * + * @see FHIR operations + * + * @param a Resource type to return + * @param

a Parameters type for operation parameters + * @param an Id type + * @param id the id of the Resource to do the invocation on + * @param name the name of the operation to invoke + * @param parameters the operation parameters + * @param returnType the class of the Resource the operation returns + * @return the results of the operation + */ + default R invoke( + I id, String name, P parameters, Class returnType) { + return this.invoke(id, name, parameters, returnType, Collections.emptyMap()); + } + + /** + * Invokes an instance-level operation on this repository that returns a Resource + * + * @see FHIR operations + * + * @param a Resource type to return + * @param

a Parameters type for operation parameters + * @param an Id type + * @param id the id of the Resource to do the invocation on + * @param name the name of the operation to invoke + * @param parameters the operation parameters + * @param returnType the class of the Resource the operation returns + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return the results of the operation + */ + R invoke( + I id, String name, P parameters, Class returnType, Map headers); + + /** + * Invokes an instance-level operation on this repository + * + * @see FHIR operations + * + * @param

a Parameters type for operation parameters + * @param an Id type + * @param id the id of the Resource to do the invocation on + * @param name the name of the operation to invoke + * @param parameters the operation parameters + * @return a MethodOutcome with a status code + */ + default

MethodOutcome invoke(I id, String name, P parameters) { + return this.invoke(id, name, parameters, Collections.emptyMap()); + } + + /** + * Invokes an instance-level operation on this repository + * + * @see FHIR operations + * + * @param

a Parameters type for operation parameters + * @param an Id type + * @param id the id of the Resource to do the invocation on + * @param name the name of the operation to invoke + * @param parameters the operation parameters + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a MethodOutcome with a status code + */ + default

MethodOutcome invoke( + I id, String name, P parameters, Map headers) { + return throwNotImplementedOperationException("instance-level invoke is not supported by this repository"); + } + + // History starts here + + /** + * Returns a Bundle with server-level history for this repository + * + * @see FHIR history + * + * @param a Bundle type to return + * @param

a Parameters type for input parameters + * @param parameters the parameters for this history interaction + * @param returnType the class of the Bundle type to return + * @return a Bundle with the server history + */ + default B history(P parameters, Class returnType) { + return this.history(parameters, returnType, Collections.emptyMap()); + } + + /** + * Returns a Bundle with server-level history for this repository + * + * @see FHIR history + * + * @param a Bundle type to return + * @param

a Parameters type for input parameters + * @param parameters the parameters for this history interaction + * @param returnType the class of the Bundle type to return + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a Bundle with the server history + */ + default B history( + P parameters, Class returnType, Map headers) { + return throwNotImplementedOperationException("server-level history is not supported by this repository"); + } + + /** + * Returns a Bundle with type-level history for this repository + * + * @see FHIR history + * + * @param a Bundle type to return + * @param

a Parameters type for input parameters + * @param a Resource type to produce history for + * @param resourceType the class of the Resource type to produce history for + * @param parameters the parameters for this history interaction + * @param returnType the class of the Bundle type to return + * @return a Bundle with the type history + */ + default B history( + Class resourceType, P parameters, Class returnType) { + return this.history(resourceType, parameters, returnType, Collections.emptyMap()); + } + + /** + * Returns a Bundle with type-level history for this repository + * + * @see FHIR history + * + * @param a Bundle type to return + * @param

a Parameters type for input parameters + * @param a Resource type to produce history for + * @param resourceType the class of the Resource type to produce history for + * @param parameters the parameters for this history interaction + * @param returnType the class of the Bundle type to return + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a Bundle with the type history + */ + default B history( + Class resourceType, P parameters, Class returnType, Map headers) { + return throwNotImplementedOperationException("type-level history is not supported by this repository"); + } + + /** + * Returns a Bundle with instance-level history + * + * @see FHIR history + * + * @param a Bundle type to return + * @param

a Parameters type for input parameters + * @param an Id type for the Resource to produce history for + * @param id the id of the Resource type to produce history for + * @param parameters the parameters for this history interaction + * @param returnType the class of the Bundle type to return + * @return a Bundle with the instance history + */ + default B history( + I id, P parameters, Class returnType) { + return this.history(id, parameters, returnType, Collections.emptyMap()); + } + + /** + * Returns a Bundle with instance-level history + * + * @see FHIR history + * + * @param a Bundle type to return + * @param

a Parameters type for input parameters + * @param an Id type for the Resource to produce history for + * @param id the id of the Resource type to produce history for + * @param parameters the parameters for this history interaction + * @param returnType the class of the Bundle type to return + * @param headers headers for this request, typically key-value pairs of HTTP headers + * @return a Bundle with the instance history + */ + default B history( + I id, P parameters, Class returnType, Map headers) { + return throwNotImplementedOperationException("instance-level history is not supported by this repository"); + } + + /** + * Returns the {@link FhirContext} used by the repository + * + * Practically, implementing FHIR functionality with the HAPI toolset requires a FhirContext. In + * particular for things like version independent code. Ideally, a user could which FHIR version a + * repository was configured for using things like the CapabilityStatement. In practice, that's + * not widely implemented (yet) and it's expensive to create a new context with every call. We + * will probably revisit this in the future. + * + * @return a FhirContext + */ + FhirContext fhirContext(); + + private static T throwNotImplementedOperationException(String theMessage) { + throw new NotImplementedOperationException(Msg.code(2542) + theMessage); + } +} diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/6140-common-data-access-api.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/6140-common-data-access-api.yaml new file mode 100644 index 00000000000..bfca99df2f5 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/7_4_0/6140-common-data-access-api.yaml @@ -0,0 +1,7 @@ +--- +type: change +issue: 6140 +title: "An prototype interface to abstract data access across different types + of FHIR repositories (e.g. remote REST, local JPA) has been added to the `hapi-fhir-base` project. + Implementations of this interface will follow in future HAPI releases, and it will continue to evolve + as it's validated through implementation."