@@ -13,6 +13,7 @@ import code.api.util.http4s.{ErrorResponseConverter, RequestScopeConnection, Res
1313import code .api .util .http4s .Http4sRequestAttributes .{EndpointHelpers , RequestOps }
1414import code .api .util .newstyle .ViewNewStyle
1515import code .api .v2_0_0 .JSONFactory200
16+ import code .api .v5_0_0 .Http4s500
1617import code .api .v5_1_0 .{Http4s510 , JSONFactory510 }
1718import code .api .v6_0_0 .JSONFactory600 .ScannedApiVersionJsonV600
1819import code .accountattribute .AccountAttributeX
@@ -80,12 +81,11 @@ import scala.concurrent.Future
8081 * v6.0.0 http4s endpoints — Phase 1 in progress.
8182 *
8283 * Wire-in into `Http4sApp.baseServices` is performed alongside this object.
83- * The v600→v510 bridge (`v600ToV510Bridge`) is intentionally NOT appended to
84- * `allRoutes`: unmigrated v6 paths must fall through the http4s chain to the
85- * Lift fallback, which still serves the v6 Lift handlers. Adding the bridge
86- * would let v6 *overrides* be hijacked into v5.1 handlers (CLAUDE.md →
87- * "Bridge-cascade hijack"). The bridge val is kept here so it can be enabled
88- * later if the team decides to short-circuit Lift for v6 originals.
84+ * The v600→v500 bridge (`v600ToV500Bridge`) rewrites unhandled v6.0.0 paths
85+ * to v5.0.0 and delegates to Http4s500.wrappedRoutesV500Services, which has a
86+ * working cascade chain (v5.0.0 → v4.0.0 → v3.1.0 → v3.0.0). The bridge
87+ * skips v5.1.0 because Http4s510's own bridge to v5.0.0 is disabled due to
88+ * MetricTest / VRPConsentRequestTest regressions.
8989 */
9090object Http4s600 {
9191
@@ -1790,6 +1790,7 @@ object Http4s600 {
17901790 .orElse(getConnectorTraces(req))
17911791 .orElse(getDynamicEntityDiagnostics(req))
17921792 .orElse(cleanupOrphanedDynamicEntityRecords(req))
1793+ .orElse(createWebUiProps(req))
17931794 .orElse(createOrUpdateWebUiProps(req))
17941795 .orElse(deleteWebUiProps(req))
17951796 .orElse(createCustomViewManagement(req))
@@ -2249,6 +2250,36 @@ object Http4s600 {
22492250 Some (canCleanupOrphanedDynamicEntityRecords :: Nil ),
22502251 http4sPartialFunction = Some (cleanupOrphanedDynamicEntityRecords))
22512252
2253+ // POST /obp/v6.0.0/management/webui_props
2254+ lazy val createWebUiProps : HttpRoutes [IO ] = HttpRoutes .of[IO ] {
2255+ case req @ POST -> `prefixPath` / " management" / " webui_props" =>
2256+ EndpointHelpers .withUserAndBodyCreated[WebUiPropsCommons , Any ](req) { (user, postedData, cc) =>
2257+ for {
2258+ _ <- NewStyle .function.hasEntitlement(" " , user.userId, canCreateWebUiProps, Some (cc))
2259+ _ <- NewStyle .function.tryons(
2260+ s """ $InvalidWebUiProps name must be start with webui_, but current post name is: ${postedData.name} """ ,
2261+ 400 , Some (cc)) { require(postedData.name.startsWith(" webui_" )) }
2262+ webUiProps <- Future (MappedWebUiPropsProvider .createOrUpdate(postedData)) map {
2263+ unboxFullOrFail(_, Some (cc))
2264+ }
2265+ } yield (webUiProps : WebUiPropsCommons )
2266+ }
2267+ }
2268+
2269+ resourceDocs += ResourceDoc (
2270+ null , implementedInApiVersion, nameOf(createWebUiProps), " POST" ,
2271+ " /management/webui_props" ,
2272+ " Create WebUiProps" ,
2273+ s """ Create a WebUiProps.
2274+ |
2275+ | ${APIUtil .userAuthenticationMessage(true )}
2276+ | """ ,
2277+ WebUiPropsCommons (" webui_api_explorer_url" , " https://apiexplorer.openbankproject.com" ),
2278+ WebUiPropsCommons (" webui_api_explorer_url" , " https://apiexplorer.openbankproject.com" , Some (" web-ui-props-id" )),
2279+ List ($AuthenticatedUserIsRequired , UserHasMissingRoles , InvalidJsonFormat , UnknownError ),
2280+ List (apiTagWebUiProps), Some (List (canCreateWebUiProps)),
2281+ http4sPartialFunction = Some (createWebUiProps))
2282+
22522283 // PUT /obp/v6.0.0/management/webui_props/WEBUI_PROP_NAME
22532284 lazy val createOrUpdateWebUiProps : HttpRoutes [IO ] = HttpRoutes .of[IO ] {
22542285 case req @ PUT -> `prefixPath` / " management" / " webui_props" / webUiPropName =>
@@ -8474,15 +8505,18 @@ object Http4s600 {
84748505 val allRoutesWithMiddleware : HttpRoutes [IO ] =
84758506 ResourceDocMiddleware .apply(resourceDocs)(allRoutes)
84768507
8477- // ─── path-rewriting bridge: /obp/v6.0.0/… → /obp/v5.1.0/… ─────────────
8508+ // ─── path-rewriting bridge: /obp/v6.0.0/… → /obp/v5.0.0/… ─────────────
8509+ // Targets v5.0.0 (not v5.1.0) because Http4s510's bridge to v5.0.0 is
8510+ // disabled (MetricTest / VRPConsentRequestTest regressions). Http4s500 has
8511+ // its own working cascade: v5.0.0 → v4.0.0 → v3.1.0 → v3.0.0.
84788512 // NOT appended to allRoutes — see object-level scaladoc.
8479- val v600ToV510Bridge : HttpRoutes [IO ] = Kleisli [HttpF , Request [IO ], Response [IO ]] { req =>
8513+ val v600ToV500Bridge : HttpRoutes [IO ] = Kleisli [HttpF , Request [IO ], Response [IO ]] { req =>
84808514 val rawPath = req.uri.path.renderString
84818515 if (rawPath.startsWith(" /obp/v6.0.0/" )) {
8482- val rewritten = rawPath.replaceFirst(" /obp/v6\\ .0\\ .0/" , " /obp/v5.1 .0/" )
8516+ val rewritten = rawPath.replaceFirst(" /obp/v6\\ .0\\ .0/" , " /obp/v5.0 .0/" )
84838517 val newUri = req.uri.withPath(Uri .Path .unsafeFromString(rewritten))
84848518 val rewrittenReq = req.withUri(newUri)
8485- Http4s510 .wrappedRoutesV510Services .run(rewrittenReq)
8519+ Http4s500 .wrappedRoutesV500Services .run(rewrittenReq)
84868520 } else {
84878521 OptionT .none[IO , Response [IO ]]
84888522 }
@@ -8501,5 +8535,8 @@ object Http4s600 {
85018535 // A `lazy val` defers the read until first access (from Http4sApp after Boot
85028536 // completes), by which time Impl6 is fully initialised.
85038537 lazy val wrappedRoutesV600Services : HttpRoutes [IO ] =
8504- Implementations6_0_0 .allRoutesWithMiddleware
8538+ Kleisli [HttpF , Request [IO ], Response [IO ]] { req =>
8539+ Implementations6_0_0 .allRoutesWithMiddleware.run(req)
8540+ .orElse(Implementations6_0_0 .v600ToV500Bridge.run(req))
8541+ }
85058542}
0 commit comments