Skip to content

Commit bf3f0cc

Browse files
fix(v6.0.0): CI failures across 6 v6 suites from prior batch
- getCurrentConsumer: add canGetCurrentConsumer to ResourceDoc roles so authenticated-but-roleless requests get 403 (matches Lift, ConsumerTest). - getOidcClient / verifyOidcClient: set authMode = UserOrApplication so anonymous requests return ApplicationNotIdentified (401) instead of AuthenticatedUserIsRequired (OidcClient tests). - counterparty-attribute (5 endpoints): rename URL placeholder COUNTERPARTY_ID to COUNTERPARTY_ID_PARAM in the http4s ResourceDocs so middleware skips its getCounterpartyTrait lookup; deleteCounterpartyAttribute now uses executeDelete for 204 (CounterpartyAttributeTest). - createOrUpdateWebUiProps PUT: inline IO handler that returns 201 when the property is new and 200 when it already exists (WebUiPropsTest). - deleteWebUiProps DELETE: switch to executeDelete for 204 (WebUiPropsTest). - createCallLimits POST: return CallLimitJsonV600 (unbox the RateLimiting from createConsumerCallLimits) so the response carries rate_limiting_id, which RateLimitsTest extracts to chain into DELETE. - deleteCallLimits DELETE: switch to executeDelete for 204 (RateLimitsTest). All 6 suites (61 tests) now pass locally.
1 parent e3828e5 commit bf3f0cc

1 file changed

Lines changed: 47 additions & 40 deletions

File tree

