mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 11:06:49 +00:00
feature/(http4s): add proxy to lift for parity testing
Add a proxy route in Http4s500 to forward unmatched requests to the legacy Lift framework, enabling contract parity testing between the two implementations. This allows new http4s endpoints to be tested against existing Lift behavior. Update V500ContractParityTest to include a test for the private accounts endpoint, verifying both implementations return consistent responses. Simplify assertion syntax from `should not be empty` to `isDefined shouldBe true` for clarity.
This commit is contained in:
parent
97ccc77a86
commit
9a6368bf80
@ -5,6 +5,7 @@ import cats.effect._
|
||||
import code.api.Constant._
|
||||
import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._
|
||||
import code.api.util.APIUtil.{EmptyBody, ResourceDoc}
|
||||
import code.api.util.APIUtil
|
||||
import code.api.util.ApiTag._
|
||||
import code.api.util.ErrorMessages._
|
||||
import code.api.util.http4s.ResourceDocMiddleware
|
||||
@ -19,6 +20,8 @@ import com.openbankproject.commons.model.BankId
|
||||
import com.openbankproject.commons.model.ProductCode
|
||||
import com.openbankproject.commons.dto.GetProductsParam
|
||||
import com.openbankproject.commons.util.{ApiVersion, ApiVersionStatus, ScannedApiVersion}
|
||||
import dispatch.{Http => DispatchHttp, as => DispatchAs, url => DispatchUrl}
|
||||
import java.nio.charset.StandardCharsets
|
||||
import net.liftweb.json.JsonAST.prettyRender
|
||||
import net.liftweb.json.{Extraction, Formats}
|
||||
import org.http4s._
|
||||
@ -54,6 +57,8 @@ object Http4s500 {
|
||||
object Implementations5_0_0 {
|
||||
|
||||
val prefixPath = Root / ApiPathZero.toString / implementedInApiVersion.toString
|
||||
private val prefixPathString = s"/${ApiPathZero.toString}/${implementedInApiVersion.toString}"
|
||||
private val liftProxyBaseUrl = APIUtil.getPropsValue("http4s.lift_proxy_base_url", "http://localhost:8080")
|
||||
|
||||
resourceDocs += ResourceDoc(
|
||||
null,
|
||||
@ -219,6 +224,42 @@ object Http4s500 {
|
||||
}
|
||||
}
|
||||
|
||||
private def proxyToLift(req: Request[IO]): IO[Response[IO]] = {
|
||||
val targetUrl = liftProxyBaseUrl.stripSuffix("/") + req.uri.renderString
|
||||
val filteredHeaders = req.headers.headers
|
||||
.filterNot(h => {
|
||||
val name = h.name.toString.toLowerCase
|
||||
name == "host" || name == "content-length" || name == "transfer-encoding"
|
||||
})
|
||||
.map(h => h.name.toString -> h.value)
|
||||
.toMap
|
||||
|
||||
for {
|
||||
body <- req.bodyText.compile.string
|
||||
dispatchReq = (
|
||||
DispatchUrl(targetUrl)
|
||||
.setMethod(req.method.name)
|
||||
.setBodyEncoding(StandardCharsets.UTF_8)
|
||||
.setBody(body)
|
||||
<:< filteredHeaders
|
||||
)
|
||||
liftResp <- IO.fromFuture(IO(DispatchHttp.default(dispatchReq > DispatchAs.Response(p => p))))
|
||||
status = org.http4s.Status.fromInt(liftResp.getStatusCode).getOrElse(org.http4s.Status.InternalServerError)
|
||||
responseBody = liftResp.getResponseBody
|
||||
correlationHeader = Option(liftResp.getHeader("Correlation-Id")).filter(_.nonEmpty)
|
||||
base = Response[IO](status).withEntity(responseBody)
|
||||
withCorrelation = correlationHeader match {
|
||||
case Some(value) => base.putHeaders(Header.Raw(org.typelevel.ci.CIString("Correlation-Id"), value))
|
||||
case None => base
|
||||
}
|
||||
} yield withCorrelation
|
||||
}
|
||||
|
||||
val proxy: HttpRoutes[IO] = HttpRoutes.of[IO] {
|
||||
case req if req.uri.path.renderString.startsWith(prefixPathString) =>
|
||||
proxyToLift(req)
|
||||
}
|
||||
|
||||
val allRoutes: HttpRoutes[IO] =
|
||||
Kleisli[HttpF, Request[IO], Response[IO]] { req: Request[IO] =>
|
||||
root(req)
|
||||
@ -226,6 +267,7 @@ object Http4s500 {
|
||||
.orElse(getBank(req))
|
||||
.orElse(getProducts(req))
|
||||
.orElse(getProduct(req))
|
||||
.orElse(proxy(req))
|
||||
}
|
||||
|
||||
val allRoutesWithMiddleware: HttpRoutes[IO] =
|
||||
|
||||
@ -3,10 +3,13 @@ package code.api.v5_0_0
|
||||
import cats.effect.IO
|
||||
import cats.effect.unsafe.implicits.global
|
||||
import code.api.util.APIUtil
|
||||
import code.api.util.APIUtil.OAuth._
|
||||
import net.liftweb.json.JValue
|
||||
import net.liftweb.json.JsonAST.{JArray, JField, JObject, JString}
|
||||
import net.liftweb.json.JsonParser.parse
|
||||
import org.http4s.{Method, Request, Status, Uri}
|
||||
import org.http4s.Header
|
||||
import org.typelevel.ci.CIString
|
||||
import org.scalatest.Tag
|
||||
|
||||
class V500ContractParityTest extends V500ServerSetup {
|
||||
@ -146,17 +149,49 @@ class V500ContractParityTest extends V500ServerSetup {
|
||||
|
||||
liftResponse.body match {
|
||||
case JObject(fields) =>
|
||||
toFieldMap(fields).get("message") should not be empty
|
||||
toFieldMap(fields).get("message").isDefined shouldBe true
|
||||
case _ =>
|
||||
fail("Expected Lift JSON object for missing product error")
|
||||
}
|
||||
|
||||
http4sJson match {
|
||||
case JObject(fields) =>
|
||||
toFieldMap(fields).get("message") should not be empty
|
||||
toFieldMap(fields).get("message").isDefined shouldBe true
|
||||
case _ =>
|
||||
fail("Expected http4s JSON object for missing product error")
|
||||
}
|
||||
}
|
||||
|
||||
scenario("private accounts endpoint is served (proxy parity)", V500ContractParityTag) {
|
||||
val bankId = APIUtil.defaultBankId
|
||||
val liftResponse = getPrivateAccounts(bankId, user1)
|
||||
val liftReq = (v5_0_0_Request / "banks" / bankId / "accounts" / "private").GET <@(user1)
|
||||
val reqData = extractParamsAndHeaders(liftReq, "", "")
|
||||
|
||||
val baseRequest = Request[IO](
|
||||
method = Method.GET,
|
||||
uri = Uri.unsafeFromString(s"/obp/v5.0.0/banks/$bankId/accounts/private")
|
||||
)
|
||||
val request = reqData.headers.foldLeft(baseRequest) { case (r, (k, v)) =>
|
||||
r.putHeaders(Header.Raw(CIString(k), v))
|
||||
}
|
||||
|
||||
val response = Http4s500.wrappedRoutesV500Services.orNotFound.run(request).unsafeRunSync()
|
||||
val http4sStatus = response.status
|
||||
val body = response.as[String].unsafeRunSync()
|
||||
val http4sJson = if (body.trim.isEmpty) JObject(Nil) else parse(body)
|
||||
|
||||
liftResponse.code should equal(http4sStatus.code)
|
||||
|
||||
http4sJson match {
|
||||
case JObject(fields) =>
|
||||
toFieldMap(fields).get("accounts") match {
|
||||
case Some(JArray(_)) => succeed
|
||||
case _ => fail("Expected accounts field to be an array")
|
||||
}
|
||||
case _ =>
|
||||
fail("Expected http4s JSON object for private accounts endpoint")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user