Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 25 additions & 4 deletions obp-api/src/main/scala/code/api/v6_0_0/Http4s600.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14120,6 +14120,11 @@ object Http4s600 {
|
|The type field must be one of "STRING", "INTEGER", "DOUBLE" or "DATE_WITH_DAY"
|
|Each Personal Data Field is identified by its own USER_ATTRIBUTE_ID. The "name" is not a unique key:
|this endpoint always creates a new field, so the same "name" can occur on multiple fields for the same user
|(e.g. two "phone_number" fields). To change the value of an existing field, use PUT /my/personal-data-fields/USER_ATTRIBUTE_ID
|rather than POSTing again. To list a user's fields and their USER_ATTRIBUTE_IDs, use GET /my/personal-data-fields.
|
|${userAuthenticationMessage(true)}
|""".stripMargin,
code.api.v5_1_0.UserAttributeJsonV510(
Expand All @@ -14142,7 +14147,11 @@ object Http4s600 {
"Get Personal Data Fields",
s"""Get Personal Data Fields for the currently authenticated user.
|
|Returns Personal Data Fields (IsPersonal=true) that are managed by the user.
|Returns all Personal Data Fields (IsPersonal=true) that are managed by the user, as a list.
|
|Each field has its own USER_ATTRIBUTE_ID. The "name" is not a unique key, so the list may contain
|multiple fields with the same "name" (each a distinct USER_ATTRIBUTE_ID). Use the USER_ATTRIBUTE_ID
|to fetch, update or delete a specific field.
|
|${userAuthenticationMessage(true)}
|""".stripMargin,
Expand All @@ -14162,7 +14171,10 @@ object Http4s600 {
"GET",
"/my/personal-data-fields/USER_ATTRIBUTE_ID",
"Get Personal Data Field By Id",
s"""Get a Personal Data Field by USER_ATTRIBUTE_ID for the currently authenticated user.
s"""Get a single Personal Data Field by USER_ATTRIBUTE_ID for the currently authenticated user.
|
|USER_ATTRIBUTE_ID is the unique identifier of the field (not its "name"). Obtain it from
|GET /my/personal-data-fields. Returns 404 if no field with that USER_ATTRIBUTE_ID belongs to the user.
|
|${userAuthenticationMessage(true)}
|""".stripMargin,
Expand All @@ -14180,7 +14192,12 @@ object Http4s600 {
"PUT",
"/my/personal-data-fields/USER_ATTRIBUTE_ID",
"Update Personal Data Field",
s"""Update a Personal Data Field by USER_ATTRIBUTE_ID for the currently authenticated user.
s"""Update a single Personal Data Field by USER_ATTRIBUTE_ID for the currently authenticated user.
|
|USER_ATTRIBUTE_ID identifies the exact field to update; this updates that one field in place and never
|creates a new one. The body's "name", "type" and "value" all replace the existing field's values, so a
|field can be renamed by changing "name". Returns 404 if no field with that USER_ATTRIBUTE_ID belongs to the user.
|The type field must be one of "STRING", "INTEGER", "DOUBLE" or "DATE_WITH_DAY".
|
|${userAuthenticationMessage(true)}
|""".stripMargin,
Expand All @@ -14207,7 +14224,11 @@ object Http4s600 {
"DELETE",
"/my/personal-data-fields/USER_ATTRIBUTE_ID",
"Delete Personal Data Field",
s"""Delete a Personal Data Field by USER_ATTRIBUTE_ID for the currently authenticated user.
s"""Delete a single Personal Data Field by USER_ATTRIBUTE_ID for the currently authenticated user.
|
|USER_ATTRIBUTE_ID identifies the exact field to delete; only that one field is removed. If several fields
|share the same "name", deleting one leaves the others intact. Returns 404 if no field with that
|USER_ATTRIBUTE_ID belongs to the user.
|
|${userAuthenticationMessage(true)}
|""".stripMargin,
Expand Down
38 changes: 6 additions & 32 deletions obp-api/src/main/scala/code/api/v7_0_0/Http4s700.scala
Original file line number Diff line number Diff line change
Expand Up @@ -202,38 +202,12 @@ object Http4s700 {
http4sPartialFunction = Some(root)
)

// Route: GET /obp/v7.0.0/banks
val getBanks: HttpRoutes[IO] = HttpRoutes.of[IO] {
case req @ GET -> `prefixPath` / "banks" =>
EndpointHelpers.executeAndRespond(req) { implicit cc =>
for {
(banks, callContext) <- NewStyle.function.getBanks(Some(cc))
} yield JSONFactory400.createBanksJson(banks)
}
}

resourceDocs += ResourceDoc(
null,
implementedInApiVersion,
nameOf(getBanks),
"GET",
"/banks",
"Get Banks",
s"""Get banks on this API instance
|Returns a list of banks supported on this server:
|
|* ID used as parameter in URLs
|* Short and full name of bank
|* Logo URL
|* Website""",
EmptyBody,
banksJSON,
List(
UnknownError
),
apiTagBank :: Nil,
http4sPartialFunction = Some(getBanks)
)
// Note: GET /obp/v7.0.0/banks is intentionally NOT defined here.
// The v7 implementation used the older v4.0.0 shape (BanksJson400: `id`, `short_name`),
// which is behind v6.0.0's BanksJsonV600 (`bank_id`, `bank_code`). Rather than duplicate
// (and drift from) the v6 shape, we let the request fall through to `v700ToV600Bridge`,
// which rewrites /obp/v7.0.0/banks → /obp/v6.0.0/banks and serves Http4s600.getBanks,
// tagging the response `X-OBP-Version-Served: v6.0.0`. v7 thus inherits the latest shape.

// Note: resource-docs requests (`GET /obp/v7.0.0/resource-docs/...`) are intercepted by
// `Http4sResourceDocs.routes`, which is registered earlier in `Http4sApp.baseServices`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -257,8 +257,8 @@ class Http4sServerIntegrationTest extends ServerSetup with DefaultUsers with Ser
And("X-OBP-Version-Served header indicates the fallback version")
versionServed should equal(Some("v6.0.0"))

When("We request a native v7.0.0 endpoint (/banks is migrated)")
val (_, _, nativeVersionServed) = makeHttp4sGetRequestFull("/obp/v7.0.0/banks")
When("We request a native v7.0.0 endpoint (/root is native to v7; /banks was removed and now cascades to v6)")
val (_, _, nativeVersionServed) = makeHttp4sGetRequestFull("/obp/v7.0.0/root")

Then("Native v7 endpoints do not set X-OBP-Version-Served")
nativeVersionServed should equal(None)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,13 @@ class ResourceDocMiddlewareEnableDisablePropsTest extends ServerSetup with Given
// OperationIds match `APIUtil.buildOperationId(v, partialFunctionName)` →
// s"$fullyQualifiedVersion-$name". v7.0.0's fully qualified form is "OBPv7.0.0".
private val rootOpId = "OBPv7.0.0-root"
private val getBanksOpId = "OBPv7.0.0-getBanks"
// A second NATIVE v7 endpoint. getBanks was removed from v7 (it now cascades to v6),
// so it can no longer serve as a "native v7" allowlist target. /api/versions is public,
// native to v7, and needs no DB — the same properties that make /root suitable here.
private val versionsOpId = "OBPv7.0.0-getScannedApiVersions"

private val rootPath = "/obp/v7.0.0/root"
private val banksPath = "/obp/v7.0.0/banks"
private val rootPath = "/obp/v7.0.0/root"
private val versionsPath = "/obp/v7.0.0/api/versions"

private def get(path: String): Int = {
val req = Request[IO](Method.GET, Uri.unsafeFromString(path), headers = Headers.empty,
Expand Down Expand Up @@ -98,12 +101,12 @@ class ResourceDocMiddlewareEnableDisablePropsTest extends ServerSetup with Given
status shouldBe 404

And("other endpoints in the same version are unaffected")
get(banksPath) shouldBe 200
get(versionsPath) shouldBe 200
}

scenario("api_enabled_endpoints contains a different operationId → 404 for non-listed", EnableDisablePropsTag) {
Given(s"api_enabled_endpoints=[$getBanksOpId] (root is NOT listed)")
setPropsValues("api_enabled_endpoints" -> s"[$getBanksOpId]")
Given(s"api_enabled_endpoints=[$versionsOpId] (root is NOT listed)")
setPropsValues("api_enabled_endpoints" -> s"[$versionsOpId]")

When("requesting GET /obp/v7.0.0/root")
val rootStatus = get(rootPath)
Expand All @@ -112,7 +115,7 @@ class ResourceDocMiddlewareEnableDisablePropsTest extends ServerSetup with Given
rootStatus shouldBe 404

And("the explicitly enabled endpoint still serves")
get(banksPath) shouldBe 200
get(versionsPath) shouldBe 200
}

scenario("api_enabled_endpoints contains the operationId → endpoint serves", EnableDisablePropsTag) {
Expand All @@ -132,7 +135,7 @@ class ResourceDocMiddlewareEnableDisablePropsTest extends ServerSetup with Given

When("requesting two unrelated v7 endpoints directly against Http4s700's wrapped routes")
val rootStatus = get(rootPath)
val banksStatus = get(banksPath)
val banksStatus = get(versionsPath)

Then("both still serve — the middleware deliberately ignores version-level Props")
And("(the prefix-level gate lives in Http4sApp.gate, which this test bypasses)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,9 @@ class Http4s700RoutesTest extends ServerSetupWithTestData {
Given("GET /obp/v7.0.0/banks request")
val (statusCode, json, _) = makeHttpRequest("/obp/v7.0.0/banks")

Then("Each bank has id, short_name, full_name, logo, website")
// /obp/v7.0.0/banks cascades to v6.0.0 via v700ToV600Bridge, so the response
// uses the v6 BanksJsonV600 shape: bank_id / bank_code (not v4's id / short_name).
Then("Each bank has bank_id, bank_code, full_name, logo, website")
statusCode shouldBe 200
json match {
case JObject(fields) =>
Expand All @@ -220,8 +222,8 @@ class Http4s700RoutesTest extends ServerSetupWithTestData {
banks.headOption match {
case Some(JObject(bankFields)) =>
val keys = bankFields.map(_.name)
keys should contain("id")
keys should contain("short_name")
keys should contain("bank_id")
keys should contain("bank_code")
keys should contain("full_name")
case _ =>
fail("Expected bank to be a JSON object")
Expand Down
Loading
Loading