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
3 changes: 3 additions & 0 deletions .github/workflows/build_container.yml
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,9 @@ jobs:
echo ResetPasswordUrlEnabled=true >> obp-api/src/main/resources/props/test.default.props
echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props
echo hikari.maximumPoolSize=20 >> obp-api/src/main/resources/props/test.default.props
# Enables the Berlin Group v1.3 alias so ResourceDocRegistryParityTest and
# ApiCollectionEndpointTest can exercise a real alias operation id end to end.
echo berlin_group_v1_3_alias_path=0.6/v1 >> obp-api/src/main/resources/props/test.default.props
echo write_metrics=false >> obp-api/src/main/resources/props/test.default.props
# Log emails instead of opening a real SMTP socket: without this,
# LocalMappedConnector.sendCustomerNotification's EMAIL branch calls
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/build_pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,9 @@ jobs:
echo ResetPasswordUrlEnabled=true >> obp-api/src/main/resources/props/test.default.props
echo consents.allowed=true >> obp-api/src/main/resources/props/test.default.props
echo hikari.maximumPoolSize=20 >> obp-api/src/main/resources/props/test.default.props
# Enables the Berlin Group v1.3 alias so ResourceDocRegistryParityTest and
# ApiCollectionEndpointTest can exercise a real alias operation id end to end.
echo berlin_group_v1_3_alias_path=0.6/v1 >> obp-api/src/main/resources/props/test.default.props
echo write_metrics=false >> obp-api/src/main/resources/props/test.default.props
# Log emails instead of opening a real SMTP socket: without this,
# LocalMappedConnector.sendCustomerNotification's EMAIL branch calls
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ package code.api.ResourceDocs1_4_0
import code.api.Constant.{GET_DYNAMIC_RESOURCE_DOCS_TTL, GET_STATIC_RESOURCE_DOCS_TTL, HostName, PARAM_LOCALE}
import code.api.OBPRestHelper
import code.api.cache.Caching
import code.api.dynamic.endpoint.OBPAPIDynamicEndpoint
import code.api.dynamic.entity.OBPAPIDynamicEntity
import code.api.util.APIUtil._
import code.api.util.ApiRole.{canReadDynamicResourceDocsAtOneBank, canReadResourceDoc}
import code.api.util.ApiTag._
Expand All @@ -22,7 +20,6 @@ import code.api.v4_0_0.{APIMethods400, OBPAPI4_0_0}
import code.api.v5_0_0.OBPAPI5_0_0
import code.api.v5_1_0.OBPAPI5_1_0
import code.api.v6_0_0.OBPAPI6_0_0
import code.api.berlin.group.ConstantsBG
import code.apicollectionendpoint.MappedApiCollectionEndpointsProvider
import code.util.Helper
import code.util.Helper.{MdcLoggable, ObpS, SILENCE_IS_GOLDEN}
Expand Down Expand Up @@ -331,59 +328,15 @@ trait ResourceDocsAPIMethods extends MdcLoggable with APIMethods220 with APIMeth

logger.debug(s"getResourceDocsList says requestedApiVersion is $requestedApiVersion")