obp-api/src/main/scala/code/api/v6_0_0/Http4s600.scala

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import code.api.util.{APIUtil, CallContext, CustomJsonFormats, NewStyle}
99
import code.api.util.ApiRole._
1010
import code.api.util.ApiTag._
1111
import code.api.util.ErrorMessages._
12-
import code.api.util.http4s.ResourceDocMiddleware
12+
import code.api.util.http4s.{ErrorResponseConverter, RequestScopeConnection, ResourceDocMiddleware}
1313
import code.api.util.http4s.Http4sRequestAttributes.{EndpointHelpers, RequestOps}
1414
import code.api.util.newstyle.ViewNewStyle
1515
import code.api.v2_0_0.JSONFactory200
@@ -2155,27 +2155,34 @@ object Http4s600 {
21552155
// PUT /obp/v6.0.0/management/webui_props/WEBUI_PROP_NAME
21562156
val createOrUpdateWebUiProps: HttpRoutes[IO] = HttpRoutes.of[IO] {
21572157
case req @ PUT -> `prefixPath` / "management" / "webui_props" / webUiPropName =>
2158-
EndpointHelpers.executeAndRespond(req) { implicit cc =>
2159-
val rawBody = cc.httpBody.getOrElse("")
2160-
val nameLower = webUiPropName.toLowerCase
2161-
for {
2162-
_ <- NewStyle.function.tryons(s"$InvalidWebUiProps name must start with webui_, but current name is: $nameLower", 400, Some(cc)) {
2163-
require(nameLower.startsWith("webui_"))
2164-
}
2165-
_ <- NewStyle.function.tryons(s"$InvalidWebUiProps name must contain only alphanumeric characters, underscore, and dot. Current name: $nameLower", 400, Some(cc)) {
2166-
require(nameLower.matches("^[a-zA-Z0-9_.]+$"))
2167-
}
2168-
_ <- NewStyle.function.tryons(s"$InvalidWebUiProps name must not exceed 255 characters. Current length: ${nameLower.length}", 400, Some(cc)) {
2169-
require(nameLower.length <= 255)
2170-
}
2171-
valueJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should contain a value field", 400, Some(cc)) {
2172-
net.liftweb.json.parse(rawBody).extract[code.webuiprops.WebUiPropsPutJsonV600]
2173-
}
2174-
saved <- Future(MappedWebUiPropsProvider.createOrUpdate(WebUiPropsCommons(nameLower, valueJson.value)))
2175-
} yield {
2176-
// PUT returns 200 if existed, 201 if created — simplified to 200 here.
2177-
saved.openOrThrowException("Could not save WebUiProps"): WebUiPropsCommons
2158+
implicit val cc: CallContext = req.callContext
2159+
implicit val formats: Formats = code.api.util.CustomJsonFormats.formats
2160+
val rawBody = cc.httpBody.getOrElse("")
2161+
val nameLower = webUiPropName.toLowerCase
2162+
val fut: Future[(WebUiPropsCommons, Boolean)] = for {
2163+
_ <- NewStyle.function.tryons(s"$InvalidWebUiProps name must start with webui_, but current name is: $nameLower", 400, Some(cc)) {
2164+
require(nameLower.startsWith("webui_"))
2165+
}
2166+
_ <- NewStyle.function.tryons(s"$InvalidWebUiProps name must contain only alphanumeric characters, underscore, and dot. Current name: $nameLower", 400, Some(cc)) {
2167+
require(nameLower.matches("^[a-zA-Z0-9_.]+$"))
21782168
}
2169+
_ <- NewStyle.function.tryons(s"$InvalidWebUiProps name must not exceed 255 characters. Current length: ${nameLower.length}", 400, Some(cc)) {
2170+
require(nameLower.length <= 255)
2171+
}
2172+
existing <- Future(MappedWebUiPropsProvider.getByName(nameLower))
2173+
resourceExists = existing.isDefined
2174+
valueJson <- NewStyle.function.tryons(s"$InvalidJsonFormat The Json body should contain a value field", 400, Some(cc)) {
2175+
net.liftweb.json.parse(rawBody).extract[code.webuiprops.WebUiPropsPutJsonV600]
2176+
}
2177+
saved <- Future(MappedWebUiPropsProvider.createOrUpdate(WebUiPropsCommons(nameLower, valueJson.value)))
2178+
} yield (saved.openOrThrowException("Could not save WebUiProps"), resourceExists)
2179+
2180+
RequestScopeConnection.fromFuture(fut).attempt.flatMap {
2181+
case Right((commons, existed)) =>
2182+
val jsonString = prettyRender(Extraction.decompose(commons))
2183+
if (existed) Ok(jsonString) else Created(jsonString)
2184+
case Left(err) =>
2185+
ErrorResponseConverter.toHttp4sResponse(err, cc)
21792186
}
21802187
}
21812188
resourceDocs += ResourceDoc(
@@ -2191,7 +2198,7 @@ object Http4s600 {
21912198
// DELETE /obp/v6.0.0/management/webui_props/WEBUI_PROP_NAME
21922199
val deleteWebUiProps: HttpRoutes[IO] = HttpRoutes.of[IO] {
21932200
case req @ DELETE -> `prefixPath` / "management" / "webui_props" / webUiPropName =>
2194-
EndpointHelpers.executeAndRespond(req) { implicit cc =>
2201+
EndpointHelpers.executeDelete(req) { cc =>
21952202
val nameLower = webUiPropName.toLowerCase
21962203
for {
21972204
_ <- NewStyle.function.tryons(s"$InvalidWebUiProps name must start with webui_, but current name is: $nameLower", 400, Some(cc)) {
@@ -2208,7 +2215,7 @@ object Http4s600 {
22082215
case Full(prop) => Future(MappedWebUiPropsProvider.delete(prop.webUiPropsId.getOrElse("")))
22092216
case _ => Future.successful(Full(true))
22102217
}
2211-
} yield ""
2218+
} yield ()
22122219
}
22132220
}
22142221
resourceDocs += ResourceDoc(
@@ -2336,6 +2343,7 @@ object Http4s600 {
23362343
List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError),
23372344
apiTagOIDC :: apiTagConsumer :: apiTagOAuth :: Nil,
23382345
Some(canGetOidcClient :: Nil),
2346+
authMode = code.api.util.APIUtil.UserOrApplication,
23392347
http4sPartialFunction = Some(getOidcClient))
23402348

23412349
// POST /obp/v6.0.0/oidc/clients/verify
@@ -2371,6 +2379,7 @@ object Http4s600 {
23712379
List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError),
23722380
apiTagOIDC :: apiTagConsumer :: apiTagOAuth :: Nil,
23732381
Some(canVerifyOidcClient :: Nil),
2382+
authMode = code.api.util.APIUtil.UserOrApplication,
23742383
http4sPartialFunction = Some(verifyOidcClient))
23752384

23762385
// ─── Phase 2: users bucket (6 of 16; chat-room + special-purpose deferred) ───
@@ -2712,7 +2721,7 @@ object Http4s600 {
27122721
}
27132722
resourceDocs += ResourceDoc(
27142723
null, implementedInApiVersion, nameOf(createCounterpartyAttribute), "POST",
2715-
"/banks/BANK_ID/accounts/ACCOUNT_ID/counterparties/COUNTERPARTY_ID/attributes", "Create Counterparty Attribute",
2724+
"/banks/BANK_ID/accounts/ACCOUNT_ID/counterparties/COUNTERPARTY_ID_PARAM/attributes", "Create Counterparty Attribute",
27162725
"""Create a new attribute on the specified counterparty.""",
27172726
EmptyBody, EmptyBody,
27182727
List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError),
@@ -2723,15 +2732,13 @@ object Http4s600 {
27232732
// DELETE /obp/v6.0.0/banks/BANK_ID/accounts/ACCOUNT_ID/counterparties/COUNTERPARTY_ID/attributes/COUNTERPARTY_ATTRIBUTE_ID
27242733
val deleteCounterpartyAttribute: HttpRoutes[IO] = HttpRoutes.of[IO] {
27252734
case req @ DELETE -> `prefixPath` / "banks" / _ / "accounts" / _ / "counterparties" / _ / "attributes" / attributeId =>
2726-
EndpointHelpers.executeAndRespond(req) { implicit cc =>
2727-
for {
2728-
_ <- code.api.util.newstyle.CounterpartyAttributeNewStyle.deleteCounterpartyAttribute(attributeId, Some(cc))
2729-
} yield ""
2735+
EndpointHelpers.executeDelete(req) { cc =>
2736+
code.api.util.newstyle.CounterpartyAttributeNewStyle.deleteCounterpartyAttribute(attributeId, Some(cc))
27302737
}
27312738
}
27322739
resourceDocs += ResourceDoc(
27332740
null, implementedInApiVersion, nameOf(deleteCounterpartyAttribute), "DELETE",
2734-
"/banks/BANK_ID/accounts/ACCOUNT_ID/counterparties/COUNTERPARTY_ID/attributes/COUNTERPARTY_ATTRIBUTE_ID",
2741+
"/banks/BANK_ID/accounts/ACCOUNT_ID/counterparties/COUNTERPARTY_ID_PARAM/attributes/COUNTERPARTY_ATTRIBUTE_ID",
27352742
"Delete Counterparty Attribute",
27362743
"""Delete a counterparty attribute.""",
27372744
EmptyBody, EmptyBody,
@@ -2751,7 +2758,7 @@ object Http4s600 {
27512758
}
27522759
resourceDocs += ResourceDoc(
27532760
null, implementedInApiVersion, nameOf(getCounterpartyAttributeById), "GET",
2754-
"/banks/BANK_ID/accounts/ACCOUNT_ID/counterparties/COUNTERPARTY_ID/attributes/COUNTERPARTY_ATTRIBUTE_ID",
2761+
"/banks/BANK_ID/accounts/ACCOUNT_ID/counterparties/COUNTERPARTY_ID_PARAM/attributes/COUNTERPARTY_ATTRIBUTE_ID",
27552762
"Get Counterparty Attribute By Id",
27562763
"""Get a counterparty attribute by its ID.""",
27572764
EmptyBody, EmptyBody,
@@ -2772,7 +2779,7 @@ object Http4s600 {
27722779
}
27732780
resourceDocs += ResourceDoc(
27742781
null, implementedInApiVersion, nameOf(getAllCounterpartyAttributes), "GET",
2775-
"/banks/BANK_ID/accounts/ACCOUNT_ID/counterparties/COUNTERPARTY_ID/attributes",
2782+
"/banks/BANK_ID/accounts/ACCOUNT_ID/counterparties/COUNTERPARTY_ID_PARAM/attributes",
27762783
"Get All Counterparty Attributes",
27772784
"""Get all attributes for the specified counterparty.""",
27782785
EmptyBody, EmptyBody,
@@ -2806,7 +2813,7 @@ object Http4s600 {
28062813
}
28072814
resourceDocs += ResourceDoc(
28082815
null, implementedInApiVersion, nameOf(updateCounterpartyAttribute), "PUT",
2809-
"/banks/BANK_ID/accounts/ACCOUNT_ID/counterparties/COUNTERPARTY_ID/attributes/COUNTERPARTY_ATTRIBUTE_ID",
2816+
"/banks/BANK_ID/accounts/ACCOUNT_ID/counterparties/COUNTERPARTY_ID_PARAM/attributes/COUNTERPARTY_ATTRIBUTE_ID",
28102817
"Update Counterparty Attribute",
28112818
"""Update a counterparty attribute by its ID.""",
28122819
EmptyBody, EmptyBody,
@@ -3271,7 +3278,8 @@ object Http4s600 {
32713278
"""Get the Consumer used to make this request, including active rate limits and call counters.""",
32723279
EmptyBody, EmptyBody,
32733280
List($AuthenticatedUserIsRequired, InvalidConsumerCredentials, UnknownError),
3274-
apiTagConsumer :: Nil, None,
3281+
apiTagConsumer :: Nil,
3282+
Some(canGetCurrentConsumer :: Nil),
32753283
http4sPartialFunction = Some(getCurrentConsumer)
32763284
)
32773285

@@ -3863,15 +3871,14 @@ object Http4s600 {
38633871
net.liftweb.json.parse(rawBody).extract[CallLimitPostJsonV600]
38643872
}
38653873
_ <- NewStyle.function.getConsumerByConsumerId(consumerId, Some(cc))
3866-
_ <- RateLimitingDI.rateLimiting.vend.createConsumerCallLimits(
3874+
rateLimitingBox <- RateLimitingDI.rateLimiting.vend.createConsumerCallLimits(
38673875
consumerId, postJson.from_date, postJson.to_date,
38683876
postJson.api_version, postJson.api_name, postJson.bank_id,
38693877
Some(postJson.per_second_call_limit), Some(postJson.per_minute_call_limit),
38703878
Some(postJson.per_hour_call_limit), Some(postJson.per_day_call_limit),
38713879
Some(postJson.per_week_call_limit), Some(postJson.per_month_call_limit))
3872-
date = new java.util.Date()
3873-
(activeRateLimit, ids) <- RateLimitingUtil.getActiveRateLimitsWithIds(consumerId, date)
3874-
} yield JSONFactory600.createActiveRateLimitsJsonV600FromCallLimit(activeRateLimit, ids, date)
3880+
rateLimiting <- Future(unboxFullOrFail(rateLimitingBox, Some(cc), UnknownError, 400))
3881+
} yield JSONFactory600.createCallLimitJsonV600(rateLimiting)
38753882
}
38763883
}
38773884

@@ -3922,11 +3929,11 @@ object Http4s600 {
39223929
// Route: DELETE /obp/v6.0.0/management/consumers/CONSUMER_ID/consumer/rate-limits/RATE_LIMITING_ID
39233930
val deleteCallLimits: HttpRoutes[IO] = HttpRoutes.of[IO] {
39243931
case req @ DELETE -> `prefixPath` / "management" / "consumers" / consumerId / "consumer" / "rate-limits" / rateLimitingId =>
3925-
EndpointHelpers.executeAndRespond(req) { implicit cc =>
3932+
EndpointHelpers.executeDelete(req) { cc =>
39263933
for {
39273934
_ <- NewStyle.function.getConsumerByConsumerId(consumerId, Some(cc))
3928-
_ <- Future(RateLimitingDI.rateLimiting.vend.deleteByRateLimitingId(rateLimitingId))
3929-
} yield ""
3935+
_ <- RateLimitingDI.rateLimiting.vend.deleteByRateLimitingId(rateLimitingId)
3936+
} yield ()
39303937
}
39313938
}
39323939

0 commit comments

Comments
 (0)