mirror of
https://github.com/OpenBankProject/OBP-API.git
synced 2026-02-06 17:17:09 +00:00
Note: By default we don't serve some older OBP standards. Also v6.0.0 version of /api/versions shows is_active
This commit is contained in:
parent
ce4f09d392
commit
a93e860fed
@ -720,9 +720,10 @@ super_admin_user_ids=USER_ID1,USER_ID2,
|
||||
# For a VERSION (the version in path e.g. /obp/v4.0.0) to be allowed, it must be:
|
||||
|
||||
# 1) Absent from here (high priority):
|
||||
# Note the default is empty, not the example here.
|
||||
# Black List of Versions
|
||||
#api_disabled_versions=[OBPv3.0.0,BGv1.3]
|
||||
# Since December 2025 we are removing older versions of OBP standards by default. Note any endpoints defined in these versions are still
|
||||
# available via calling v5.1.0 or v6.0.0. We're doing this so API Explorer (II) loads faster etc.
|
||||
#api_disabled_versions=["OBPv1.2.1,OBPv1.3.0,OBPv1.4.0,OBPv2.0.0,OBPv2.1.0,OBPv2.2.0,OBPv3.0.0,OBPv3.1.0,OBPv4.0.0,OBPv5.0.0"]
|
||||
|
||||
# 2) Present here OR this entry must be empty:
|
||||
# Note the default is empty, not the example here.
|
||||
|
||||
@ -2678,7 +2678,14 @@ object APIUtil extends MdcLoggable with CustomJsonFormats{
|
||||
case JField("ccy", x) => JField("currency", x)
|
||||
}
|
||||
|
||||
def getDisabledVersions() : List[String] = APIUtil.getPropsValue("api_disabled_versions").getOrElse("").replace("[", "").replace("]", "").split(",").toList.filter(_.nonEmpty)
|
||||
def getDisabledVersions() : List[String] = {
|
||||
val defaultDisabledVersions = "OBPv1.2.1,OBPv1.3.0,OBPv1.4.0,OBPv2.0.0,OBPv2.1.0,OBPv2.2.0,OBPv3.0.0,OBPv3.1.0,OBPv4.0.0,OBPv5.0.0"
|
||||
val disabledVersions = APIUtil.getPropsValue("api_disabled_versions").getOrElse(defaultDisabledVersions).replace("[", "").replace("]", "").split(",").toList.filter(_.nonEmpty)
|
||||
if (disabledVersions.nonEmpty) {
|
||||
logger.info(s"Disabled API versions: ${disabledVersions.mkString(", ")}")
|
||||
}
|
||||
disabledVersions
|
||||
}
|
||||
|
||||
def getDisabledEndpointOperationIds() : List[String] = APIUtil.getPropsValue("api_disabled_endpoints").getOrElse("").replace("[", "").replace("]", "").split(",").toList.filter(_.nonEmpty)
|
||||
|
||||
|
||||
@ -26,7 +26,7 @@ import code.api.v5_0_0.JSONFactory500
|
||||
import code.api.v5_0_0.{ViewJsonV500, ViewsJsonV500}
|
||||
import code.api.v5_1_0.{JSONFactory510, PostCustomerLegalNameJsonV510}
|
||||
import code.api.dynamic.entity.helper.{DynamicEntityHelper, DynamicEntityInfo}
|
||||
import code.api.v6_0_0.JSONFactory600.{DynamicEntityDiagnosticsJsonV600, DynamicEntityIssueJsonV600, GroupJsonV600, GroupMembershipJsonV600, GroupMembershipsJsonV600, GroupsJsonV600, PostGroupJsonV600, PostGroupMembershipJsonV600, PostResetPasswordUrlJsonV600, PutGroupJsonV600, ReferenceTypeJsonV600, ReferenceTypesJsonV600, ResetPasswordUrlJsonV600, RoleWithEntitlementCountJsonV600, RolesWithEntitlementCountsJsonV600, ValidateUserEmailJsonV600, ValidateUserEmailResponseJsonV600, createActiveCallLimitsJsonV600, createCallLimitJsonV600, createCurrentUsageJson}
|
||||
import code.api.v6_0_0.JSONFactory600.{DynamicEntityDiagnosticsJsonV600, DynamicEntityIssueJsonV600, GroupJsonV600, GroupMembershipJsonV600, GroupMembershipsJsonV600, GroupsJsonV600, PostGroupJsonV600, PostGroupMembershipJsonV600, PostResetPasswordUrlJsonV600, PutGroupJsonV600, ReferenceTypeJsonV600, ReferenceTypesJsonV600, ResetPasswordUrlJsonV600, RoleWithEntitlementCountJsonV600, RolesWithEntitlementCountsJsonV600, ScannedApiVersionJsonV600, ValidateUserEmailJsonV600, ValidateUserEmailResponseJsonV600, createActiveCallLimitsJsonV600, createCallLimitJsonV600, createCurrentUsageJson}
|
||||
import code.api.v6_0_0.OBPAPI6_0_0
|
||||
import code.metrics.APIMetrics
|
||||
import code.bankconnectors.LocalMappedConnectorInternal
|
||||
@ -65,6 +65,7 @@ import scala.collection.immutable.{List, Nil}
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.duration._
|
||||
import scala.jdk.CollectionConverters._
|
||||
import scala.util.Random
|
||||
|
||||
|
||||
@ -1245,6 +1246,90 @@ trait APIMethods600 {
|
||||
}
|
||||
}
|
||||
|
||||
staticResourceDocs += ResourceDoc(
|
||||
getScannedApiVersions,
|
||||
implementedInApiVersion,
|
||||
nameOf(getScannedApiVersions),
|
||||
"GET",
|
||||
"/api/versions",
|
||||
"Get Scanned API Versions",
|
||||
s"""Get all scanned API versions available in this codebase.
|
||||
|
|
||||
|This endpoint returns all API versions that have been discovered/scanned, along with their active status.
|
||||
|
|
||||
|**Response Fields:**
|
||||
|
|
||||
|* `url_prefix`: The URL prefix for the version (e.g., "obp", "berlin-group", "open-banking")
|
||||
|* `api_standard`: The API standard name (e.g., "OBP", "BG", "UK", "STET")
|
||||
|* `api_short_version`: The version number (e.g., "v4.0.0", "v1.3")
|
||||
|* `fully_qualified_version`: The fully qualified version combining standard and version (e.g., "OBPv4.0.0", "BGv1.3")
|
||||
|* `is_active`: Boolean indicating if the version is currently enabled and accessible
|
||||
|
|
||||
|**Active Status:**
|
||||
|
|
||||
|* `is_active=true`: Version is enabled and can be accessed via its URL prefix
|
||||
|* `is_active=false`: Version is scanned but disabled (via `api_disabled_versions` props)
|
||||
|
|
||||
|**Use Cases:**
|
||||
|
|
||||
|* Discover what API versions are available in the codebase
|
||||
|* Check which versions are currently enabled
|
||||
|* Verify that disabled versions configuration is working correctly
|
||||
|* API documentation and discovery
|
||||
|
|
||||
|**Note:** This differs from v4.0.0's `/api/versions` endpoint which shows all scanned versions without is_active status.
|
||||
|
|
||||
|""",
|
||||
EmptyBody,
|
||||
ListResult(
|
||||
"scanned_api_versions",
|
||||
List(
|
||||
ScannedApiVersionJsonV600(url_prefix = "obp", api_standard = "OBP", api_short_version = "v1.2.1", fully_qualified_version = "OBPv1.2.1", is_active = true),
|
||||
ScannedApiVersionJsonV600(url_prefix = "obp", api_standard = "OBP", api_short_version = "v1.3.0", fully_qualified_version = "OBPv1.3.0", is_active = true),
|
||||
ScannedApiVersionJsonV600(url_prefix = "obp", api_standard = "OBP", api_short_version = "v1.4.0", fully_qualified_version = "OBPv1.4.0", is_active = true),
|
||||
ScannedApiVersionJsonV600(url_prefix = "obp", api_standard = "OBP", api_short_version = "v2.0.0", fully_qualified_version = "OBPv2.0.0", is_active = true),
|
||||
ScannedApiVersionJsonV600(url_prefix = "obp", api_standard = "OBP", api_short_version = "v2.1.0", fully_qualified_version = "OBPv2.1.0", is_active = true),
|
||||
ScannedApiVersionJsonV600(url_prefix = "obp", api_standard = "OBP", api_short_version = "v2.2.0", fully_qualified_version = "OBPv2.2.0", is_active = true),
|
||||
ScannedApiVersionJsonV600(url_prefix = "obp", api_standard = "OBP", api_short_version = "v3.0.0", fully_qualified_version = "OBPv3.0.0", is_active = true),
|
||||
ScannedApiVersionJsonV600(url_prefix = "obp", api_standard = "OBP", api_short_version = "v3.1.0", fully_qualified_version = "OBPv3.1.0", is_active = true),
|
||||
ScannedApiVersionJsonV600(url_prefix = "obp", api_standard = "OBP", api_short_version = "v4.0.0", fully_qualified_version = "OBPv4.0.0", is_active = true),
|
||||
ScannedApiVersionJsonV600(url_prefix = "obp", api_standard = "OBP", api_short_version = "v5.0.0", fully_qualified_version = "OBPv5.0.0", is_active = true),
|
||||
ScannedApiVersionJsonV600(url_prefix = "obp", api_standard = "OBP", api_short_version = "v5.1.0", fully_qualified_version = "OBPv5.1.0", is_active = true),
|
||||
ScannedApiVersionJsonV600(url_prefix = "obp", api_standard = "OBP", api_short_version = "v6.0.0", fully_qualified_version = "OBPv6.0.0", is_active = true),
|
||||
ScannedApiVersionJsonV600(url_prefix = "berlin-group", api_standard = "BG", api_short_version = "v1.3", fully_qualified_version = "BGv1.3", is_active = false)
|
||||
)
|
||||
),
|
||||
List(
|
||||
UnknownError
|
||||
),
|
||||
List(apiTagDocumentation, apiTagApi),
|
||||
Some(Nil)
|
||||
)
|
||||
|
||||
lazy val getScannedApiVersions: OBPEndpoint = {
|
||||
case "api" :: "versions" :: Nil JsonGet _ => { cc =>
|
||||
implicit val ec = EndpointContext(Some(cc))
|
||||
Future {
|
||||
val versions: List[ScannedApiVersionJsonV600] =
|
||||
ApiVersion.allScannedApiVersion.asScala.toList
|
||||
.filter(version => version.urlPrefix.trim.nonEmpty)
|
||||
.map { version =>
|
||||
ScannedApiVersionJsonV600(
|
||||
url_prefix = version.urlPrefix,
|
||||
api_standard = version.apiStandard,
|
||||
api_short_version = version.apiShortVersion,
|
||||
fully_qualified_version = version.fullyQualifiedVersion,
|
||||
is_active = versionIsAllowed(version)
|
||||
)
|
||||
}
|
||||
(
|
||||
ListResult("scanned_api_versions", versions),
|
||||
HttpCode.`200`(cc.callContext)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
staticResourceDocs += ResourceDoc(
|
||||
createCustomer,
|
||||
implementedInApiVersion,
|
||||
|
||||
@ -646,4 +646,12 @@ case class PostResetPasswordUrlJsonV600(username: String, email: String, user_id
|
||||
|
||||
case class ResetPasswordUrlJsonV600(reset_password_url: String)
|
||||
|
||||
case class ScannedApiVersionJsonV600(
|
||||
url_prefix: String,
|
||||
api_standard: String,
|
||||
api_short_version: String,
|
||||
fully_qualified_version: String,
|
||||
is_active: Boolean
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@ -77,11 +77,36 @@ object OBPAPI6_0_0 extends OBPRestHelper
|
||||
// Exclude v5.1.0 root endpoint since v6.0.0 has its own
|
||||
lazy val endpointsOf5_1_0_without_root = OBPAPI5_1_0.routes.filterNot(_ == Implementations5_1_0.root)
|
||||
|
||||
/*
|
||||
* IMPORTANT: Endpoint Exclusion Pattern
|
||||
*
|
||||
* excludeEndpoints is used to filter out old endpoints when v6.0.0 has a DIFFERENT URL pattern.
|
||||
*
|
||||
* WHEN TO EXCLUDE:
|
||||
* - Old and new endpoints have DIFFERENT URLs (e.g., v4.0.0: /users/:username vs v6.0.0: /providers/:provider/users/:username)
|
||||
* - The old endpoint should not be accessible via v6.0.0 at all
|
||||
*
|
||||
* WHEN NOT TO EXCLUDE:
|
||||
* - Old and new endpoints have the SAME URL and HTTP method (e.g., GET /api/versions)
|
||||
* - In this case, collectResourceDocs() automatically deduplicates by (URL, method) and keeps newest version
|
||||
* - Excluding by function name would remove BOTH versions since they share the same name!
|
||||
*
|
||||
* Why? The routing works as follows:
|
||||
* 1. endpoints list = endpointsOf6_0_0 ++ endpointsOf5_1_0_without_root (contains BOTH old and new)
|
||||
* 2. allResourceDocs = collectResourceDocs() deduplicates docs by (URL, method), keeps newest
|
||||
* 3. excludeEndpoints filters ResourceDocs by partialFunctionName (removes by name, not by version)
|
||||
* 4. getAllowedEndpoints() filters endpoints to only those with matching ResourceDocs
|
||||
*
|
||||
* Pattern: Add nameOf(Implementations{version}.endpointName) :: with a comment explaining why
|
||||
*/
|
||||
lazy val excludeEndpoints =
|
||||
nameOf(Implementations3_0_0.getUserByUsername) :: // following 4 endpoints miss Provider parameter in the URL, we introduce new ones in V600.
|
||||
nameOf(Implementations3_1_0.getBadLoginStatus) ::
|
||||
nameOf(Implementations3_1_0.unlockUser) ::
|
||||
nameOf(Implementations4_0_0.lockUser) ::
|
||||
// NOTE: getScannedApiVersions is NOT excluded here because it has the same URL in both v4.0.0 and v6.0.0
|
||||
// collectResourceDocs() automatically deduplicates by (URL, HTTP method) and keeps the newest version (v6.0.0)
|
||||
// Excluding by function name would incorrectly filter out BOTH versions since they share the same function name
|
||||
nameOf(Implementations4_0_0.createUserWithAccountAccess) :: // following 3 endpoints miss ViewId parameter in the URL, we introduce new ones in V600.
|
||||
nameOf(Implementations4_0_0.grantUserAccessToView) ::
|
||||
nameOf(Implementations4_0_0.revokeUserAccessToView) ::
|
||||
|
||||
Loading…
Reference in New Issue
Block a user