val resourceDocs = requestedApiVersion match {
case ApiVersion.v7_0_0 => code.api.v7_0_0.Http4s700.allResourceDocs // Use aggregated docs for v7.0.0
case ConstantsBG.`berlinGroupVersion1` => code.api.berlin.group.v1_3.Http4sBGv13.resourceDocs
case ConstantsBG.`berlinGroupVersion2` => code.api.berlin.group.v2.Http4sBGv2.resourceDocs
case ApiVersion.v6_0_0 => code.api.util.http4s.Http4sResourceDocAggregation.v600
case ApiVersion.v5_1_0 => code.api.util.http4s.Http4sResourceDocAggregation.v510
case ApiVersion.v5_0_0 => code.api.util.http4s.Http4sResourceDocAggregation.v500
case ApiVersion.v4_0_0 => code.api.util.http4s.Http4sResourceDocAggregation.v400
case ApiVersion.v3_1_0 => code.api.util.http4s.Http4sResourceDocAggregation.v310
case ApiVersion.v3_0_0 => code.api.util.http4s.Http4sResourceDocAggregation.v300
case ApiVersion.v2_2_0 => code.api.util.http4s.Http4sResourceDocAggregation.v220
case ApiVersion.v2_1_0 => code.api.util.http4s.Http4sResourceDocAggregation.v210
case ApiVersion.v2_0_0 => code.api.util.http4s.Http4sResourceDocAggregation.v200
case ApiVersion.v1_4_0 => code.api.util.http4s.Http4sResourceDocAggregation.v140
case ApiVersion.v1_3_0 => code.api.util.http4s.Http4sResourceDocAggregation.v130
case ApiVersion.v1_2_1 => code.api.util.http4s.Http4sResourceDocAggregation.v121
case ApiVersion.`dynamic-endpoint` => OBPAPIDynamicEndpoint.allResourceDocs
case ApiVersion.`dynamic-entity` => OBPAPIDynamicEntity.allResourceDocs
case version: ScannedApiVersion => ScannedApis.versionMapScannedApis.get(version).map(_.allResourceDocs).getOrElse(ArrayBuffer.empty[ResourceDoc])
case _ => ArrayBuffer.empty[ResourceDoc]
}
// ResourceDocRegistry is the single source of truth for both this per-version dispatch and
// APIUtil.allStaticResourceDocs' global operation-id union -- see that object's doc comment.
val resourceDocs = ResourceDocRegistry.docsFor(requestedApiVersion)

logger.debug(s"There are ${resourceDocs.length} resource docs available to $requestedApiVersion")

val activeResourceDocs = requestedApiVersion match {
case ApiVersion.v7_0_0 => resourceDocs
case ConstantsBG.`berlinGroupVersion1` => resourceDocs // fully on http4s — no Lift route filter
case ConstantsBG.`berlinGroupVersion2` => resourceDocs
case ApiVersion.v1_2_1 => resourceDocs
case ApiVersion.v6_0_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v5_1_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v5_0_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v4_0_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v3_1_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v3_0_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v2_2_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v2_1_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v2_0_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v1_4_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.v1_3_0 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.`dynamic-entity` => resourceDocs // runtime CRUD now on Http4sDynamicEntity; routes are Nil, skip Lift-route filter
case ApiVersion.`dynamic-endpoint` => resourceDocs // dispatch now on Http4sDynamicEndpoint (proxy + native Piece C); routes carry only the stub, skip Lift-route filter
case ApiVersion.ukOpenBankingV20 => resourceDocs // fully on http4s — no Lift route filter
case ApiVersion.ukOpenBankingV31 => resourceDocs // fully on http4s — no Lift route filter
case _ => resourceDocs
}

logger.debug(s"There are ${activeResourceDocs.length} resource docs available to $requestedApiVersion")


val activePlusLocalResourceDocs = ArrayBuffer[ResourceDoc]()

