diff --git a/onfhir-client/src/main/scala/io/onfhir/client/OnFhirNetworkClient.scala b/onfhir-client/src/main/scala/io/onfhir/client/OnFhirNetworkClient.scala index 1102e1b4..52eccd5a 100644 --- a/onfhir-client/src/main/scala/io/onfhir/client/OnFhirNetworkClient.scala +++ b/onfhir-client/src/main/scala/io/onfhir/client/OnFhirNetworkClient.scala @@ -5,31 +5,33 @@ import akka.http.scaladsl.Http import akka.http.scaladsl.model.{HttpRequest, HttpResponse, Uri} import akka.stream.Materializer import com.typesafe.config.Config -import io.onfhir.api.client.{BaseFhirClient, FHIRPaginatedBundle, FhirClientException, FhirHistoryRequestBuilder, FhirSearchRequestBuilder, IFhirBundleReturningRequestBuilder} +import io.onfhir.api.client._ import io.onfhir.api.model.{FHIRRequest, FHIRResponse} import io.onfhir.client.intrcp.{BasicAuthenticationInterceptor, BearerTokenInterceptorFromTokenEndpoint, FixedBasicTokenInterceptor} import io.onfhir.client.parsers.{FHIRRequestMarshaller, FHIRResponseUnmarshaller} import org.slf4j.{Logger, LoggerFactory} -import scala.annotation.unused import scala.concurrent.{ExecutionContext, Future} import scala.util.Try -case class OnFhirNetworkClient(serverBaseUrl:String, interceptors:Seq[IHttpRequestInterceptor] = Nil)(implicit actorSystem: ActorSystem) extends BaseFhirClient { - val logger:Logger = LoggerFactory.getLogger(this.getClass) - implicit val ex:ExecutionContext = actorSystem.dispatcher +case class OnFhirNetworkClient(serverBaseUrl: String, interceptors: Seq[IHttpRequestInterceptor] = Nil)(implicit actorSystem: ActorSystem) extends BaseFhirClient { + val logger: Logger = LoggerFactory.getLogger(this.getClass) + + implicit val ex: ExecutionContext = actorSystem.dispatcher implicit val materializer: Materializer = Materializer(actorSystem) - override def getBaseUrl(): String = serverBaseUrl + private val cleanServerBaseUrl = serverBaseUrl.stripSuffix("/") + + override def getBaseUrl(): String = cleanServerBaseUrl /** * Client with basic authentication - * - * @param username Username - * @param password Password + * + * @param username Username + * @param password Password * @return */ - def withBasicAuthentication(username:String, password:String):OnFhirNetworkClient = { + def withBasicAuthentication(username: String, password: String): OnFhirNetworkClient = { this.copy(interceptors = interceptors :+ new BasicAuthenticationInterceptor(username, password)) } @@ -45,29 +47,31 @@ case class OnFhirNetworkClient(serverBaseUrl:String, interceptors:Seq[IHttpReque /** * Client with OAuth2.0 Bearer Token authentication - * @param clientId Client identifier - * @param clientSecret Client secret - * @param requiredScopes Required scopes for the FHIR client to access the required data - * @param authzServerTokenEndpoint Authorization server token endpoint to request token - * @param clientAuthenticationMethod Client authentication method (client_secret_basic | client_secret_post | client_secret_jwt) + * + * @param clientId Client identifier + * @param clientSecret Client secret + * @param requiredScopes Required scopes for the FHIR client to access the required data + * @param authzServerTokenEndpoint Authorization server token endpoint to request token + * @param clientAuthenticationMethod Client authentication method (client_secret_basic | client_secret_post | client_secret_jwt) * @return */ - def withOpenIdBearerTokenAuthentication(clientId:String, clientSecret:String, requiredScopes:Seq[String], authzServerTokenEndpoint:String, clientAuthenticationMethod:String = "client_secret_basic"):OnFhirNetworkClient = { + def withOpenIdBearerTokenAuthentication(clientId: String, clientSecret: String, requiredScopes: Seq[String], authzServerTokenEndpoint: String, clientAuthenticationMethod: String = "client_secret_basic"): OnFhirNetworkClient = { this.copy(interceptors = interceptors :+ new BearerTokenInterceptorFromTokenEndpoint(clientId, clientSecret, requiredScopes, authzServerTokenEndpoint, clientAuthenticationMethod)) } /** * Execute the FHIR request and return FHIR response + * * @param fhirRequest FHIR request - * @return + * @return */ override def execute(fhirRequest: FHIRRequest): Future[FHIRResponse] = { FHIRRequestMarshaller - .marshallRequest(fhirRequest, serverBaseUrl) + .marshallRequest(fhirRequest, getBaseUrl()) .flatMap(httpRequest => executeHttpRequest(httpRequest)) .flatMap(httpResponse => FHIRResponseUnmarshaller.unmarshallResponse(httpResponse)) .recover { - case t:Throwable => + case t: Throwable => logger.error("Problem while executing FHIR request!", t) throw FhirClientException("Problem while executing FHIR request!" + t.getMessage) } @@ -75,8 +79,9 @@ case class OnFhirNetworkClient(serverBaseUrl:String, interceptors:Seq[IHttpReque /** * Execute the next page for the search - * @param bundle FHIR search result-set bundle - * @tparam T Concrete class for bundle representation + * + * @param bundle FHIR search result-set bundle + * @tparam T Concrete class for bundle representation * @return */ override def next[T <: FHIRPaginatedBundle](bundle: T): Future[T] = { @@ -97,21 +102,21 @@ case class OnFhirNetworkClient(serverBaseUrl:String, interceptors:Seq[IHttpReque //Set the new page bundle.request match { - case srb:FhirSearchRequestBuilder => srb.page = Some(paginationParam._1, paginationParam._2.head) - case hrb:FhirHistoryRequestBuilder => hrb.page = Some(paginationParam._1, paginationParam._2.head) + case srb: FhirSearchRequestBuilder => srb.page = Some(paginationParam._1, paginationParam._2.head) + case hrb: FhirHistoryRequestBuilder => hrb.page = Some(paginationParam._1, paginationParam._2.head) } FHIRRequestMarshaller - .marshallRequest(bundle.request.compileRequest(), serverBaseUrl) - .flatMap(httpRequest => executeHttpRequest(httpRequest)) + .marshallRequest(bundle.request.compileRequest(), getBaseUrl()) + .flatMap(httpRequest => executeHttpRequest(httpRequest)) //.marshallRequest(bundle.request.request, serverBaseUrl) /*.flatMap(httpRequest => executeHttpRequest(httpRequest.withUri(Uri.apply(bundle.getNext()))) )*/ .flatMap(httpResponse => FHIRResponseUnmarshaller.unmarshallResponse(httpResponse)) .recover { - case t:Throwable => + case t: Throwable => logger.error(s"Problem while executing FHIR request '${bundle.getNext()}'!", t) throw FhirClientException(s"Problem while executing FHIR request '${bundle.getNext()}'!" + t.getMessage) } @@ -125,11 +130,12 @@ case class OnFhirNetworkClient(serverBaseUrl:String, interceptors:Seq[IHttpReque /** * Execut the HTTP request while handling interceptors + * * @param httpRequest HTTP request constructed by the client * @return */ - private def executeHttpRequest(httpRequest:HttpRequest):Future[HttpResponse] = { - if(interceptors.isEmpty) + private def executeHttpRequest(httpRequest: HttpRequest): Future[HttpResponse] = { + if (interceptors.isEmpty) Http().singleRequest(httpRequest) else handleInterceptors(interceptors, httpRequest) @@ -138,14 +144,15 @@ case class OnFhirNetworkClient(serverBaseUrl:String, interceptors:Seq[IHttpReque /** * Recursively handle interceptors to update the request - * @param interceptors Configured request intercetors - * @param httpRequest HTTP request constructed by the client + * + * @param interceptors Configured request intercetors + * @param httpRequest HTTP request constructed by the client * @return */ - private def handleInterceptors(interceptors:Seq[IHttpRequestInterceptor], httpRequest: HttpRequest):Future[HttpRequest] = { + private def handleInterceptors(interceptors: Seq[IHttpRequestInterceptor], httpRequest: HttpRequest): Future[HttpRequest] = { val interceptor = interceptors.head val processedRequest = interceptor.processRequest(httpRequest) - if(interceptors.tail.isEmpty) + if (interceptors.tail.isEmpty) processedRequest else processedRequest @@ -158,45 +165,49 @@ case class OnFhirNetworkClient(serverBaseUrl:String, interceptors:Seq[IHttpReque object OnFhirNetworkClient { /** * Create an network FHIR client with the given server base url + * * @param serverBaseUrl FHIR server's base url * @return */ - def apply(serverBaseUrl:String)(implicit actorSystem: ActorSystem): OnFhirNetworkClient = new OnFhirNetworkClient(serverBaseUrl) + def apply(serverBaseUrl: String)(implicit actorSystem: ActorSystem): OnFhirNetworkClient = new OnFhirNetworkClient(serverBaseUrl) /** * Create an network FHIR client with the given server base url and http request interceptors - * @param serverBaseUrl FHIR server's base url - * @param interceptors Interceptors for the request that configures the HTTP Request before sending it - * (e.g. an authorization interceptor that insert certain headers to the HTTP request) + * + * @param serverBaseUrl FHIR server's base url + * @param interceptors Interceptors for the request that configures the HTTP Request before sending it + * (e.g. an authorization interceptor that insert certain headers to the HTTP request) * @return */ def apply(serverBaseUrl: String, interceptors: Seq[IHttpRequestInterceptor])(implicit actorSystem: ActorSystem): OnFhirNetworkClient = new OnFhirNetworkClient(serverBaseUrl, interceptors) /** * Create an network FHIR client with the given server base url and http request interceptor + * * @param serverBaseUrl FHIR server's base url * @param interceptor Interceptor for the request that configures the HTTP Request before sending it * (e.g. an authorization interceptor that insert certain headers to the HTTP request) * @return */ - def apply(serverBaseUrl: String, interceptor:IHttpRequestInterceptor)(implicit actorSystem: ActorSystem):OnFhirNetworkClient = new OnFhirNetworkClient(serverBaseUrl, Seq(interceptor)) + def apply(serverBaseUrl: String, interceptor: IHttpRequestInterceptor)(implicit actorSystem: ActorSystem): OnFhirNetworkClient = new OnFhirNetworkClient(serverBaseUrl, Seq(interceptor)) /** * Create the network client from the configuration + * * @param config * @param actorSystem * @return */ - def apply(config:Config)(implicit actorSystem: ActorSystem):OnFhirNetworkClient = { + def apply(config: Config)(implicit actorSystem: ActorSystem): OnFhirNetworkClient = { val authzInterceptor = Try(config.getConfig("authz")) - .toOption - .map(authzConfig => - authzConfig.getString("method") match { - case "basic" => BasicAuthenticationInterceptor.apply(authzConfig) - case "oauth2" => BearerTokenInterceptorFromTokenEndpoint.apply(authzConfig) - } - ) + .toOption + .map(authzConfig => + authzConfig.getString("method") match { + case "basic" => BasicAuthenticationInterceptor.apply(authzConfig) + case "oauth2" => BearerTokenInterceptorFromTokenEndpoint.apply(authzConfig) + } + ) val serverBaseUrl = config.getString("serverBaseUrl") new OnFhirNetworkClient(serverBaseUrl, authzInterceptor.toSeq) diff --git a/onfhir-core/src/main/scala/io/onfhir/api/client/OnFhirLocalClient.scala b/onfhir-core/src/main/scala/io/onfhir/api/client/OnFhirLocalClient.scala index fc7ada55..91b0d98a 100644 --- a/onfhir-core/src/main/scala/io/onfhir/api/client/OnFhirLocalClient.scala +++ b/onfhir-core/src/main/scala/io/onfhir/api/client/OnFhirLocalClient.scala @@ -11,7 +11,7 @@ import io.onfhir.server.ErrorHandler object OnFhirLocalClient extends BaseFhirClient { implicit val executionContext: ExecutionContextExecutor = Onfhir.actorSystem.dispatcher - override def getBaseUrl(): String = OnfhirConfig.fhirRootUrl + override def getBaseUrl(): String = OnfhirConfig.fhirRootUrl.stripSuffix("/") /** * Execute a FHIR Request internally and return FHIR Response