activePlusLocalResourceDocs ++= activeResourceDocs
activePlusLocalResourceDocs ++= resourceDocs
requestedApiVersion match
{
// only `obp` standard show the `localResourceDocs`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,22 @@ import scala.collection.mutable.ArrayBuffer
*/
object OBP_BERLIN_GROUP_1_3_Alias extends OBPRestHelper with MdcLoggable with ScannedApis {

/**
* The version this aggregator registers under.
*
* `berlinGroupV13AliasPath` is empty when `berlin_group_v1_3_alias_path` is unset, so `.head` /
* `.last` must be guarded: this object is instantiated by the ScannedApis classpath scan, which
* catches a throwing companion and merely logs a warning, so an unguarded NoSuchElementException
* would drop the alias silently. Inactive registrations keep the empty-string version they have
* always had, which no request can address and which deliberately does NOT equal
* ConstantsBG.berlinGroupVersion1 -- colliding with the canonical BG v1.3 key would let this
* (doc-less) object win ScannedApis' `.toMap` and blank out /resource-docs/BGv1.3/obp.
*/
override val apiVersion: ScannedApiVersion =
ScannedApiVersion(berlinGroupV13AliasPath.head, berlinGroupV13AliasPath.head, berlinGroupV13AliasPath.last)
if (berlinGroupV13AliasPath.nonEmpty)
ScannedApiVersion(berlinGroupV13AliasPath.head, berlinGroupV13AliasPath.head, berlinGroupV13AliasPath.last)
else
ScannedApiVersion("", "", "")

val versionStatus: String = ApiVersionStatus.DRAFT.toString

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,31 @@ import cats.data.{Kleisli, OptionT}
import cats.effect._
import code.api.berlin.group.ConstantsBG
import code.api.util.APIUtil.ResourceDoc
import code.api.util.ScannedApis
import code.api.util.http4s.ResourceDocMiddleware
import code.util.Helper.MdcLoggable
import com.openbankproject.commons.util.ScannedApiVersion
import org.http4s._

import scala.collection.mutable.ArrayBuffer

object Http4sBGv2 extends MdcLoggable {
object Http4sBGv2 extends MdcLoggable with ScannedApis {

type HttpF[A] = OptionT[IO, A]

val implementedInApiVersion = ConstantsBG.berlinGroupVersion2

// ScannedApis discovery marker: makes BGv2 convention-driven like the other Berlin Group /
// UK Open Banking standards, so ResourceDocRegistry picks it up without a hand-maintained entry.
override val apiVersion: ScannedApiVersion = implementedInApiVersion

val resourceDocs: ArrayBuffer[ResourceDoc] =
Http4sBGv2AIS.resourceDocs ++
Http4sBGv2PIS.resourceDocs ++
Http4sBGv2PIIS.resourceDocs

override val allResourceDocs: ArrayBuffer[ResourceDoc] = resourceDocs

val allRoutes: HttpRoutes[IO] = Kleisli[HttpF, Request[IO], Response[IO]] { req =>
Http4sBGv2AIS.routes(req)
.orElse(Http4sBGv2PIS.routes(req))
Expand Down
42 changes: 15 additions & 27 deletions obp-api/src/main/scala/code/api/util/APIUtil.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ import cats.effect.IO
import code.abacrule.AbacRuleEngine
import code.accountholders.AccountHolders
import code.api.Constant._
import code.api.UKOpenBanking.v2_0_0.OBP_UKOpenBanking_200
import code.api.UKOpenBanking.v3_1_0.OBP_UKOpenBanking_310
import code.api.UKOpenBanking.v4_0_1.OBP_UKOpenBanking_401
import code.api._
import code.api.berlin.group.ConstantsBG
import code.api.berlin.group.v1_3.JSONFactory_BERLIN_GROUP_1_3.{ErrorMessageBG, ErrorMessagesBG}
Expand Down Expand Up @@ -4759,7 +4756,14 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
}
)

val berlinGroupV13AliasPath = APIUtil.getPropsValue("berlin_group_v1_3_alias_path","").split("/").toList.map(_.trim)
// Empty segments are dropped so that "unset" really means "no alias". Without the filter an
// absent prop yields List("") -- "".split("/") returns Array(""), not an empty array -- which is
// nonEmpty, so every `if (berlinGroupV13AliasPath.nonEmpty)` guard downstream took its ACTIVE
// branch on a default instance: Http4sBGv13Alias published 55 docs stamped with the degenerate
// version ScannedApiVersion("", "", ""), whose operation ids came out as `BG-<name>`, and its
// route bridge matched on the prefix "/" (every path) only to fall through again.
val berlinGroupV13AliasPath =
APIUtil.getPropsValue("berlin_group_v1_3_alias_path","").split("/").toList.map(_.trim).filter(_.nonEmpty)

val getAtmsIsPublic = APIUtil.getPropsAsBoolValue("apiOptions.getAtmsIsPublic", true)

Expand Down Expand Up @@ -4874,29 +4878,13 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{

val allowedAnswerTransactionRequestChallengeAttempts = APIUtil.getPropsAsIntValue("answer_transactionRequest_challenge_allowed_attempts").openOr(3)

// Base is the v7 aggregation — the newest OBP-standard surface, which already contains the
// v6.0.0-and-older aggregation plus the v7-only endpoints (deduped by URL/method). Basing on
// the v6 aggregation silently excluded v7-only operation ids from everything that resolves
// operation ids through this list (api-collection endpoint validation, top-apis lookups, ...).
// ResourceDocRegistryParityTest pins the invariant that every per-standard surface the
// resource-docs dispatcher can serve is contained here.
lazy val allStaticResourceDocs = (code.api.v7_0_0.Http4s700.allResourceDocs
++ OBP_UKOpenBanking_200.allResourceDocs
++ OBP_UKOpenBanking_310.allResourceDocs
++ OBP_UKOpenBanking_401.allResourceDocs
// Commented out: Lift endpoints migrated off / removed (Polish, STET, AUOpenBanking, MxOF/CNBV9, BahrainOBF)
// ++ code.api.Polish.v2_1_1_1.OBP_PAPI_2_1_1_1.allResourceDocs
// ++ code.api.STET.v1_4.OBP_STET_1_4.allResourceDocs
// ++ code.api.AUOpenBanking.v1_0_0.ApiCollector.allResourceDocs
// ++ code.api.MxOF.CNBV9_1_0_0.allResourceDocs
// ++ code.api.MxOF.OBP_MXOF_1_0_0.allResourceDocs
// ++ code.api.BahrainOBF.v1_0_0.ApiCollector.allResourceDocs
++ code.api.berlin.group.v1_3.OBP_BERLIN_GROUP_1_3.allResourceDocs
// BGv2 was missing here even though /resource-docs/BGv2 serves it, so a BGv2 operation id
// (e.g. BGv2-getAccountDetails) failed the getAllResourceDocs membership check that
// api-collection-endpoints (and anything else resolving operation ids) relies on.
++ code.api.berlin.group.v2.Http4sBGv2.resourceDocs).toList

// Delegates to ResourceDocRegistry, the single source of truth shared with the per-version
// resource-docs dispatcher (ResourceDocsAPIMethods.getResourceDocsList) -- see that object's
// doc comment for why the two used to drift and how deriving both from one registry fixes it.
// Kept under this name so existing call sites (Http4s400, Http4s600, JSONFactory6.0.0, ...)
// don't need to move.
lazy val allStaticResourceDocs: List[ResourceDoc] = ResourceDocRegistry.allStaticResourceDocs

def allDynamicResourceDocs= (DynamicEntityHelper.doc ++ DynamicEndpointHelper.doc ++ DynamicEndpoints.dynamicResourceDocs).toList

def getAllResourceDocs = allStaticResourceDocs ++ allDynamicResourceDocs
Expand Down
3 changes: 0 additions & 3 deletions obp-api/src/main/scala/code/api/util/ApiVersionUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package code.api.util

import com.openbankproject.commons.util.ApiVersion._
import com.openbankproject.commons.util.ScannedApiVersion
import code.api.berlin.group.ConstantsBG

object ApiVersionUtils {

Expand All @@ -23,7 +22,6 @@ object ApiVersionUtils {
v7_0_0 ::
`dynamic-endpoint` ::
`dynamic-entity` ::
ConstantsBG.berlinGroupVersion2 ::
scannedApis
).distinct

Expand All @@ -48,7 +46,6 @@ object ApiVersionUtils {
case v7_0_0.fullyQualifiedVersion | v7_0_0.apiShortVersion => v7_0_0
case `dynamic-endpoint`.fullyQualifiedVersion | `dynamic-endpoint`.apiShortVersion => `dynamic-endpoint`
case `dynamic-entity`.fullyQualifiedVersion | `dynamic-entity`.apiShortVersion => `dynamic-entity`
case version if version == ConstantsBG.berlinGroupVersion2.fullyQualifiedVersion || version == ConstantsBG.berlinGroupVersion2.apiShortVersion => ConstantsBG.berlinGroupVersion2
case version if(scannedApis.map(_.fullyQualifiedVersion).contains(version))
=>scannedApis.filter(_.fullyQualifiedVersion==version).head
case version if(scannedApis.map(_.apiShortVersion).contains(version))
Expand Down
Loading
Loading