diff --git a/CLAUDE.md b/CLAUDE.md index f03fae5336..a8ece6c322 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,6 +5,7 @@ - Never add `Co-Authored-By` trailers to commit messages. - **Goal is full http4s migration** — eliminate Lift Web and all deprecated libraries entirely. Treat Lift code as temporary scaffolding to be removed, not maintained. When fixing bugs or adding features, always prefer the http4s path. - **Versioning is tech-agnostic** — API version numbers reflect API signature changes (new/changed fields, new behaviour), never the underlying framework. A framework migration (Lift → http4s) happens in-place at the existing version; it does not justify a version bump. +- **`APIMethodsXYZ.scala` (Lift) files are the source of truth for migration.** The commented-out Lift ResourceDocs and endpoints inside each `APIMethodsXYZ.scala` are the canonical reference for what the http4s version should match: URL templates, verb casing, summaries, descriptions, example bodies, error lists, tags. **Do NOT edit these files to make the parity audit pass.** The audit compares http4s against the Lift source-of-truth — when it flags a diff, the fix is to either (a) update http4s to match Lift, or (b) document the difference at the http4s site as a known intentional drift (e.g. a placeholder rename for `ResourceDocMatcher` middleware, or an upstream-driven case-class shape change). Rewriting the Lift comments to match http4s runs the comparison backwards and destroys the historical record. See `scripts/check_lift_http4s_resource_doc_parity.py` for the audit, and `scripts/rehydrate_resource_docs.py` / `scripts/restore_resource_doc_bodies.py` for the canonical Lift → http4s restoration tools. ## Architecture (Onboarding) diff --git a/LIFT_HTTP4S_MIGRATION.md b/LIFT_HTTP4S_MIGRATION.md index 644c10d614..b26fb73b31 100644 --- a/LIFT_HTTP4S_MIGRATION.md +++ b/LIFT_HTTP4S_MIGRATION.md @@ -164,6 +164,137 @@ Currently served via a raw Lift `serve { case Req(..., "openapi.yaml", ...) }` b --- +## ResourceDoc parity (per-version drift from Lift) + +Separate from the resource-docs **serving** workstream above, there is a parity workstream covering the **content** of each migrated ResourceDoc declaration. The goal is for every http4s `ResourceDoc(...)` to render identically to its Lift original, so the public API docs aren't silently degraded by migration. + +### Principle + +**`APIMethodsXYZ.scala` (Lift) is the source of truth for migration.** The commented-out Lift ResourceDocs and endpoints inside each `APIMethodsXYZ.scala` are the canonical reference for what the http4s version should render: URL templates, verb casing, summaries, descriptions, example bodies, error lists, tags. **Do NOT edit these files to make the audit pass** — the audit compares http4s against the Lift source-of-truth. When the audit flags a diff, the resolution is either (a) update http4s to match Lift, or (b) document the difference at the http4s site as a known intentional drift (placeholder rename for middleware, upstream-driven case-class shift, etc.). Rewriting the Lift comments runs the comparison backwards and erases the historical record. (Mistakes in commits `d95c1df01` and `6154bf2cc` did this; reverted in `27f48af72`.) + +**Stub fidelity verified.** Commits `810589330` (v6) and `88f46f854` (v5.1) replaced the live Lift code with commented-out stubs. Comparing each stub's uncommented ResourceDoc bodies against the pre-stub live versions: **0 field diffs across 243/243 v6 docs and 111/111 v5.1 docs**. The non-ResourceDoc deltas (imports, etc., ~16KB v6 / ~5KB v5.1) are immaterial. The stubs are an exact preservation of the original Lift ResourceDocs. + +### Tooling (`scripts/`) + +| Script | Role | +|---|---| +| `check_lift_http4s_resource_doc_parity.py` | Read-only audit. Parses both files, matches by `nameOf(...)` (with `.replace("a","b")` evaluation for derived names), reports per-field diffs. `--field=X` to focus, `--list-only` for endpoint-presence summary. | +| `rehydrate_resource_docs.py` | Upstream (simonredfern, `67593ea28`). Lifts positional args 7/8/9 (description, exampleRequestBody, successResponseBody) from commented Lift blocks into http4s. Has a `split-init` subcommand for JVM 64KB method-size workaround. | +| `restore_resource_doc_bodies.py` | Companion to the above. Restores any subset of (summary, description, exampleRequestBody, successResponseBody, errorResponseBodies, tags) from Lift into http4s. Surgical per-field replacement preserves layout. `--fields=X,Y` to scope, `--only=ep` to target one endpoint. | + +### Current drift (audit re-run 2026-05-21 evening) + +| Version | shared | mismatch | only-lift | only-http4s | Status | +|---|---|---|---|---|---| +| v1_2_1 | 70 | 48 | 0 | 0 | not started | +| v1_3_0 | 3 | 0 | 0 | 0 | clean | +| v1_4_0 | 10 | 1 | 0 | 0 | one minor | +| v2_0_0 | 37 | 19 | 0 | 0 | not started | +| v2_1_0 | 23 | 13 | 5 | 2 | not started | +| v2_2_0 | 18 | 13 | 0 | 0 | not started | +| v3_0_0 | 47 | 4 | 0 | 0 | semantic fields restored; 4 middleware-driven URL renames remain | +| v3_1_0 | 102 | 5 | 0 | 0 | semantic fields restored; 5 structural drifts (placeholder renames) remain | +| v4_0_0 | 254 | 20 | 2 | 5 | semantic fields restored; 20 structural drifts (placeholder renames + 1 verb fix) remain | +| v5_0_0 | 39 | 8 | 0 | 3 | descriptions restored; structural/errors remain | +| v5_1_0 | 111 | 1 | 1 | 2 | one verb-casing drift to fix | +| v6_0_0 | 243 | 12 | 0 | 1 | 11 placeholder renames + 1 routing-shape upstream change | +| **Total** | **956** | **144** | | | | + +### v6.0.0 — 12 specific drifts (each is a fix candidate) + +These are the cases where http4s deviates from Lift. Under the source-of-truth rule, the default is to fix http4s; deliberate exceptions need to be documented at the http4s site. + +| Endpoint | Field | Lift | http4s | Resolution | +|---|---|---|---|---| +| `createCounterpartyAttribute` | requestUrl | `…/counterparties/COUNTERPARTY_ID/attributes` | `…/COUNTERPARTY_ID_PARAM/…` | TBD — verify `ResourceDocMatcher` correctly handles `COUNTERPARTY_ID` as a wildcard (the literal set contains `COUNTERPARTY`, but `COUNTERPARTY_ID` is whole-segment-different). If safe, revert to Lift's name. | +| `deleteCounterpartyAttribute` | requestUrl | same | same | same as above | +| `getAllCounterpartyAttributes` | requestUrl | same | same | same as above | +| `getCounterpartyAttributeById` | requestUrl | same | same | same as above | +| `updateCounterpartyAttribute` | requestUrl | same | same | same as above | +| `createTransactionRequestCardano` | requestUrl | `…/ACCOUNT_ID/owner/transaction-request-types/CARDANO/…` | `…/ACCOUNT_ID/VIEW_ID/…/CARDANO/…` | **Functional broadening** — http4s lets any view, Lift hardcoded `owner`. Keep http4s; document at the http4s ResourceDoc site. | +| `createTransactionRequestHold` | requestUrl | `…/owner/…HOLD/…` | `…/VIEW_ID/…HOLD/…` | same as above | +| `getSystemViewById` | requestUrl | `/management/system-views/VIEW_ID` | `/management/system-views/SYS_VIEW_ID` | TBD — disambiguation rename. If `ResourceDocMatcher` handles both fine, revert. | +| `updateSystemView` | requestUrl | `/system-views/VIEW_ID` | `/system-views/UPD_VIEW_ID` | same as above | +| `removeBankReaction` | requestUrl | `…/reactions/EMOJI` | `…/reactions/EMOJI_REACTION` | `EMOJI` is NOT in `literalAllCapsSegments` (only `EMAIL`/`SMS`/`IMPLICIT` of the SCA cluster are). Rename may have been defensive; safe to revert. | +| `removeSystemReaction` | requestUrl | same | same | same as above | +| `getAccountDirectory` | successResponseBody | `FastFirehoseRoutings(bank_id, account_id)` | `AccountRoutingJsonV121(scheme, address)` | **Upstream functional change** (`9e151c524` / `9dc4c4c46` migrated the case class). Cannot revert; document. Also note: the same change broke `mvn test` (pre-existing upstream compile error in `JSONFactory6.0.0.scala:2934`). | + +Also: 1 only-http4s (`createWebUiProps`) — genuinely http4s-only with no Lift counterpart. Document. + +### v5.1.0 — 1 specific drift + +| Endpoint | Field | Lift | http4s | Resolution | +|---|---|---|---|---| +| `revokeMyConsent` | requestVerb | `"Delete"` | `"DELETE"` | Trivial casing fix on the http4s side. | + +Also: +- 1 only-lift (`createConsentImplicit`) + 1 only-http4s (`createConsent`) — Lift had `lazy val createConsentImplicit = createConsent` aliasing and registered the doc under the alias; http4s registers under the canonical name. Fix: in http4s, either rename the partial function to `createConsentImplicit` to match Lift, or register a second `nameOf(createConsentImplicit)` doc for the same handler. +- 1 only-http4s (`getBanks`) — kept in the v5.1.0 layer for metrics attribution (intentional addition; see comment at `Http4s510.scala:288`). Document. + +### v3.0.0 — 4 specific drifts + +After semantic-field restoration, only middleware-driven URL renames remain. + +| Endpoint | Field | Lift | http4s | Resolution | +|---|---|---|---|---| +| `createViewForBankAccount` | requestUrl | `…/accounts/ACCOUNT_ID/views` | `…/accounts/VIEW_ACCOUNT_ID/views` | Middleware account-validation bypass (see CLAUDE.md "Middleware URL template bypass" gotcha). **Document** — required. | +| `updateViewForBankAccount` | requestUrl | `…/views/VIEW_ID` | `…/views/UPD_VIEW_ID` | Disambiguation rename. **Document**. | +| `getFirehoseAccountsAtOneBank` | requestUrl | `/banks/BANK_ID/firehose/accounts/views/VIEW_ID` | `/banks/FIREHOSE_BANK_ID/firehose/accounts/views/FIREHOSE_VIEW_ID` | Firehose middleware bypass. **Document**. | +| `getFirehoseTransactionsForBankAccount` | requestUrl | `/banks/BANK_ID/firehose/accounts/ACCOUNT_ID/views/VIEW_ID/transactions` | `/banks/FIREHOSE_BANK_ID/firehose/accounts/FIREHOSE_ACCOUNT_ID/views/FIREHOSE_VIEW_ID/transactions` | Same firehose pattern. **Document**. | + +No only-lift or only-http4s entries for v3.0.0. + +### v3.1.0 — 5 specific drifts + +After semantic-field restoration (commit `f4b9bd183`), only middleware-driven placeholder renames remain. + +| Endpoint | Field | Lift | http4s | Resolution | +|---|---|---|---|---| +| `createAccount` | requestUrl | `/banks/BANK_ID/accounts/ACCOUNT_ID` | `…/NEW_ACCOUNT_ID` | PUT-creates-account pattern. Middleware would 404 on `ACCOUNT_ID` lookup before the handler. **Document** — required. | +| `deleteSystemView` | requestUrl | `/system-views/VIEW_ID` | `/SYS_VIEW_ID` | Disambiguation from other VIEW_ID usages. **Document**. | +| `getSystemView` | requestUrl | same | same | same | +| `updateSystemView` | requestUrl | same | same | same | +| `getFirehoseCustomers` | requestUrl | `/banks/BANK_ID/firehose/customers` | `…/FIREHOSE_BANK_ID/…` | Firehose middleware bypass — prop check must run before bank lookup (see CLAUDE.md). **Document** — required. | + +No only-lift or only-http4s entries for v3.1.0. + +### v4.0.0 — 20 specific drifts + 2 only-lift + 5 only-http4s + +After semantic-field restoration (commit `2b24811e5`), the remaining drifts are all structural / functional: + +| Category | Count | Endpoints | Resolution | +|---|---|---|---| +| requestVerb | 1 | `deleteExplicitCounterparty` (Lift `POST` → http4s `DELETE`) | http4s is REST-correct. **Document** as deliberate fix. | +| requestUrl — `VIEW_ID` → `GRANT_VIEW_ID` | 9 | `answerTransactionRequestChallenge` and 8 `createTransactionRequest*` variants (Account/AccountOtp/AgentCashWithDrawal/Counterparty/FreeForm/Refund/Sepa/Simple) | Middleware disambiguation rename. Verify if `VIEW_ID` collides in `ResourceDocMatcher`; if not, revert. If it does, **document**. | +| requestUrl — hyphen→underscore | 6 | `delete`/`get`/`update` × `BankLevelDynamicResourceDoc` / `DynamicResourceDoc` (Lift `DYNAMIC-RESOURCE-DOC-ID` → http4s `DYNAMIC_RESOURCE_DOC_ID`) | The matcher's ALL_CAPS-with-underscores wildcard requires underscores. **Fix Lift**? No — Lift is source-of-truth. **Document** at the http4s site as a required matcher constraint. | +| requestUrl — `COUNTERPARTY_ID` → `COUNTERPARTY_ID_PARAM` | 2 | `deleteExplicitCounterparty`, `getCounterpartyByIdForAnyAccount` | Same as v6's COUNTERPARTY rename family. Verify matcher behavior; revert if safe. | +| requestUrl — `COUNTERPARTY_ID` → `EXPLICIT_COUNTERPARTY_ID` | 1 | `getExplicitCounterpartyById` | Same defensive rename pattern. | +| requestUrl — firehose pattern | 1 | `getFirehoseAccountsAtOneBank` (Lift `BANK_ID/.../VIEW_ID` → http4s `FIREHOSE_BANK_ID/.../FIREHOSE_VIEW_ID`) | Middleware bypass for the prop-check-before-bank-lookup pattern (see CLAUDE.md "Prop check before role check" gotcha). **Document** — required for correctness. | +| requestUrl — Lift URL malformed | 1 | `deleteCustomerAttribute` (Lift `/banks/BANK_ID/CUSTOMER_ID/attributes/.../...` is missing `/customers/`; http4s uses `/banks/BANK_ID/customers/attributes/...`) | Lift URL was buggy. http4s fixed it. **Document** as deliberate URL fix; flag that the Lift comment preserves the original bug as historical record. | + +Also: 2 only-lift (`getAllAuthenticationTypeValidationsPublic`, `getAllJsonSchemaValidationsPublic`) — these endpoints exist in Lift v4 but were not migrated to `Http4s400`. **Migration gap** — port them. 5 only-http4s (`createBankLevelDynamicEntity`, `createSystemDynamicEntity`, `updateBankLevelDynamicEntity`, `updateMyDynamicEntity`, `updateSystemDynamicEntity`) — dynamic-entity overrides added in http4s with no Lift equivalent. Document if intentional, or audit whether they should have Lift counterparts. + +### v5.0.0 — 8 specific drifts + 3 only-http4s + +| Category | Count | Endpoints | Resolution | +|---|---|---|---| +| requestUrl placeholder rename | 1 | `createAccount` (Lift `ACCOUNT_ID` → http4s `NEW_ACCOUNT_ID` for the PUT-creates pattern) | Verify matcher behavior; may be required for `ACCOUNT_ID` literal handling. | +| errorResponseBodies — SCA val-vs-inline | 3 | `createConsentByConsentRequestIdEmail` / `Sms` / `Implicit` | http4s uses `private val createConsentByConsentRequestIdCommonErrors = List(...)` for DRY; Lift inlined the list. Either inline the val in the 3 doc registrations to match Lift verbatim, or extend the audit script to expand simple `val X = List(...)` references. | +| errorResponseBodies — system-view accuracy | 4 | `createSystemView`, `deleteSystemView`, `getSystemView`, `updateSystemView` | http4s has more accurate errors (`SystemViewNotFound`, `SystemViewCannotBePublicError`, `InvalidSystemViewFormat`). Lift had wrong/legacy errors (`BankAccountNotFound`, `$BankNotFound`, `"user does not have owner access"`). **Genuine improvement** — document at http4s site. | + +Also: 3 only-http4s (`getBanks`, `getProduct`, `getProducts`) — kept in this layer for metrics attribution. Document. + +### Strategy summary + +For each remaining drift on a migrated version: +1. **Default**: fix http4s to match Lift verbatim. Use `restore_resource_doc_bodies.py` for field-level restoration. +2. **Documented exceptions**: where the drift is a deliberate http4s improvement or required by middleware semantics, leave the drift and add a `// Lift had X; we use Y because Z` comment at the http4s ResourceDoc site. +3. **Never**: edit `APIMethodsXYZ.scala` to make the audit pass. The Lift comments are the canonical record. + +Untouched versions (v1_2_1 through v4_0_0, plus v2_1_0) need the same treatment: run `rehydrate_resource_docs.py` then `restore_resource_doc_bodies.py`, then audit and address any residual drifts at the http4s site. + +--- + ## Auth Stack (separate workstream) Token-generation paths — not version-file endpoints. Each `extends RestHelper` and needs to become an http4s route or middleware independently. Can run in parallel with the APIMethods migration. diff --git a/obp-api/src/main/scala/code/api/v3_1_0/Http4s310.scala b/obp-api/src/main/scala/code/api/v3_1_0/Http4s310.scala index d337804b68..c336b41eae 100644 --- a/obp-api/src/main/scala/code/api/v3_1_0/Http4s310.scala +++ b/obp-api/src/main/scala/code/api/v3_1_0/Http4s310.scala @@ -13,7 +13,7 @@ import code.api.util.ApiRole._ import code.api.util.ApiTag._ import code.api.util.ErrorMessages._ import code.api.util.CertificateUtil -import code.api.util.{ApiTrigger, Consent, SecureRandomUtil} +import code.api.util.{ApiTrigger, Consent, Glossary, SecureRandomUtil} import code.api.util.http4s.Http4sRequestAttributes.{EndpointHelpers, RequestOps} import code.api.util.http4s.ResourceDocMiddleware import code.api.util.newstyle.{BalanceNewStyle, ViewNewStyle} @@ -69,6 +69,85 @@ object Http4s310 { type HttpF[A] = OptionT[IO, A] + // Local doc-strings carried over from the commented-out APIMethods310.scala + // so the restored ResourceDoc descriptions compile. Kept verbatim — these + // are referenced inside `s"""..."""` interpolations in the doc text. + private val productAttributeGeneralInfo = + s""" + |Product Attributes are used to describe a financial Product with a list of typed key value pairs. + | + |Each Product Attribute is linked to its Product by PRODUCT_CODE + | + | + """.stripMargin + + private val accountAttributeGeneralInfo = + s""" + |Account Attributes are used to describe a financial Product with a list of typed key value pairs. + | + |Each Account Attribute is linked to its Account by ACCOUNT_ID + | + | + """.stripMargin + + private val supportedConnectorNames = + NewStyle.function.getSupportedConnectorNames().mkString("[", " | ", "]") + + private val generalObpConsentText: String = + s""" + | + |An OBP Consent allows the holder of the Consent to call one or more endpoints. + | + |Consents must be created and authorisied using SCA (Strong Customer Authentication). + | + |That is, Consents can be created by an authorised User via the OBP REST API but they must be confirmed via an out of band (OOB) mechanism such as a code sent to a mobile phone. + | + |Each Consent has one of the following states: ${ConsentStatus.values.toList.sorted.mkString(", ") }. + | + |Each Consent is bound to a consumer i.e. you need to identify yourself over request header value Consumer-Key. + |For example: + |GET /obp/v4.0.0/users/current HTTP/1.1 + |Host: 127.0.0.1:8080 + |Consent-JWT: eyJhbGciOiJIUzI1NiJ9.eyJlbnRpdGxlbWVudHMiOlt7InJvbGVfbmFtZSI6IkNhbkdldEFueVVzZXIiLCJiYW5rX2lkIjoiIn + |1dLCJjcmVhdGVkQnlVc2VySWQiOiJhYjY1MzlhOS1iMTA1LTQ0ODktYTg4My0wYWQ4ZDZjNjE2NTciLCJzdWIiOiIzNDc1MDEzZi03YmY5LTQyNj + |EtOWUxYy0xZTdlNWZjZTJlN2UiLCJhdWQiOiI4MTVhMGVmMS00YjZhLTQyMDUtYjExMi1lNDVmZDZmNGQzYWQiLCJuYmYiOjE1ODA3NDE2NjcsIml + |zcyI6Imh0dHA6XC9cLzEyNy4wLjAuMTo4MDgwIiwiZXhwIjoxNTgwNzQ1MjY3LCJpYXQiOjE1ODA3NDE2NjcsImp0aSI6ImJkYzVjZTk5LTE2ZTY + |tNDM4Yi1hNjllLTU3MTAzN2RhMTg3OCIsInZpZXdzIjpbXX0.L3fEEEhdCVr3qnmyRKBBUaIQ7dk1VjiFaEBW8hUNjfg + | + |Consumer-Key: ejznk505d132ryomnhbx1qmtohurbsbb0kijajsk + |cache-control: no-cache + | + |Maximum time to live of the token is specified over props value consents.max_time_to_live. In case isn't defined default value is 3600 seconds. + | + |Example of POST JSON: + |{ + | "everything": false, + | "views": [ + | { + | "bank_id": "GENODEM1GLS", + | "account_id": "8ca8a7e4-6d02-40e3-a129-0b2bf89de9f0", + | "view_id": "${Constant.SYSTEM_OWNER_VIEW_ID}" + | } + | ], + | "entitlements": [ + | { + | "bank_id": "GENODEM1GLS", + | "role_name": "CanGetCustomersAtOneBank" + | } + | ], + | "consumer_id": "7uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", + | "email": "eveline@example.com", + | "valid_from": "2020-02-07T08:43:34Z", + | "time_to_live": 3600 + |} + |Please note that only optional fields are: consumer_id, valid_from and time_to_live. + |In case you omit they the default values are used: + |consumer_id = consumer of current user + |valid_from = current time + |time_to_live = consents.max_time_to_live + | + """.stripMargin + object Implementations3_1_0 { val prefixPath: Path = Root / ApiPathZero.toString / implementedInApiVersion.toString @@ -1607,12 +1686,23 @@ object Http4s310 { null, implementedInApiVersion, "getProducts", "GET", "/banks/BANK_ID/products", "Get Products", - s"""Returns information about the financial products offered by a bank specified by BANK_ID. - | - |Can filter with attributes name and values. - |URL params example: /banks/some-bank-id/products?&limit=50&offset=1 - | - |${userAuthenticationMessage(!getProductsIsPublic)}""".stripMargin, + s"""Returns information about the financial products offered by a bank specified by BANK_ID including: + | + |* Name + |* Code + |* Parent Product Code + |* Category + |* Family + |* Super Family + |* More info URL + |* Description + |* Terms and Conditions + |* License the data under this endpoint is released under + | + |Can filter with attributes name and values. + |URL params example: /banks/some-bank-id/products?&limit=50&offset=1 + | + |${userAuthenticationMessage(!getProductsIsPublic)}""".stripMargin, EmptyBody, productsJsonV310, List(AuthenticatedUserIsRequired, BankNotFound, ProductNotFoundByProductCode, UnknownError), List(apiTagProduct), None, @@ -2183,10 +2273,22 @@ object Http4s310 { null, implementedInApiVersion, "revokeConsent", "GET", "/banks/BANK_ID/my/consents/CONSENT_ID/revoke", "Revoke Consent", - s"""Revoke Consent for current user specified by CONSENT_ID + s""" + |Revoke Consent for current user specified by CONSENT_ID + | + |There are a few reasons you might need to revoke an application’s access to a user’s account: + | - The user explicitly wishes to revoke the application’s access + | - You as the service provider have determined an application is compromised or malicious, and want to disable it + | - etc. + | + |Please note that this endpoint only supports the case:: "The user explicitly wishes to revoke the application’s access" + | + |OBP as a resource server stores access tokens in a database, then it is relatively easy to revoke some token that belongs to a particular user. + |The status of the token is changed to "REVOKED" so the next time the revoked client makes a request, their token will fail to validate. | |${userAuthenticationMessage(true)} - |""".stripMargin, + | + """.stripMargin, EmptyBody, revokedConsentJsonV310, List(AuthenticatedUserIsRequired, BankNotFound, UnknownError), List(apiTagConsent, apiTagPSD2AIS, apiTagPsd2), None, @@ -3360,9 +3462,17 @@ object Http4s310 { "/banks/BANK_ID/meetings", "Create Meeting (video conference/call)", """Create Meeting: Initiate a video conference/call with the bank. + | + |The Meetings resource contains meta data about video/other conference sessions + | + |provider_id determines the provider of the meeting / video chat service. MUST be url friendly (no spaces). + | + |purpose_id explains the purpose of the chat. onboarding | mortgage | complaint etc. MUST be url friendly (no spaces). | |Login is required. - |""".stripMargin, + | + |This call is **experimental**. Currently staff_user_id is not set. Further calls will be needed to correctly set this. + """.stripMargin, createMeetingJsonV310, meetingJsonV310, List(AuthenticatedUserIsRequired, BankNotFound, InvalidJsonFormat, UnknownError), List(apiTagMeeting, apiTagCustomer, apiTagExperimental), None, @@ -3973,9 +4083,23 @@ object Http4s310 { "/banks/BANK_ID/products/PRODUCT_CODE", "Create Product", s"""Create or Update Product for the Bank. - | - |${userAuthenticationMessage(true)} - |""", + | + | + |Typical Super Family values / Asset classes are: + | + |Debt + |Equity + |FX + |Commodity + |Derivative + | + |$productHiearchyAndCollectionNote + | + | + |${userAuthenticationMessage(true) } + | + | + |""", postPutProductJsonV310, productJsonV310, List(AuthenticatedUserIsRequired, BankNotFound, UserHasMissingRoles, UnknownError), List(apiTagProduct), Some(List(canCreateProduct, canCreateProductAtAnyBank)), @@ -4297,10 +4421,17 @@ object Http4s310 { "/banks/BANK_ID/accounts/NEW_ACCOUNT_ID", "Create Account", """Create Account at bank specified by BANK_ID with Id specified by ACCOUNT_ID. - | - |The User can create an Account for themself - or - the User that has the USER_ID specified in the POST body. - | - |Note: The Amount MUST be zero.""".stripMargin, + | + |The User can create an Account for themself - or - the User that has the USER_ID specified in the POST body. + | + |If the PUT body USER_ID *is* specified, the logged in user must have the Role canCreateAccount. Once created, the Account will be owned by the User specified by USER_ID. + | + |If the PUT body USER_ID is *not* specified, the account will be owned by the logged in User. + | + |The 'product_code' field SHOULD be a product_code from Product. + |If the 'product_code' matches a product_code from Product, account attributes will be created that match the Product Attributes. + | + |Note: The Amount MUST be zero.""".stripMargin, createAccountRequestJsonV310, createAccountResponseJsonV310, List(InvalidJsonFormat, BankNotFound, AuthenticatedUserIsRequired, InvalidUserId, InvalidAccountIdFormat, InvalidBankIdFormat, diff --git a/obp-api/src/main/scala/code/api/v4_0_0/Http4s400.scala b/obp-api/src/main/scala/code/api/v4_0_0/Http4s400.scala index f8ab352905..80f4e567f6 100644 --- a/obp-api/src/main/scala/code/api/v4_0_0/Http4s400.scala +++ b/obp-api/src/main/scala/code/api/v4_0_0/Http4s400.scala @@ -17,6 +17,13 @@ import code.api.util.Glossary import code.api.util.Glossary._ import code.api.dynamic.endpoint.helper.practise.PractiseEndpoint import code.api.Constant +import code.api.v2_1_0.ConsumerPostJSON +import code.api.v3_1_0.ConsentChallengeJsonV310 +import code.api.dynamic.endpoint.helper.practise.PractiseEndpoint +import code.bankconnectors.LocalMappedConnectorInternal._ +import code.consent.ConsentStatus +import com.openbankproject.commons.model.enums.{AttributeCategory, AttributeType, UserInvitationPurpose} +import java.util.Date import code.api.dynamic.endpoint.helper.DynamicEndpointHelper import code.api.dynamic.entity.helper.DynamicEntityInfo import code.api.util.{ApiRole => ApiRoleObj} @@ -102,6 +109,59 @@ object Http4s400 { type HttpF[A] = OptionT[IO, A] + // Local doc-strings carried over from the pre-stub APIMethods400.scala so the + // restored ResourceDoc descriptions compile. Kept verbatim — these are + // referenced inside `s"""..."""` interpolations in the doc text. + private val productAttributeGeneralInfo = + s""" + |Product Attributes are used to describe a financial Product with a list of typed key value pairs. + | + |Each Product Attribute is linked to its Product by PRODUCT_CODE + | + | + """.stripMargin + + private val customerAttributeGeneralInfo = + s""" + |CustomerAttributes are used to enhance the OBP Customer object with Bank specific entities. + | + """.stripMargin + + private val generalWebHookInfo = s""" + |Webhooks are used to call external web services when certain events happen. + | + |For instance, a webhook can be used to notify an external service if a transaction is created on an account. + | + |""" + + private val accountNotificationWebhookInfo = s""" + |When an account notification webhook fires it will POST to the URL you specify during the creation of the webhook. + | + |Inside the payload you will find account_id and transaction_id and also user_ids and customer_ids of the Users / Customers linked to the Account. + | | + |The webhook will POST the following structure to your service: + | + |{ + | "event_name": "OnCreateTransaction", + | "event_id": "9ca9a7e4-6d02-40e3-a129-0b2bf89de9b1", + | "bank_id": "gh.29.uk", + | "account_id": "8ca9a7e4-6d02-40e3-a129-0b2bf89de9b1", + | "transaction_id": "7ca9a7e4-6d02-40e3-a129-0b2bf89de9b1", + | "related_entities": [ + | { + | "user_id": "8ca9a7e4-6d02-40e3-a129-0b2bf89de9b1", + | "customer_ids": ["3ca9a7e4-6d02-40e3-a129-0b2bf89de9b1"] + | } + | ] + |} + | + |Thus, your service should accept the above POST body structure. + | + |In this way, your web service can be informed about an event on an account and act accordingly. + | + |Further information about the account, transaction or related entities can then be retrieved using the standard REST APIs. + |""" + object Implementations4_0_0 { // Expose as a member so ResourceDocsAPIMethods can access it via APIMethods400.Implementations4_0_0.implementedInApiVersion val implementedInApiVersion: com.openbankproject.commons.util.ScannedApiVersion = Http4s400.implementedInApiVersion @@ -423,11 +483,24 @@ object Http4s400 { "/banks", "Create Bank", s"""Create a new bank (Authenticated access). - | - |The user creating this will be automatically assigned the Role CanCreateEntitlementAtOneBank.""", + | + |The user creating this will be automatically assigned the Role CanCreateEntitlementAtOneBank. + |Thus the User can manage the bank they create and assign Roles to other Users. + | + |Only SANDBOX mode (i.e. when connector=mapped in properties file) + |The settlement accounts are automatically created by the system when the bank is created. + |Name and account id are created in accordance to the next rules: + | - Incoming account (name: Default incoming settlement account, Account ID: OBP_DEFAULT_INCOMING_ACCOUNT_ID, currency: EUR) + | - Outgoing account (name: Default outgoing settlement account, Account ID: OBP_DEFAULT_OUTGOING_ACCOUNT_ID, currency: EUR) + | + |""", postBankJson400, bankJson400, - List(InvalidJsonFormat, AuthenticatedUserIsRequired, - InsufficientAuthorisationToCreateBank, UnknownError), + List( + InvalidJsonFormat, + $AuthenticatedUserIsRequired, + InsufficientAuthorisationToCreateBank, + UnknownError + ), List(apiTagBank), Some(List(canCreateBank)), http4sPartialFunction = Some(createBank)) @@ -685,7 +758,11 @@ object Http4s400 { "Create ATM", s"""Create ATM.""", atmJsonV400, atmJsonV400, - List(AuthenticatedUserIsRequired, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + UnknownError + ), List(apiTagATM), Some(List(canCreateAtm, canCreateAtmAtAnyBank)), http4sPartialFunction = Some(createAtm)) @@ -893,9 +970,12 @@ object Http4s400 { null, implementedInApiVersion, "getEntitlements", "GET", "/users/USER_ID/entitlements", "Get Entitlements for User", - "", + s""" + | + | + """.stripMargin, EmptyBody, entitlementsJsonV400, - List(AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), List(apiTagRole, apiTagEntitlement, apiTagUser), Some(List(canGetEntitlementsForAnyUserAtAnyBank)), http4sPartialFunction = Some(getEntitlements)) @@ -931,7 +1011,12 @@ object Http4s400 { | |CanGetAnyUser entitlement is required,""", EmptyBody, userJsonV400, - List(AuthenticatedUserIsRequired, UserHasMissingRoles, UserNotFoundByUserId, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UserNotFoundById, + UnknownError + ), List(apiTagUser), Some(List(canGetAnyUser)), http4sPartialFunction = Some(getUserByUserId)) @@ -962,8 +1047,12 @@ object Http4s400 { | |CanGetAnyUser entitlement is required,""", EmptyBody, userJsonV400, - List(AuthenticatedUserIsRequired, UserHasMissingRoles, - UserNotFoundByProviderAndUsername, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UserNotFoundByProviderAndUsername, + UnknownError + ), List(apiTagUser), Some(List(canGetAnyUser)), http4sPartialFunction = Some(getUserByUsername)) @@ -988,7 +1077,12 @@ object Http4s400 { |${userAuthenticationMessage(true)} |CanGetAnyUser entitlement is required,""", EmptyBody, usersJsonV400, - List(AuthenticatedUserIsRequired, UserHasMissingRoles, UserNotFoundByEmail, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UserNotFoundByEmail, + UnknownError + ), List(apiTagUser), Some(List(canGetAnyUser)), http4sPartialFunction = Some(getUsersByEmail)) @@ -1018,9 +1112,18 @@ object Http4s400 { | |${userAuthenticationMessage(true)} | - |CanGetAnyUser entitlement is required,""", + |CanGetAnyUser entitlement is required, + | + |${urlParametersDocument(false, false)} + |* locked_status (if null ignore) + | + """.stripMargin, EmptyBody, usersJsonV400, - List(AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), List(apiTagUser), Some(List(canGetAnyUser)), http4sPartialFunction = Some(getUsers)) @@ -1051,10 +1154,21 @@ object Http4s400 { null, implementedInApiVersion, "getCustomersByAttributes", "GET", "/banks/BANK_ID/customers", "Get Customers by ATTRIBUTES", - "Gets the Customers specified by attributes", + s"""Gets the Customers specified by attributes + | + |URL params example: /banks/some-bank-id/customers?name=John&age=8 + |URL params example: /banks/some-bank-id/customers?&limit=50&offset=1 + | + | + |""", EmptyBody, ListResult("customers", List(customerWithAttributesJsonV310)), - List(AuthenticatedUserIsRequired, BankNotFound, UserCustomerLinksNotFoundForUser, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + UserCustomerLinksNotFoundForUser, + UnknownError + ), List(apiTagCustomer), Some(List(canGetCustomersAtOneBank)), http4sPartialFunction = Some(getCustomersByAttributes)) @@ -1339,9 +1453,17 @@ object Http4s400 { | |${userAuthenticationMessage(true)}""", createUserCustomerLinkJson, userCustomerLinkJson, - List(AuthenticatedUserIsRequired, InvalidBankIdFormat, BankNotFound, InvalidJsonFormat, - CustomerNotFoundByCustomerId, UserHasMissingRoles, CustomerAlreadyExistsForUser, - CreateUserCustomerLinksError, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidBankIdFormat, + $BankNotFound, + InvalidJsonFormat, + CustomerNotFoundByCustomerId, + UserHasMissingRoles, + CustomerAlreadyExistsForUser, + CreateUserCustomerLinksError, + UnknownError + ), List(apiTagCustomer, apiTagUser), Some(List(canCreateUserCustomerLinkAtAnyBank, canCreateUserCustomerLink)), http4sPartialFunction = Some(createUserCustomerLinks)) @@ -2278,7 +2400,11 @@ object Http4s400 { "Delete My Dynamic Endpoint", s"""Delete a DynamicEndpoint specified by DYNAMIC_ENDPOINT_ID.""", EmptyBody, EmptyBody, - List(AuthenticatedUserIsRequired, DynamicEndpointNotFoundByDynamicEndpointId, UnknownError), + List( + $AuthenticatedUserIsRequired, + DynamicEndpointNotFoundByDynamicEndpointId, + UnknownError + ), List(apiTagManageDynamicEndpoint, apiTagApi), None, http4sPartialFunction = Some(deleteMyDynamicEndpoint)) @@ -2549,8 +2675,12 @@ object Http4s400 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/counterparties", "Get Counterparties (Explicit)", s"""Get the Counterparties that have been explicitly created on the specified Account / View. - | - |${userAuthenticationMessage(true)}""", + | + |For a general introduction to Counterparties in OBP, see ${Glossary + .getGlossaryItemLink("Counterparties")} + | + |${userAuthenticationMessage(true)} + |""".stripMargin, EmptyBody, counterpartiesJson400, List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, $UserNoPermissionAccessView, ViewNotFound, UnknownError), @@ -2579,9 +2709,13 @@ object Http4s400 { null, implementedInApiVersion, "getExplicitCounterpartyById", "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/counterparties/EXPLICIT_COUNTERPARTY_ID", "Get Counterparty by Id (Explicit)", - s"""This endpoint returns a single Counterparty on an Account View specified by its COUNTERPARTY_ID. - | - |${userAuthenticationMessage(true)}""", + s"""This endpoint returns a single Counterparty on an Account View specified by its COUNTERPARTY_ID: + | + |For a general introduction to Counterparties in OBP, see ${Glossary + .getGlossaryItemLink("Counterparties")} + | + |${userAuthenticationMessage(true)} + |""".stripMargin, EmptyBody, counterpartyWithMetadataJson400, List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, $UserNoPermissionAccessView, UnknownError), @@ -2671,14 +2805,29 @@ object Http4s400 { null, implementedInApiVersion, "createCounterparty", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/counterparties", "Create Counterparty (Explicit)", - s"""Create Counterparty (Explicit) for an Account. - | - |${userAuthenticationMessage(true)}""", + s"""This endpoint creates an (Explicit) Counterparty for an Account. + | + |For an introduction to Counterparties in OBP see ${Glossary + .getGlossaryItemLink("Counterparties")} + | + |${userAuthenticationMessage(true)} + | + |""".stripMargin, postCounterpartyJson400, counterpartyWithMetadataJson400, - List($AuthenticatedUserIsRequired, InvalidAccountIdFormat, InvalidBankIdFormat, - InvalidJsonFormat, NoViewPermission, CounterpartyAlreadyExists, - InvalidValueLength, InvalidISOCurrencyCode, UnknownError), - List(apiTagCounterparty, apiTagPSD2PIS, apiTagPsd2, apiTagAccount), None, + List( + $AuthenticatedUserIsRequired, + InvalidAccountIdFormat, + InvalidBankIdFormat, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + InvalidJsonFormat, + InvalidISOCurrencyCode, + ViewNotFound, + CounterpartyAlreadyExists, + UnknownError + ), + List(apiTagCounterparty, apiTagAccount), None, http4sPartialFunction = Some(createExplicitCounterparty)) // ─── getFirehoseAccountsAtOneBank ───────────────────────────────────────── @@ -2848,15 +2997,32 @@ object Http4s400 { staticResourceDocs += ResourceDoc( null, implementedInApiVersion, "createTransactionRequestAccount", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/GRANT_VIEW_ID/transaction-request-types/TRANSACTION_REQUEST_TYPE/transaction-requests", - "Create Transaction Request", - s"""Create a Transaction Request of the type specified in the URL. - | - |${userAuthenticationMessage(true)}""", - EmptyBody, transactionRequestWithChargeJSON400, - List($AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidNumber, NotPositiveAmount, - InvalidTransactionRequestType, InvalidISOCurrencyCode, + "Create Transaction Request (ACCOUNT)", + s"""When using ACCOUNT, the payee is set in the request body. + | + |Money goes into the BANK_ID and ACCOUNT_ID specified in the request body. + | + |$transactionRequestGeneralText + | + """.stripMargin, + transactionRequestBodyJsonV200, transactionRequestWithChargeJSON400, + List( + $AuthenticatedUserIsRequired, + InvalidBankIdFormat, + InvalidAccountIdFormat, + InvalidJsonFormat, + $BankNotFound, + AccountNotFound, + $BankAccountNotFound, InsufficientAuthorisationToCreateTransactionRequest, - InvalidAccountIdFormat, InvalidBankIdFormat, TransactionDisabled, UnknownError), + InvalidTransactionRequestType, + InvalidJsonFormat, + InvalidNumber, + NotPositiveAmount, + InvalidTransactionRequestCurrency, + TransactionDisabled, + UnknownError + ), List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(createTransactionRequest)) @@ -2873,14 +3039,31 @@ object Http4s400 { null, implementedInApiVersion, "createTransactionRequestAccountOtp", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/GRANT_VIEW_ID/transaction-request-types/ACCOUNT_OTP/transaction-requests", "Create Transaction Request (ACCOUNT_OTP)", - s"""Create Transaction Request (ACCOUNT_OTP). - | - |${userAuthenticationMessage(true)}""", + s"""When using ACCOUNT, the payee is set in the request body. + | + |Money goes into the BANK_ID and ACCOUNT_ID specified in the request body. + | + |$transactionRequestGeneralText + | + """.stripMargin, transactionRequestBodyJsonV200, transactionRequestWithChargeJSON400, - List($AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidNumber, NotPositiveAmount, - InvalidTransactionRequestType, InvalidISOCurrencyCode, + List( + $AuthenticatedUserIsRequired, + InvalidBankIdFormat, + InvalidAccountIdFormat, + InvalidJsonFormat, + $BankNotFound, + AccountNotFound, + $BankAccountNotFound, InsufficientAuthorisationToCreateTransactionRequest, - InvalidAccountIdFormat, InvalidBankIdFormat, TransactionDisabled, UnknownError), + InvalidTransactionRequestType, + InvalidJsonFormat, + InvalidNumber, + NotPositiveAmount, + InvalidTransactionRequestCurrency, + TransactionDisabled, + UnknownError + ), List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(createTransactionRequest)) @@ -2888,14 +3071,33 @@ object Http4s400 { null, implementedInApiVersion, "createTransactionRequestSepa", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/GRANT_VIEW_ID/transaction-request-types/SEPA/transaction-requests", "Create Transaction Request (SEPA)", - s"""Create Transaction Request (SEPA). - | - |${userAuthenticationMessage(true)}""", + s""" + |Special instructions for SEPA: + | + |When using a SEPA Transaction Request, you specify the IBAN of a Counterparty in the body of the request. + |The routing details (IBAN) of the counterparty will be forwarded to the core banking system for the transfer. + | + |$transactionRequestGeneralText + | + """.stripMargin, transactionRequestBodySEPAJsonV400, transactionRequestWithChargeJSON400, - List($AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidNumber, NotPositiveAmount, - InvalidTransactionRequestType, InvalidISOCurrencyCode, + List( + $AuthenticatedUserIsRequired, + InvalidBankIdFormat, + InvalidAccountIdFormat, + InvalidJsonFormat, + $BankNotFound, + AccountNotFound, + $BankAccountNotFound, InsufficientAuthorisationToCreateTransactionRequest, - InvalidAccountIdFormat, InvalidBankIdFormat, TransactionDisabled, UnknownError), + InvalidTransactionRequestType, + InvalidJsonFormat, + InvalidNumber, + NotPositiveAmount, + InvalidTransactionRequestCurrency, + TransactionDisabled, + UnknownError + ), List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(createTransactionRequest)) @@ -2903,14 +3105,40 @@ object Http4s400 { null, implementedInApiVersion, "createTransactionRequestCounterparty", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/GRANT_VIEW_ID/transaction-request-types/COUNTERPARTY/transaction-requests", "Create Transaction Request (COUNTERPARTY)", - s"""Create Transaction Request (COUNTERPARTY). - | - |${userAuthenticationMessage(true)}""", + s""" + |$transactionRequestGeneralText + | + |When using a COUNTERPARTY to create a Transaction Request, specify the counterparty_id in the body of the request. + |The routing details of the counterparty will be forwarded to the Core Banking System (CBS) for the transfer. + | + |COUNTERPARTY Transaction Requests are used for Variable Recurring Payments (VRP). Use the following ${Glossary + .getApiExplorerLink( + "endpoint", + "OBPv5.1.0-createVRPConsentRequest" + )} to create a consent for VRPs. + | + |For a general introduction to Counterparties in OBP, see ${Glossary + .getGlossaryItemLink("Counterparties")} + | + """.stripMargin, transactionRequestBodyCounterpartyJSON, transactionRequestWithChargeJSON400, - List($AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidNumber, NotPositiveAmount, - InvalidTransactionRequestType, InvalidISOCurrencyCode, + List( + $AuthenticatedUserIsRequired, + InvalidBankIdFormat, + InvalidAccountIdFormat, + InvalidJsonFormat, + $BankNotFound, + AccountNotFound, + $BankAccountNotFound, InsufficientAuthorisationToCreateTransactionRequest, - InvalidAccountIdFormat, InvalidBankIdFormat, TransactionDisabled, UnknownError), + InvalidTransactionRequestType, + InvalidJsonFormat, + InvalidNumber, + NotPositiveAmount, + InvalidTransactionRequestCurrency, + TransactionDisabled, + UnknownError + ), List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(createTransactionRequest)) @@ -2918,14 +3146,40 @@ object Http4s400 { null, implementedInApiVersion, "createTransactionRequestRefund", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/GRANT_VIEW_ID/transaction-request-types/REFUND/transaction-requests", "Create Transaction Request (REFUND)", - s"""Create Transaction Request (REFUND). - | - |${userAuthenticationMessage(true)}""", + s""" + | + |Either the `from` or the `to` field must be filled. Those fields refers to the information about the party that will be refunded. + | + |In case the `from` object is used, it means that the refund comes from the part that sent you a transaction. + |In the `from` object, you have two choices : + |- Use `bank_id` and `account_id` fields if the other account is registered on the OBP-API + |- Use the `counterparty_id` field in case the counterparty account is out of the OBP-API + | + |In case the `to` object is used, it means you send a request to a counterparty to ask for a refund on a previous transaction you sent. + |(This case is not managed by the OBP-API and require an external adapter) + | + | + |$transactionRequestGeneralText + | + """.stripMargin, transactionRequestBodyRefundJsonV400, transactionRequestWithChargeJSON400, - List($AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidNumber, NotPositiveAmount, - InvalidTransactionRequestType, InvalidISOCurrencyCode, + List( + $AuthenticatedUserIsRequired, + InvalidBankIdFormat, + InvalidAccountIdFormat, + InvalidJsonFormat, + $BankNotFound, + AccountNotFound, + $BankAccountNotFound, InsufficientAuthorisationToCreateTransactionRequest, - InvalidAccountIdFormat, InvalidBankIdFormat, TransactionDisabled, UnknownError), + InvalidTransactionRequestType, + InvalidJsonFormat, + InvalidNumber, + NotPositiveAmount, + InvalidTransactionRequestCurrency, + TransactionDisabled, + UnknownError + ), List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(createTransactionRequest)) @@ -2933,14 +3187,27 @@ object Http4s400 { null, implementedInApiVersion, "createTransactionRequestFreeForm", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/GRANT_VIEW_ID/transaction-request-types/FREE_FORM/transaction-requests", "Create Transaction Request (FREE_FORM)", - s"""Create Transaction Request (FREE_FORM). - | - |${userAuthenticationMessage(true)}""", + s"""$transactionRequestGeneralText + | + """.stripMargin, transactionRequestBodyFreeFormJSON, transactionRequestWithChargeJSON400, - List($AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidNumber, NotPositiveAmount, - InvalidTransactionRequestType, InvalidISOCurrencyCode, + List( + $AuthenticatedUserIsRequired, + InvalidBankIdFormat, + InvalidAccountIdFormat, + InvalidJsonFormat, + $BankNotFound, + AccountNotFound, + $BankAccountNotFound, InsufficientAuthorisationToCreateTransactionRequest, - InvalidAccountIdFormat, InvalidBankIdFormat, TransactionDisabled, UnknownError), + InvalidTransactionRequestType, + InvalidJsonFormat, + InvalidNumber, + NotPositiveAmount, + InvalidTransactionRequestCurrency, + TransactionDisabled, + UnknownError + ), List(apiTagTransactionRequest, apiTagPSD2PIS), Some(List(canCreateAnyTransactionRequest)), http4sPartialFunction = Some(createTransactionRequest)) @@ -2949,14 +3216,32 @@ object Http4s400 { null, implementedInApiVersion, "createTransactionRequestSimple", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/GRANT_VIEW_ID/transaction-request-types/SIMPLE/transaction-requests", "Create Transaction Request (SIMPLE)", - s"""Create Transaction Request (SIMPLE). - | - |${userAuthenticationMessage(true)}""", + s""" + |Special instructions for SIMPLE: + | + |You can transfer money to the Bank Account Number or IBAN directly. + | + |$transactionRequestGeneralText + | + """.stripMargin, transactionRequestBodySimpleJsonV400, transactionRequestWithChargeJSON400, - List($AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidNumber, NotPositiveAmount, - InvalidTransactionRequestType, InvalidISOCurrencyCode, + List( + $AuthenticatedUserIsRequired, + InvalidBankIdFormat, + InvalidAccountIdFormat, + InvalidJsonFormat, + $BankNotFound, + AccountNotFound, + $BankAccountNotFound, InsufficientAuthorisationToCreateTransactionRequest, - InvalidAccountIdFormat, InvalidBankIdFormat, TransactionDisabled, UnknownError), + InvalidTransactionRequestType, + InvalidJsonFormat, + InvalidNumber, + NotPositiveAmount, + InvalidTransactionRequestCurrency, + TransactionDisabled, + UnknownError + ), List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(createTransactionRequest)) @@ -2964,14 +3249,40 @@ object Http4s400 { null, implementedInApiVersion, "createTransactionRequestAgentCashWithDrawal", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/GRANT_VIEW_ID/transaction-request-types/AGENT_CASH_WITHDRAWAL/transaction-requests", "Create Transaction Request (AGENT_CASH_WITHDRAWAL)", - s"""Create Transaction Request (AGENT_CASH_WITHDRAWAL). - | - |${userAuthenticationMessage(true)}""", + s""" + | + |Either the `from` or the `to` field must be filled. Those fields refers to the information about the party that will be refunded. + | + |In case the `from` object is used, it means that the refund comes from the part that sent you a transaction. + |In the `from` object, you have two choices : + |- Use `bank_id` and `account_id` fields if the other account is registered on the OBP-API + |- Use the `counterparty_id` field in case the counterparty account is out of the OBP-API + | + |In case the `to` object is used, it means you send a request to a counterparty to ask for a refund on a previous transaction you sent. + |(This case is not managed by the OBP-API and require an external adapter) + | + | + |$transactionRequestGeneralText + | + """.stripMargin, transactionRequestBodyAgentJsonV400, transactionRequestWithChargeJSON400, - List($AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidNumber, NotPositiveAmount, - InvalidTransactionRequestType, InvalidISOCurrencyCode, + List( + $AuthenticatedUserIsRequired, + InvalidBankIdFormat, + InvalidAccountIdFormat, + InvalidJsonFormat, + $BankNotFound, + AccountNotFound, + $BankAccountNotFound, InsufficientAuthorisationToCreateTransactionRequest, - InvalidAccountIdFormat, InvalidBankIdFormat, TransactionDisabled, UnknownError), + InvalidTransactionRequestType, + InvalidJsonFormat, + InvalidNumber, + NotPositiveAmount, + InvalidTransactionRequestCurrency, + TransactionDisabled, + UnknownError + ), List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(createTransactionRequest)) } @@ -3008,14 +3319,33 @@ object Http4s400 { null, implementedInApiVersion, "createTransactionRequestCard", "POST", "/transaction-request-types/CARD/transaction-requests", "Create Transaction Request (CARD)", - s"""Create Transaction Request (CARD). - | - |${userAuthenticationMessage(true)}""", + s""" + | + |When using CARD, the payee is set in the request body . + | + |Money goes into the Counterparty in the request body. + | + |$transactionRequestGeneralText + | + """.stripMargin, transactionRequestBodyCardJsonV400, transactionRequestWithChargeJSON400, - List($AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidNumber, NotPositiveAmount, - InvalidTransactionRequestType, InvalidISOCurrencyCode, + List( + $AuthenticatedUserIsRequired, + InvalidBankIdFormat, + InvalidAccountIdFormat, + InvalidJsonFormat, + $BankNotFound, + AccountNotFound, + $BankAccountNotFound, InsufficientAuthorisationToCreateTransactionRequest, - InvalidAccountIdFormat, InvalidBankIdFormat, TransactionDisabled, UnknownError), + InvalidTransactionRequestType, + InvalidJsonFormat, + InvalidNumber, + NotPositiveAmount, + InvalidTransactionRequestCurrency, + TransactionDisabled, + UnknownError + ), List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(createTransactionRequestCard)) @@ -3190,13 +3520,60 @@ object Http4s400 { "/banks/BANK_ID/accounts/ACCOUNT_ID/GRANT_VIEW_ID/transaction-request-types/TRANSACTION_REQUEST_TYPE/transaction-requests/TRANSACTION_REQUEST_ID/challenge", "Answer Transaction Request Challenge", s"""In Sandbox mode, any string that can be converted to a positive integer will be accepted as an answer. - | - |${userAuthenticationMessage(true)}""", - challengeAnswerJson400, transactionRequestWithChargeJSON400, - List($AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidBankIdFormat, - InvalidAccountIdFormat, InvalidTransactionRequestChallengeId, - AllowedAttemptsUsedUp, TransactionRequestStatusNotInitiatedOrPendingOrForwarded, - TransactionRequestTypeHasChanged, UnknownError), + | + |This endpoint totally depends on createTransactionRequest, it need get the following data from createTransactionRequest response body. + | + |1)`TRANSACTION_REQUEST_TYPE` : is the same as createTransactionRequest request URL . + | + |2)`TRANSACTION_REQUEST_ID` : is the `id` field in createTransactionRequest response body. + | + |3) `id` : is `challenge.id` field in createTransactionRequest response body. + | + |4) `answer` : must be `123` in case that Strong Customer Authentication method for OTP challenge is dummy. + | For instance: SANDBOX_TAN_OTP_INSTRUCTION_TRANSPORT=dummy + | Possible values are dummy,email and sms + | In CBS mode, the answer can be got by phone message or other SCA methods. + | + |Note that each Transaction Request Type can have its own OTP_INSTRUCTION_TRANSPORT method. + |OTP_INSTRUCTION_TRANSPORT methods are set in Props. See sample.props.template for instructions. + | + |Single or Multiple authorisations + | + |OBP allows single or multi party authorisations. + | + |Single party authorisation: + | + |In the case that only one person needs to authorise i.e. answer a security challenge we have the following change of state of a `transaction request`: + | INITIATED => COMPLETED + | + | + |Multiparty authorisation: + | + |In the case that multiple parties (n persons) need to authorise a transaction request i.e. answer security challenges, we have the followings state flow for a `transaction request`: + | INITIATED => NEXT_CHALLENGE_PENDING => ... => NEXT_CHALLENGE_PENDING => COMPLETED + | + |The security challenge is bound to a user i.e. in the case of a correct answer but the user is different than expected the challenge will fail. + | + |Rule for calculating number of security challenges: + |If Product Account attribute REQUIRED_CHALLENGE_ANSWERS=N then create N challenges + |(one for every user that has a View where permission $CAN_ADD_TRANSACTION_REQUEST_TO_ANY_ACCOUNT=true) + |In the case REQUIRED_CHALLENGE_ANSWERS is not defined as an account attribute, the default number of security challenges created is one. + | + """.stripMargin, + challengeAnswerJson400, transactionRequestWithChargeJSON210, + List( + $AuthenticatedUserIsRequired, + InvalidBankIdFormat, + InvalidAccountIdFormat, + InvalidJsonFormat, + $BankNotFound, + $BankAccountNotFound, + TransactionRequestStatusNotInitiated, + TransactionRequestTypeHasChanged, + AllowedAttemptsUsedUp, + TransactionDisabled, + UnknownError + ), List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(answerTransactionRequestChallenge)) @@ -3773,9 +4150,11 @@ object Http4s400 { null, implementedInApiVersion, "deleteTagForViewOnAccount", "DELETE", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/metadata/tags/TAG_ID", "Delete a tag on account", - s"""Delete a tag on account. - | - |${userAuthenticationMessage(true)}""", + s"""Deletes the tag TAG_ID about the account ACCOUNT_ID made on [view](#1_2_1-getViewsForBankAccount). + | + |${userAuthenticationMessage(true)} + | + |Authentication is required as the tag is linked with the user.""", EmptyBody, EmptyBody, List(NoViewPermission, ViewNotFound, $AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, $UserNoPermissionAccessView, UnknownError), @@ -3786,9 +4165,10 @@ object Http4s400 { null, implementedInApiVersion, "getTagsForViewOnAccount", "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/metadata/tags", "Get tags on account", - s"""Get tags on account. - | - |${userAuthenticationMessage(true)}""", + s"""Returns the account ACCOUNT_ID tags made on a [view](#1_2_1-getViewsForBankAccount) (VIEW_ID). + |${userAuthenticationMessage(true)} + | + |Authentication is required as the tag is linked with the user.""", EmptyBody, accountTagsJSON, List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, NoViewPermission, $UserNoPermissionAccessView, UnknownError), @@ -3799,13 +4179,23 @@ object Http4s400 { null, implementedInApiVersion, "addTagForViewOnAccount", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/metadata/tags", "Create a tag on account", - s"""Create a tag on account. - | - |${userAuthenticationMessage(true)}""", - code.api.v1_2_1.PostTransactionTagJSON("tag-value-example"), + s"""Posts a tag about an account ACCOUNT_ID on a [view](#1_2_1-getViewsForBankAccount) VIEW_ID. + | + |${userAuthenticationMessage(true)} + | + |Authentication is required as the tag is linked with the user.""", + postAccountTagJSON, accountTagJSON, - List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, - InvalidJsonFormat, NoViewPermission, $UserNoPermissionAccessView, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + InvalidJsonFormat, + NoViewPermission, + $UserNoPermissionAccessView, + UnknownError + ), List(apiTagAccountMetadata, apiTagAccount), None, http4sPartialFunction = Some(addTagForViewOnAccount)) @@ -4765,9 +5155,13 @@ object Http4s400 { null, implementedInApiVersion, "grantUserAccessToView", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/account-access/grant", "Grant User access to View", - s"""Grant User access to View. - | - |${userAuthenticationMessage(true)} and the user needs to be account holder.""", + s"""Grants the User identified by USER_ID access to the view identified by VIEW_ID. + | + |${userAuthenticationMessage( + true + )} and the user needs to be account holder. + | + |""", postAccountAccessJsonV400, viewJsonV300, List($AuthenticatedUserIsRequired, UserLacksPermissionCanGrantAccessToViewForTargetAccount, @@ -4780,9 +5174,13 @@ object Http4s400 { null, implementedInApiVersion, "revokeUserAccessToView", "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/account-access/revoke", "Revoke User access to View", - s"""Revoke User access to View. - | - |${userAuthenticationMessage(true)} and the user needs to be account holder.""", + s"""Revoke the User identified by USER_ID access to the view identified by VIEW_ID. + | + |${userAuthenticationMessage( + true + )} and the user needs to be account holder. + | + |""", postAccountAccessJsonV400, revokedJsonV400, List($AuthenticatedUserIsRequired, UserLacksPermissionCanRevokeAccessToViewForTargetAccount, @@ -4795,9 +5193,13 @@ object Http4s400 { null, implementedInApiVersion, "revokeGrantUserAccessToViews", "PUT", "/banks/BANK_ID/accounts/ACCOUNT_ID/account-access", "Revoke/Grant User access to View", - s"""Revoke/Grant User access to View. - | - |${userAuthenticationMessage(true)} and the user needs to be an account holder or has owner view access.""", + s"""Revoke/Grant the logged in User access to the views identified by json. + | + |${userAuthenticationMessage( + true + )} and the user needs to be an account holder or has owner view access. + | + |""", postRevokeGrantAccountAccessJsonV400, revokedJsonV400, List($AuthenticatedUserIsRequired, UserLacksPermissionCanGrantAccessToViewForTargetAccount, @@ -7395,7 +7797,10 @@ object Http4s400 { "Delete ATM", s"""Delete ATM.""", EmptyBody, EmptyBody, - List(UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UnknownError + ), List(apiTagATM), Some(List(canDeleteAtm, canDeleteAtmAtAnyBank)), http4sPartialFunction = Some(deleteAtm)) @@ -10209,11 +10614,19 @@ object Http4s400 { "/management/consumers", "Post a Consumer", s"""Create a Consumer (Authenticated access).""", - code.api.v2_1_0.ConsumerPostJSON( - "Test", "Web", "Description", "some@email.com", "redirecturl", "createdby", true, new java.util.Date(), + ConsumerPostJSON( + "Test", + "Web", + "Description", + "some@email.com", + "redirecturl", + "createdby", + true, + new Date(), """-----BEGIN CERTIFICATE----- |client_certificate_content - |-----END CERTIFICATE-----""".stripMargin), + |-----END CERTIFICATE-----""".stripMargin + ), consumerJsonV400, List(AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), List(apiTagConsumer), @@ -10224,7 +10637,14 @@ object Http4s400 { null, implementedInApiVersion, "createCounterpartyForAnyAccount", "POST", "/management/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/counterparties", "Create Counterparty for any account (Explicit)", - s"""This is a management endpoint that allows the creation of a Counterparty on any Account.""", + s"""This is a management endpoint that allows the creation of a Counterparty on any Account. + | + |For an introduction to Counterparties in OBP, see ${Glossary + .getGlossaryItemLink("Counterparties")} + | + |${userAuthenticationMessage(true)} + | + |""".stripMargin, postCounterpartyJson400, counterpartyWithMetadataJson400, List($AuthenticatedUserIsRequired, InvalidAccountIdFormat, InvalidBankIdFormat, $BankNotFound, $BankAccountNotFound, AccountNotFound, InvalidJsonFormat, diff --git a/obp-api/src/main/scala/code/api/v5_0_0/Http4s500.scala b/obp-api/src/main/scala/code/api/v5_0_0/Http4s500.scala index 23f65fc3d8..878c4d47b6 100644 --- a/obp-api/src/main/scala/code/api/v5_0_0/Http4s500.scala +++ b/obp-api/src/main/scala/code/api/v5_0_0/Http4s500.scala @@ -645,9 +645,17 @@ object Http4s500 { null, implementedInApiVersion, nameOf(createAccount), "PUT", "/banks/BANK_ID/accounts/NEW_ACCOUNT_ID", "Create Account (PUT)", """Create Account at bank specified by BANK_ID with Id specified by ACCOUNT_ID. - | - |The User can create an Account for themself - or - the User specified in the PUT body. - |If the PUT body USER_ID is specified, the logged in user must have the Role canCreateAccount.""".stripMargin, + | + |The User can create an Account for themself - or - the User that has the USER_ID specified in the POST body. + | + |If the PUT body USER_ID *is* specified, the logged in user must have the Role canCreateAccount. Once created, the Account will be owned by the User specified by USER_ID. + | + |If the PUT body USER_ID is *not* specified, the account will be owned by the logged in User. + | + |The 'product_code' field SHOULD be a product_code from Product. + |If the 'product_code' matches a product_code from Product, account attributes will be created that match the Product Attributes. + | + |Note: The Amount MUST be zero.""".stripMargin, createAccountRequestJsonV500, createAccountResponseJsonV310, List(InvalidJsonFormat, BankNotFound, AuthenticatedUserIsRequired, InvalidUserId, InvalidAccountIdFormat, InvalidBankIdFormat, UserNotFoundById, UserHasMissingRoles, @@ -1345,7 +1353,22 @@ object Http4s500 { null, implementedInApiVersion, nameOf(createConsentByConsentRequestId).replace("Id", "IdEmail"), "POST", "/consumer/consent-requests/CONSENT_REQUEST_ID/EMAIL/consents", "Create Consent By CONSENT_REQUEST_ID (EMAIL)", - "Answer a Consent Request and create the resulting Consent, with an EMAIL Strong Customer Authentication challenge.", + s""" + |Answer a Consent Request and create the resulting Consent, with an EMAIL Strong Customer Authentication challenge. + | + |After the TPP has called Create Consent Request (Client Credentials), the User authenticates and answers the request via this endpoint. This creates the Consent (the credential the consumer will use to access OBP on the User's behalf). + | + |An SCA challenge code is sent to the email address that was supplied in the Create Consent Request body. The User then completes SCA via Answer Consent Challenge, which moves the Consent from INITIATED to ACCEPTED. + | + |Pinning: the resulting Consent is pinned to a single consumer at creation. The pinned consumer is taken from the `consumer_id` field of the original Create Consent Request body if present, otherwise from the consumer that created the Request. After creation, only that consumer can present the resulting Consent JWT — any other consumer presenting it gets ConsentNotFound (consumer mismatch). + | + |Each Consent Request can be answered exactly once. A second call returns ConsentRequestIsInvalid. + | + |The Consent's authority is bounded by the answering User's own entitlements — it cannot grant access beyond what that User already has. + | + |Authentication: Any authenticated User may answer a Consent Request whose CONSENT_REQUEST_ID they know. This will be tightened in v6.0.0; until then, treat CONSENT_REQUEST_IDs as sensitive. + | + |""", EmptyBody, consentJsonV500, createConsentByConsentRequestIdCommonErrors, apiTagConsent :: apiTagPSD2AIS :: apiTagPsd2 :: apiTagVrp :: Nil, @@ -1357,7 +1380,22 @@ object Http4s500 { null, implementedInApiVersion, nameOf(createConsentByConsentRequestId).replace("Id", "IdSms"), "POST", "/consumer/consent-requests/CONSENT_REQUEST_ID/SMS/consents", "Create Consent By CONSENT_REQUEST_ID (SMS)", - "Answer a Consent Request and create the resulting Consent, with an SMS Strong Customer Authentication challenge.", + s""" + |Answer a Consent Request and create the resulting Consent, with an SMS Strong Customer Authentication challenge. + | + |After the TPP has called Create Consent Request (Client Credentials), the User authenticates and answers the request via this endpoint. This creates the Consent (the credential the consumer will use to access OBP on the User's behalf). + | + |An SCA challenge code is sent to the phone number that was supplied in the Create Consent Request body. The User then completes SCA via Answer Consent Challenge, which moves the Consent from INITIATED to ACCEPTED. + | + |Pinning: the resulting Consent is pinned to a single consumer at creation. The pinned consumer is taken from the `consumer_id` field of the original Create Consent Request body if present, otherwise from the consumer that created the Request. After creation, only that consumer can present the resulting Consent JWT — any other consumer presenting it gets ConsentNotFound (consumer mismatch). + | + |Each Consent Request can be answered exactly once. A second call returns ConsentRequestIsInvalid. + | + |The Consent's authority is bounded by the answering User's own entitlements — it cannot grant access beyond what that User already has. + | + |Authentication: Any authenticated User may answer a Consent Request whose CONSENT_REQUEST_ID they know. This will be tightened in v6.0.0; until then, treat CONSENT_REQUEST_IDs as sensitive. + | + |""", EmptyBody, consentJsonV500, ConsentRequestIsInvalid :: MissingPropsValueAtThisInstance :: SmsServerNotResponding :: createConsentByConsentRequestIdCommonErrors, apiTagConsent :: apiTagPSD2AIS :: apiTagPsd2 :: Nil, @@ -1369,7 +1407,22 @@ object Http4s500 { null, implementedInApiVersion, nameOf(createConsentByConsentRequestId).replace("Id", "IdImplicit"), "POST", "/consumer/consent-requests/CONSENT_REQUEST_ID/IMPLICIT/consents", "Create Consent By CONSENT_REQUEST_ID (IMPLICIT)", - "Answer a Consent Request and create the resulting Consent without an SCA challenge.", + s""" + |Answer a Consent Request and create the resulting Consent, without a Strong Customer Authentication challenge — the Consent is moved directly from INITIATED to ACCEPTED. + | + |After the TPP has called Create Consent Request (Client Credentials), the User authenticates and answers the request via this endpoint. This creates the Consent (the credential the consumer will use to access OBP on the User's behalf). + | + |IMPLICIT means no SCA challenge is sent. The Consent is immediately ACCEPTED. Use only in flows where the User has already been strongly authenticated by upstream means; for production use behind a public TPP, prefer EMAIL or SMS. + | + |Pinning: the resulting Consent is pinned to a single consumer at creation. The pinned consumer is taken from the `consumer_id` field of the original Create Consent Request body if present, otherwise from the consumer that created the Request. After creation, only that consumer can present the resulting Consent JWT — any other consumer presenting it gets ConsentNotFound (consumer mismatch). + | + |Each Consent Request can be answered exactly once. A second call returns ConsentRequestIsInvalid. + | + |The Consent's authority is bounded by the answering User's own entitlements — it cannot grant access beyond what that User already has. + | + |Authentication: Any authenticated User may answer a Consent Request whose CONSENT_REQUEST_ID they know. This will be tightened in v6.0.0; until then, treat CONSENT_REQUEST_IDs as sensitive. + | + |""", EmptyBody, consentJsonV500, ConsentRequestIsInvalid :: MissingPropsValueAtThisInstance :: SmsServerNotResponding :: createConsentByConsentRequestIdCommonErrors, apiTagConsent :: apiTagPSD2AIS :: apiTagPsd2 :: Nil, diff --git a/obp-api/src/main/scala/code/api/v5_1_0/Http4s510.scala b/obp-api/src/main/scala/code/api/v5_1_0/Http4s510.scala index 74602b2735..5807783cfb 100644 --- a/obp-api/src/main/scala/code/api/v5_1_0/Http4s510.scala +++ b/obp-api/src/main/scala/code/api/v5_1_0/Http4s510.scala @@ -2,6 +2,7 @@ package code.api.v5_1_0 import cats.data.{Kleisli, OptionT} import cats.effect._ +import code.api.Constant import code.api.Constant._ import code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON._ import code.api.util.APIUtil._ @@ -235,9 +236,49 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(getAggregateMetrics), "GET", "/management/aggregate-metrics", "Get Aggregate Metrics", - s"""Returns aggregated metrics. Requires CanReadAggregateMetrics role. + s"""Returns aggregate metrics on api usage eg. total count, response time (in ms), etc. | - |${userAuthenticationMessage(true)}""", + |Should be able to filter on the following fields + | + |eg: /management/aggregate-metrics?from_date=$DateWithMsExampleString&to_date=$DateWithMsExampleString&consumer_id=5 + |&user_id=66214b8e-259e-44ad-8868-3eb47be70646&implemented_by_partial_function=getTransactionsForBankAccount + |&implemented_in_version=v3.0.0&url=/obp/v3.0.0/banks/gh.29.uk/accounts/8ca8a7e4-6d02-48e3-a029-0b2bf89de9f0/owner/transactions + |&verb=GET&anon=false&app_name=MapperPostman + |&exclude_app_names=API-EXPLORER,API-Manager,SOFI,null + | + |1 from_date (defaults to the day before the current date): eg:from_date=$DateWithMsExampleString + | + |2 to_date (defaults to the current date) eg:to_date=$DateWithMsExampleString + | + |3 consumer_id (if null ignore) + | + |4 user_id (if null ignore) + | + |5 anon (if null ignore) only support two value : true (return where user_id is null.) or false (return where user_id is not null.) + | + |6 url (if null ignore), note: can not contain '&'. + | + |7 app_name (if null ignore) + | + |8 implemented_by_partial_function (if null ignore), + | + |9 implemented_in_version (if null ignore) + | + |10 verb (if null ignore) + | + |11 correlation_id (if null ignore) + | + |12 include_app_names (if null ignore).eg: &include_app_names=API-EXPLORER,API-Manager,SOFI,null + | + |13 include_url_patterns (if null ignore).you can design you own SQL LIKE pattern. eg: &include_url_patterns=%management/metrics%,%management/aggregate-metrics% + | + |14 include_implemented_by_partial_functions (if null ignore).eg: &include_implemented_by_partial_functions=getMetrics,getConnectorMetrics,getAggregateMetrics + | + |15 http_status_code (if null ignore) - Filter by HTTP status code. eg: http_status_code=200 returns only successful calls, http_status_code=500 returns server errors + | + |${userAuthenticationMessage(true)} + | + """.stripMargin, EmptyBody, aggregateMetricsJSONV300, List(AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), List(apiTagMetric, apiTagAggregateMetrics), @@ -504,9 +545,16 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(getAtm), "GET", "/banks/BANK_ID/atms/ATM_ID", "Get Bank ATM", - s"""Returns information about ATM for a single bank specified by BANK_ID and ATM_ID. - | - |${userAuthenticationMessage(!getAtmsIsPublic)}""", + s"""Returns information about ATM for a single bank specified by BANK_ID and ATM_ID including: + | + |* Address + |* Geo Location + |* License the data under this endpoint is released under + |* ATM Attributes + | + | + | + |${userAuthenticationMessage(!getAtmsIsPublic)}""".stripMargin, EmptyBody, atmJsonV510, List(AuthenticatedUserIsRequired, BankNotFound, AtmNotFoundByAtmId, UnknownError), List(apiTagATM), @@ -586,7 +634,7 @@ object Http4s510 { nameOf(createConsumer), "POST", "/management/consumers", - "Create Consumer", + "Create a Consumer", s"""Create a Consumer (Authenticated access). | |A Consumer represents an application that uses the Open Bank Project API. Each Consumer has: @@ -722,11 +770,19 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(getConsumers), "GET", "/management/consumers", "Get Consumers", - s"""Get all Consumers. - | - |${userAuthenticationMessage(true)}""", + s"""Get the all Consumers. + | + |${userAuthenticationMessage(true)} + | + |${urlParametersDocument(true, true)} + | + |""", EmptyBody, consumersJsonV510, - List(AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), List(apiTagConsumer), Some(List(canGetConsumers)), authMode = UserOrApplication, @@ -768,7 +824,30 @@ object Http4s510 { null, implementedInApiVersion, nameOf(getTransactionRequests), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transaction-requests", "Get Transaction Requests.", - "Returns transaction requests for account, with attribute filter support.", + """Returns transaction requests for account specified by ACCOUNT_ID at bank specified by BANK_ID. + | + |The VIEW_ID specified must be 'owner' and the user must have access to this view. + | + |Version 2.0.0 now returns charge information. + | + |Transaction Requests serve to initiate transactions that may or may not proceed. They contain information including: + | + |* Transaction Request Id + |* Type + |* Status (INITIATED, COMPLETED) + |* Challenge (in order to confirm the request) + |* From Bank / Account + |* Details including Currency, Value, Description and other initiation information specific to each type. (Could potentialy include a list of future transactions.) + |* Related Transactions + | + |PSD2 Context: PSD2 requires transparency of charges to the customer. + |This endpoint provides the charge that would be applied if the Transaction Request proceeds - and a record of that charge there after. + |The customer can proceed with the Transaction by answering the security challenge. + | + |We support query transaction request by attribute + |URL params example:/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transaction-requests?invoiceNumber=123&referenceNumber=456 + | + """.stripMargin, EmptyBody, transactionRequestWithChargeJSONs210, List(AuthenticatedUserIsRequired, BankNotFound, BankAccountNotFound, UserNoPermissionAccessView, ViewDoesNotPermitAccess, @@ -829,7 +908,7 @@ object Http4s510 { nameOf(getAllBankAccountBalances), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/balances", - "Get Account Balances", + "Get All Bank Account Balances", s"""Get all Balances for a Bank Account. | |${userAuthenticationMessage(true)} @@ -1031,9 +1110,10 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(deleteRegulatedEntity), "DELETE", "/regulated-entities/REGULATED_ENTITY_ID", "Delete Regulated Entity", - s"""Delete Regulated Entity specified by REGULATED_ENTITY_ID. - | - |${userAuthenticationMessage(true)}""", + s"""Delete Regulated Entity specified by REGULATED_ENTITY_ID + | + |${userAuthenticationMessage(true)} + |""".stripMargin, EmptyBody, EmptyBody, List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidConnectorResponse, UnknownError), List(apiTagDirectory, apiTagApi), @@ -1996,9 +2076,24 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(getWebUiProps), "GET", "/webui-props", "Get WebUiProps", - "Get all WebUiProps key/values. ?active=true also includes implicit (default) props.", + s""" + | + |Get the all WebUiProps key values, those props key with "webui_" can be stored in DB, this endpoint get all from DB. + | + |url query parameter: + |active: It must be a boolean string. and If active = true, it will show + | combination of explicit (inserted) + implicit (default) method_routings. + | + |eg: + |${getObpApiRoot}/v5.1.0/webui-props + |${getObpApiRoot}/v5.1.0/webui-props?active=true + | + |""", EmptyBody, - ListResult("webui-props", List(WebUiPropsCommons("webui_api_explorer_url", "https://apiexplorer.openbankproject.com", Some("web-ui-props-id")))), + ListResult( + "webui-props", + (List(WebUiPropsCommons("webui_api_explorer_url", "https://apiexplorer.openbankproject.com", Some("web-ui-props-id")))) + ), List(UserHasMissingRoles, UnknownError), List(apiTagWebUiProps), None, @@ -2060,7 +2155,10 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(deleteNonPersonalUserAttribute), "DELETE", "/users/USER_ID/non-personal/attributes/USER_ATTRIBUTE_ID", "Delete Non Personal User Attribute", - s"Delete the Non Personal User Attribute.\n\n${userAuthenticationMessage(true)}", + s"""Delete the Non Personal User Attribute specified by ENTITLEMENT_REQUEST_ID for a user specified by USER_ID + | + |${userAuthenticationMessage(true)} + |""".stripMargin, EmptyBody, EmptyBody, List(AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidConnectorResponse, UnknownError), List(apiTagUser), @@ -2081,7 +2179,10 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(getNonPersonalUserAttributes), "GET", "/users/USER_ID/non-personal/attributes", "Get Non Personal User Attributes", - s"Get Non Personal User Attributes for a user.\n\n${userAuthenticationMessage(true)}", + s"""Get Non Personal User Attribute for a user specified by USER_ID + | + |${userAuthenticationMessage(true)} + |""".stripMargin, EmptyBody, EmptyBody, List(AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidConnectorResponse, UnknownError), List(apiTagUser), @@ -2181,7 +2282,7 @@ object Http4s510 { nameOf(getUserByProviderAndUsername), "GET", "/users/provider/PROVIDER/username/USERNAME", - "Get User by Provider and Username", + "Get User by USERNAME", s"""Get user by PROVIDER and USERNAME | |Get a User by their authentication provider and username. @@ -2228,7 +2329,11 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(getUserLockStatus), "GET", "/users/PROVIDER/USERNAME/lock-status", "Get User Lock Status", - s"Get User Login Status.\n\n${userAuthenticationMessage(true)}", + s""" + |Get User Login Status. + |${userAuthenticationMessage(true)} + | + |""".stripMargin, EmptyBody, badLoginStatusJson, List(AuthenticatedUserIsRequired, UserNotFoundByProviderAndUsername, UserHasMissingRoles, UnknownError), List(apiTagUser), @@ -2253,7 +2358,14 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(unlockUserByProviderAndUsername), "PUT", "/users/PROVIDER/USERNAME/lock-status", "Unlock the user", - s"Unlock a User (e.g. after multiple failed login attempts).\n\n${userAuthenticationMessage(true)}", + s""" + |Unlock a User. + | + |(Perhaps the user was locked due to multiple failed login attempts) + | + |${userAuthenticationMessage(true)} + | + |""".stripMargin, EmptyBody, badLoginStatusJson, List(AuthenticatedUserIsRequired, UserNotFoundByProviderAndUsername, UserHasMissingRoles, UnknownError), List(apiTagUser), @@ -2517,7 +2629,12 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(getCustomersByLegalName), "POST", "/banks/BANK_ID/customers/legal-name", "Get Customers by Legal Name", - s"Gets the Customers specified by Legal Name.\n\n${userAuthenticationMessage(true)}", + s"""Gets the Customers specified by Legal Name. + | + | + |${userAuthenticationMessage(true)} + | + |""", postCustomerLegalNameJsonV510, customerJsonV310, List(AuthenticatedUserIsRequired, UserCustomerLinksNotFoundForUser, UnknownError), List(apiTagCustomer, apiTagKyc), @@ -3710,7 +3827,24 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(createCustomView), "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/target-views", "Create Custom View", - "Create a custom view on bank account. Name MUST start with `_`.", + s"""Create a custom view on bank account + | + | ${userAuthenticationMessage(true)} and the user needs to have access to the owner view. + | The 'alias' field in the JSON can take one of three values: + | + | * _public_: to use the public alias if there is one specified for the other account. + | * _private_: to use the private alias if there is one specified for the other account. + | + | * _''(empty string)_: to use no alias; the view shows the real name of the other account. + | + | The 'hide_metadata_if_alias_used' field in the JSON can take boolean values. If it is set to `true` and there is an alias on the other account then the other accounts' metadata (like more_info, url, image_url, open_corporates_url, etc.) will be hidden. Otherwise the metadata will be shown. + | + | The 'allowed_actions' field is a list containing the name of the actions allowed on this view, all the actions contained will be set to `true` on the view creation, the rest will be set to `false`. + | + | The 'metadata_view' field determines where metadata (comments, tags, images, where tags) for transactions are stored and retrieved. If set to another view's ID (e.g. 'owner'), metadata added through this view will be shared with all other views that also use the same metadata_view value. If left empty, metadata is stored under this view's own ID and is not shared with other views. + | + | You MUST use a leading _ (underscore) in the view name because other view names are reserved for OBP [system views](/index#group-View-System). + | """, createCustomViewJson, customViewJsonV510, List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, $UserNoPermissionAccessView, InvalidJsonFormat, UnknownError), List(apiTagView, apiTagAccount), @@ -3744,7 +3878,12 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(updateCustomView), "PUT", "/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID/target-views/TARGET_VIEW_ID", "Update Custom View", - "Update an existing custom view on a bank account.", + s"""Update an existing custom view on a bank account + | + |${userAuthenticationMessage(true)} and the user needs to have access to the owner view. + | + |The json sent is the same as during view creation (above), with one difference: the 'name' field + |of a view is not editable (it is only set when a view is created)""", updateCustomViewJson, customViewJsonV510, List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, $UserNoPermissionAccessView, InvalidJsonFormat, UnknownError), List(apiTagView, apiTagAccount), @@ -4000,7 +4139,7 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(addSystemViewPermission), "POST", "/system-views/VIEW_ID/permissions", "Add Permission to a System View", - "Add Permission to a System View.", + """Add Permission to a System View.""", createViewPermissionJson, entitlementJSON, List($AuthenticatedUserIsRequired, InvalidJsonFormat, IncorrectRoleName, EntitlementAlreadyExists, UnknownError), List(apiTagSystemView), @@ -4023,7 +4162,8 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(deleteSystemViewPermission), "DELETE", "/system-views/VIEW_ID/permissions/PERMISSION_NAME", "Delete Permission to a System View", - "Delete Permission to a System View.", + """Delete Permission to a System View + """.stripMargin, EmptyBody, EmptyBody, List(AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), List(apiTagSystemView), @@ -4509,6 +4649,64 @@ object Http4s510 { http4sPartialFunction = Some(selfRevokeConsent) ) + private val generalObpConsentText: String = + s""" + | + |An OBP Consent allows the holder of the Consent to call one or more endpoints. + | + |Consents must be created and authorisied using SCA (Strong Customer Authentication). + | + |That is, Consents can be created by an authorised User via the OBP REST API but they must be confirmed via an out of band (OOB) mechanism such as a code sent to a mobile phone. + | + |Each Consent has one of the following states: ${ConsentStatus.values.toList.sorted.mkString(", ")}. + | + |Each Consent is bound to a consumer i.e. you need to identify yourself over request header value Consumer-Key. + | + |Examples: + | + |For example: + |GET /obp/v4.0.0/users/current HTTP/1.1 + |Host: 127.0.0.1:8080 + |Consent-JWT: eyJhbGciOiJIUzI1NiJ9.eyJlbnRpdGxlbWVudHMiOlt7InJvbGVfbmFtZSI6IkNhbkdldEFueVVzZXIiLCJiYW5rX2lkIjoiIn + |1dLCJjcmVhdGVkQnlVc2VySWQiOiJhYjY1MzlhOS1iMTA1LTQ0ODktYTg4My0wYWQ4ZDZjNjE2NTciLCJzdWIiOiIzNDc1MDEzZi03YmY5LTQyNj + |EtOWUxYy0xZTdlNWZjZTJlN2UiLCJhdWQiOiI4MTVhMGVmMS00YjZhLTQyMDUtYjExMi1lNDVmZDZmNGQzYWQiLCJuYmYiOjE1ODA3NDE2NjcsIml + |zcyI6Imh0dHA6XC9cLzEyNy4wLjAuMTo4MDgwIiwiZXhwIjoxNTgwNzQ1MjY3LCJpYXQiOjE1ODA3NDE2NjcsImp0aSI6ImJkYzVjZTk5LTE2ZTY + |tNDM4Yi1hNjllLTU3MTAzN2RhMTg3OCIsInZpZXdzIjpbXX0.L3fEEEhdCVr3qnmyRKBBUaIQ7dk1VjiFaEBW8hUNjfg + | + |Consumer-Key: ejznk505d132ryomnhbx1qmtohurbsbb0kijajsk + |cache-control: no-cache + | + |Maximum time to live of the token is specified over props value consents.max_time_to_live. In case isn't defined default value is 3600 seconds. + | + |Example of POST JSON: + |{ + | "everything": false, + | "views": [ + | { + | "bank_id": "GENODEM1GLS", + | "account_id": "8ca8a7e4-6d02-40e3-a129-0b2bf89de9f0", + | "view_id": "${Constant.SYSTEM_OWNER_VIEW_ID}" + | } + | ], + | "entitlements": [ + | { + | "bank_id": "GENODEM1GLS", + | "role_name": "CanGetCustomersAtOneBank" + | } + | ], + | "consumer_id": "7uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", + | "email": "eveline@example.com", + | "valid_from": "2020-02-07T08:43:34Z", + | "time_to_live": 3600 + |} + |Please note that only optional fields are: consumer_id, valid_from and time_to_live. + |In case you omit they the default values are used: + |consumer_id = consumer of current user + |valid_from = current time + |time_to_live = consents.max_time_to_live + | + """.stripMargin + // ─── createConsent (IMPLICIT alias) — handles SCA: EMAIL/SMS/IMPLICIT ── val revokeMyConsent: HttpRoutes[IO] = HttpRoutes.of[IO] { @@ -4530,7 +4728,22 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(revokeMyConsent), "DELETE", "/my/consents/CONSENT_ID", "Revoke My Consent", - "Revoke a Consent for the current user, specified by CONSENT_ID.", + s""" + |Revoke Consent for current user specified by CONSENT_ID + | + |There are a few reasons you might need to revoke an application’s access to a user’s account: + | - The user explicitly wishes to revoke the application’s access + | - You as the service provider have determined an application is compromised or malicious, and want to disable it + | - etc. + | + |Please note that this endpoint only supports the case:: "The user explicitly wishes to revoke the application’s access" + | + |OBP as a resource server stores access tokens in a database, then it is relatively easy to revoke some token that belongs to a particular user. + |The status of the token is changed to "REVOKED" so the next time the revoked client makes a request, their token will fail to validate. + | + |${userAuthenticationMessage(true)} + | + """.stripMargin, EmptyBody, revokedConsentJsonV310, List($AuthenticatedUserIsRequired, UnknownError), List(apiTagConsent, apiTagPSD2AIS, apiTagPsd2), @@ -4651,7 +4864,59 @@ object Http4s510 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(createConsent), "POST", "/my/consents/IMPLICIT", "Create Consent (IMPLICIT)", - "Create a Consent in INITIATED state. SCA challenge is sent OOB based on SCA_METHOD.", + s""" + | + |This endpoint starts the process of creating a Consent. + | + |The Consent is created in an ${ConsentStatus.INITIATED} state. + | + |A One Time Password (OTP) (AKA security challenge) is sent Out of Band (OOB) to the User via the transport defined in SCA_METHOD + |SCA_METHOD is typically "SMS","EMAIL" or "IMPLICIT". "EMAIL" is used for testing purposes. OBP mapped mode "IMPLICIT" is "EMAIL". + |Other mode, bank can decide it in the connector method 'getConsentImplicitSCA'. + | + |When the Consent is created, OBP (or a backend system) stores the challenge so it can be checked later against the value supplied by the User with the Answer Consent Challenge endpoint. + | + |$generalObpConsentText + | + |${userAuthenticationMessage(true)} + | + |Example 1: + |{ + | "everything": true, + | "views": [], + | "entitlements": [], + | "consumer_id": "7uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", + |} + | + |Please note that consumer_id is optional field + |Example 2: + |{ + | "everything": true, + | "views": [], + | "entitlements": [], + |} + | + |Please note if everything=false you need to explicitly specify views and entitlements + |Example 3: + |{ + | "everything": false, + | "views": [ + | { + | "bank_id": "GENODEM1GLS", + | "account_id": "8ca8a7e4-6d02-40e3-a129-0b2bf89de9f0", + | "view_id": "${Constant.SYSTEM_OWNER_VIEW_ID}" + | } + | ], + | "entitlements": [ + | { + | "bank_id": "GENODEM1GLS", + | "role_name": "CanGetCustomersAtOneBank" + | } + | ], + | "consumer_id": "7uy8a7e4-6d02-40e3-a129-0b2bf89de8uh", + |} + | + |""", postConsentImplicitJsonV310, consentJsonV310, List(AuthenticatedUserIsRequired, BankNotFound, InvalidJsonFormat, ConsentAllowedScaMethods, RolesAllowedInConsent, ViewsAllowedInConsent, ConsumerNotFoundByConsumerId, ConsumerIsDisabled, diff --git a/obp-api/src/main/scala/code/api/v6_0_0/Http4s600.scala b/obp-api/src/main/scala/code/api/v6_0_0/Http4s600.scala index dc866c637f..5d00fb7c6e 100644 --- a/obp-api/src/main/scala/code/api/v6_0_0/Http4s600.scala +++ b/obp-api/src/main/scala/code/api/v6_0_0/Http4s600.scala @@ -6253,7 +6253,7 @@ object Http4s600 { """.stripMargin, EmptyBody, userJsonV300, - List($AuthenticatedUserIsRequired, UnknownError), + List(AuthenticatedUserIsRequired, UnknownError), apiTagUser :: Nil, None, http4sPartialFunction = Some(getCurrentUser) @@ -6286,7 +6286,7 @@ object Http4s600 { attributes = Some(List(BankAttributeBankResponseJsonV400("OVERDRAFT_LIMIT", "1000"))) ))), List(UnknownError), - apiTagBank :: Nil, + apiTagBank :: apiTagPSD2AIS :: apiTagPsd2 :: Nil, None, http4sPartialFunction = Some(getBanks) ) @@ -6315,8 +6315,8 @@ object Http4s600 { bank_routings = List(BankRoutingJsonV121("OBP", "gh.29.uk")), attributes = Some(List(BankAttributeBankResponseJsonV400("OVERDRAFT_LIMIT", "1000"))) ), - List($BankNotFound, UnknownError), - apiTagBank :: Nil, + List(UnknownError, BankNotFound), + apiTagBank :: apiTagPSD2AIS :: apiTagPsd2 :: Nil, None, http4sPartialFunction = Some(getBank) ) @@ -6344,8 +6344,12 @@ object Http4s600 { |""", EmptyBody, customerJSONsV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), - apiTagCustomer :: Nil, + List( + $AuthenticatedUserIsRequired, + UserCustomerLinksNotFoundForUser, + UnknownError + ), + List(apiTagCustomer, apiTagUser), Some(canGetCustomersAtOneBank :: Nil), http4sPartialFunction = Some(getCustomersAtOneBank) ) @@ -6366,7 +6370,12 @@ object Http4s600 { |""", EmptyBody, customerWithAttributesJsonV600, - List($AuthenticatedUserIsRequired, UserCustomerLinksNotFoundForUser, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UserCustomerLinksNotFoundForUser, + UnknownError + ), apiTagCustomer :: Nil, Some(canGetCustomersAtOneBank :: Nil), http4sPartialFunction = Some(getCustomerByCustomerId) @@ -6408,7 +6417,7 @@ object Http4s600 { views_basic = List("owner") ), List($AuthenticatedUserIsRequired, $BankAccountNotFound, UnknownError), - apiTagAccount :: Nil, + apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil, None, http4sPartialFunction = Some(getCoreAccountByIdV600) ) @@ -6497,7 +6506,7 @@ object Http4s600 { nameOf(getBankLevelDynamicEntities), "GET", "/management/banks/BANK_ID/dynamic-entities", - "Get Bank-Level Dynamic Entities", + "Get Bank Level Dynamic Entities", s"""Get all Bank Level Dynamic Entities for one bank with record counts. | |Each dynamic entity in the response includes a `record_count` field showing how many data records exist for that entity. @@ -6521,7 +6530,12 @@ object Http4s600 { ) ) ), - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $BankNotFound, + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagManageDynamicEntity :: apiTagApi :: Nil, Some(canGetBankLevelDynamicEntities :: canGetAnyBankLevelDynamicEntities :: Nil), http4sPartialFunction = Some(getBankLevelDynamicEntities) @@ -6552,8 +6566,13 @@ object Http4s600 { |""".stripMargin, EmptyBody, consumerJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagConsumer :: apiTagApi :: Nil, + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + ConsumerNotFoundByConsumerId, + UnknownError + ), + List(apiTagConsumer), Some(canGetConsumers :: Nil), http4sPartialFunction = Some(getConsumer) ) @@ -6581,8 +6600,12 @@ object Http4s600 { |""", EmptyBody, customerJSONsV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagCustomer :: Nil, + List( + $AuthenticatedUserIsRequired, + UserCustomerLinksNotFoundForUser, + UnknownError + ), + List(apiTagCustomer, apiTagUser), Some(canGetCustomersAtAllBanks :: Nil), http4sPartialFunction = Some(getCustomersAtAllBanks) ) @@ -6681,8 +6704,12 @@ object Http4s600 { |""", postCustomerNumberJsonV310, customerWithAttributesJsonV600, - List($AuthenticatedUserIsRequired, UserCustomerLinksNotFoundForUser, $BankNotFound, UserHasMissingRoles, UnknownError), - apiTagCustomer :: Nil, + List( + $AuthenticatedUserIsRequired, + UserCustomerLinksNotFoundForUser, + UnknownError + ), + List(apiTagCustomer, apiTagKyc), Some(canGetCustomersAtOneBank :: Nil), http4sPartialFunction = Some(getCustomerByCustomerNumber) ) @@ -6705,8 +6732,12 @@ object Http4s600 { |""", PostCustomerLegalNameJsonV510(legal_name = "John Smith"), customerJSONsV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), - apiTagCustomer :: Nil, + List( + $AuthenticatedUserIsRequired, + UserCustomerLinksNotFoundForUser, + UnknownError + ), + List(apiTagCustomer, apiTagKyc), Some(canGetCustomersAtOneBank :: Nil), http4sPartialFunction = Some(getCustomersByLegalName) ) @@ -6832,7 +6863,13 @@ object Http4s600 { personal_requires_role = false, schema = net.liftweb.json.parse("""{"description": "User preferences", "required": ["theme"], "properties": {"theme": {"type": "string", "minLength": 1, "maxLength": 20, "example": "dark", "description": "The UI theme preference"}, "language": {"type": "string", "minLength": 2, "maxLength": 5, "example": "en", "description": "ISO language code"}}}""").asInstanceOf[net.liftweb.json.JsonAST.JObject] ), - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + $BankNotFound, + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), apiTagManageDynamicEntity :: apiTagApi :: Nil, Some(canCreateBankLevelDynamicEntity :: Nil), authMode = code.api.util.APIUtil.UserOrApplication, @@ -6951,7 +6988,13 @@ object Http4s600 { has_public_access = false, schema = net.liftweb.json.parse("""{"description": "User preferences updated", "required": ["theme"], "properties": {"theme": {"type": "string", "minLength": 1, "maxLength": 20, "example": "dark", "description": "The UI theme preference"}, "language": {"type": "string", "minLength": 2, "maxLength": 5, "example": "en", "description": "ISO language code"}, "notifications_enabled": {"type": "boolean", "example": "true", "description": "Whether to send notifications"}}}""").asInstanceOf[net.liftweb.json.JsonAST.JObject] ), - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + $BankNotFound, + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), apiTagManageDynamicEntity :: apiTagApi :: Nil, Some(canUpdateBankLevelDynamicEntity :: Nil), http4sPartialFunction = Some(updateBankLevelDynamicEntity) @@ -7010,7 +7053,11 @@ object Http4s600 { has_public_access = false, schema = net.liftweb.json.parse("""{"description": "User preferences updated", "required": ["theme"], "properties": {"theme": {"type": "string", "minLength": 1, "maxLength": 20, "example": "dark", "description": "The UI theme preference"}, "language": {"type": "string", "minLength": 2, "maxLength": 5, "example": "en", "description": "ISO language code"}, "notifications_enabled": {"type": "boolean", "example": "true", "description": "Whether to send notifications"}}}""").asInstanceOf[net.liftweb.json.JsonAST.JObject] ), - List($AuthenticatedUserIsRequired, DynamicEntityNotFoundByDynamicEntityId, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + UnknownError + ), apiTagManageDynamicEntity :: apiTagApi :: Nil, None, http4sPartialFunction = Some(updateMyDynamicEntity) @@ -7069,8 +7116,15 @@ object Http4s600 { "can_add_comment" ) ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, SystemViewCannotBePublicError, UnknownError), - apiTagView :: Nil, + List( + InvalidJsonFormat, + AuthenticatedUserIsRequired, + UserHasMissingRoles, + SystemViewNotFound, + SystemViewCannotBePublicError, + UnknownError + ), + List(apiTagSystemView, apiTagView), None, http4sPartialFunction = Some(updateSystemView) ) @@ -7176,7 +7230,11 @@ object Http4s600 { """.stripMargin, EmptyBody, metricsJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagMetric :: apiTagApi :: Nil, Some(canReadMetrics :: Nil), http4sPartialFunction = Some(getMetrics) @@ -7272,8 +7330,12 @@ object Http4s600 { """.stripMargin, EmptyBody, aggregateMetricsJSONV300, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagMetric :: apiTagApi :: Nil, + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), + List(apiTagMetric, apiTagAggregateMetrics), Some(canReadAggregateMetrics :: Nil), http4sPartialFunction = Some(getAggregateMetrics) ) @@ -7338,7 +7400,13 @@ object Http4s600 { TopApiJsonV600(500, "getBank", "v4.0.0", "OBPv4.0.0-getBank"), TopApiJsonV600(250, "getAccountList", "v1.3", "BGv1.3-getAccountList") )), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidFilterParameterFormat, + GetTopApisError, + UnknownError + ), apiTagMetric :: apiTagApi :: Nil, Some(canReadMetrics :: Nil), http4sPartialFunction = Some(getTopAPIs) @@ -7398,8 +7466,10 @@ object Http4s600 { "webui_props", (List(WebUiPropsCommons("webui_api_explorer_url", "https://apiexplorer.openbankproject.com", Some("web-ui-props-id"), Some("database")))) ), - List(InvalidFilterParameterFormat, UnknownError), - apiTagWebUiProps :: apiTagApi :: Nil, + List( + UnknownError + ), + List(apiTagWebUiProps), None, http4sPartialFunction = Some(getWebUiProps) ) @@ -7431,7 +7501,7 @@ object Http4s600 { views_available = List(BasicViewJson("owner", "Owner", false)) ))), List($AuthenticatedUserIsRequired, $BankNotFound, UnknownError), - apiTagAccount :: Nil, + List(apiTagAccount, apiTagPrivateData, apiTagPublicData), None, http4sPartialFunction = Some(getAccountsAtBank) ) @@ -7483,8 +7553,17 @@ object Http4s600 { metadata = transactionMetadataJSON, transaction_attributes = Nil ))), - List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, $UserNoPermissionAccessView, UnknownError), - apiTagTransaction :: Nil, + List( + FilterSortDirectionError, + FilterOffersetError, + FilterLimitError, + FilterDateFormatError, + AuthenticatedUserIsRequired, + BankAccountNotFound, + ViewNotFound, + UnknownError + ), + List(apiTagTransaction, apiTagAccount), None, http4sPartialFunction = Some(getTransactionsForBankAccount) ) @@ -7502,7 +7581,10 @@ object Http4s600 { |${userAuthenticationMessage(!getProductsIsPublic)}""".stripMargin, EmptyBody, productsJsonV600, - List($BankNotFound, UnknownError), + List( + BankNotFound, + UnknownError + ), apiTagProduct :: Nil, None, http4sPartialFunction = Some(getProductsV600) @@ -7516,7 +7598,7 @@ object Http4s600 { nameOf(getUsers), "GET", "/users", - "Get Users", + "Get all Users", s"""Get all users, optionally filtered. | |All query parameters are optional and may be combined. @@ -7544,7 +7626,14 @@ object Http4s600 { """.stripMargin, EmptyBody, usersInfoJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + FilterSortByError, + FilterSortByNotAllowedForEndpoint, + FilterSortDirectionError, + UnknownError + ), apiTagUser :: Nil, Some(canGetAnyUser :: Nil), http4sPartialFunction = Some(getUsers) @@ -7570,7 +7659,12 @@ object Http4s600 { |""", postBankJson600, bankJson600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, bankIdAlreadyExists, UnknownError), + List( + InvalidJsonFormat, + $AuthenticatedUserIsRequired, + InsufficientAuthorisationToCreateBank, + UnknownError + ), apiTagBank :: Nil, Some(canCreateBank :: Nil), http4sPartialFunction = Some(createBank) @@ -7628,8 +7722,21 @@ object Http4s600 { |""", postCustomerJsonV600, customerJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, InvalidJsonFormat, UserHasMissingRoles, UnknownError), - apiTagCustomer :: Nil, + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + InvalidJsonFormat, + InvalidJsonContent, + InvalidDateFormat, + InvalidCustomerType, + ParentCustomerNotFound, + CustomerNumberAlreadyExists, + UserNotFoundById, + CustomerAlreadyExistsForUser, + CreateConsumerError, + UnknownError + ), + List(apiTagCustomer, apiTagPerson), Some(canCreateCustomer :: Nil), http4sPartialFunction = Some(createCustomer) ) @@ -7639,7 +7746,7 @@ object Http4s600 { nameOf(createUser), "POST", "/users", - "Create User", + "Create User (v6.0.0)", s"""Creates OBP user. | No authorisation required. | @@ -7665,8 +7772,8 @@ object Http4s600 { |""", createUserJsonV600, userJsonV200, - List(InvalidJsonFormat, InvalidStrongPasswordFormat, DuplicateUsername, UnknownError), - apiTagUser :: Nil, + List(InvalidJsonFormat, InvalidStrongPasswordFormat, DuplicateUsername, ExternalUserCheckFailed, "Error occurred during user creation.", UnknownError), + List(apiTagUser, apiTagOnboarding), None, http4sPartialFunction = Some(createUser) ) @@ -7676,7 +7783,7 @@ object Http4s600 { nameOf(resetPasswordUrl), "POST", "/management/user/reset-password-url", - "Send Password Reset URL", + "Create Password Reset URL and Send Email", s"""Create a password reset URL for a user and automatically send it via email. | |Authentication is Required. @@ -7797,7 +7904,11 @@ object Http4s600 { environment = "dev", global_prefix = "obp_dev_" ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagCache :: apiTagSystem :: apiTagApi :: Nil, Some(canGetCacheConfig :: Nil), http4sPartialFunction = Some(getCacheConfig) @@ -7857,7 +7968,11 @@ object Http4s600 { total_keys = 170, redis_available = true ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagCache :: apiTagSystem :: apiTagApi :: Nil, Some(canGetCacheInfo :: Nil), http4sPartialFunction = Some(getCacheInfo) @@ -7952,7 +8067,11 @@ object Http4s600 { max_lifetime_ms = 1800000, keepalive_time_ms = 0 ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagSystem :: apiTagApi :: Nil, Some(canGetDatabasePoolInfo :: Nil), http4sPartialFunction = Some(getDatabasePoolInfo) @@ -8015,7 +8134,11 @@ object Http4s600 { response_time_ms = 45, error_message = None ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagConnector :: apiTagSystem :: apiTagApi :: Nil, Some(canGetConnectorHealth :: Nil), http4sPartialFunction = Some(getStoredProcedureConnectorHealth) @@ -8101,8 +8224,11 @@ object Http4s600 { |""", EmptyBody, customerJSONsV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), - apiTagCustomer :: Nil, + List( + $AuthenticatedUserIsRequired, + UnknownError + ), + List(apiTagCorporateCustomer, apiTagCustomer), Some(canGetCustomersAtOneBank :: Nil), http4sPartialFunction = Some(getCorporateCustomersAtOneBank) ) @@ -8112,7 +8238,7 @@ object Http4s600 { nameOf(getCorporateCustomerByCustomerId), "GET", "/banks/BANK_ID/corporate-customers/CUSTOMER_ID", - "Get Corporate Customer by Id", + "Get Corporate Customer by CUSTOMER_ID", s"""Gets the Corporate Customer specified by CUSTOMER_ID. | |Returns 404 if the customer exists but is not of type CORPORATE or SUBSIDIARY. @@ -8125,8 +8251,12 @@ object Http4s600 { |""", EmptyBody, customerWithAttributesJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, CustomerTypeMismatch, UnknownError), - apiTagCustomer :: Nil, + List( + $AuthenticatedUserIsRequired, + CustomerTypeMismatch, + UnknownError + ), + List(apiTagCorporateCustomer, apiTagCustomer), Some(canGetCustomersAtOneBank :: Nil), http4sPartialFunction = Some(getCorporateCustomerByCustomerId) ) @@ -8136,7 +8266,7 @@ object Http4s600 { nameOf(getCorporateCustomerSubsidiaries), "GET", "/banks/BANK_ID/corporate-customers/CUSTOMER_ID/subsidiaries", - "Get Subsidiaries", + "Get Corporate Customer Subsidiaries", s"""Get the subsidiary customers of a corporate customer. | |Returns a list of customers whose parent_customer_id matches the specified CUSTOMER_ID. @@ -8146,8 +8276,14 @@ object Http4s600 { |""", EmptyBody, customerJSONsV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, CustomerTypeMismatch, UnknownError), - apiTagCustomer :: Nil, + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + CustomerNotFoundByCustomerId, + CustomerTypeMismatch, + UnknownError + ), + List(apiTagCorporateCustomer, apiTagCustomer), Some(canGetCustomersAtOneBank :: Nil), http4sPartialFunction = Some(getCorporateCustomerSubsidiaries) ) @@ -8174,8 +8310,11 @@ object Http4s600 { |""", EmptyBody, customerJSONsV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), - apiTagCustomer :: Nil, + List( + $AuthenticatedUserIsRequired, + UnknownError + ), + List(apiTagRetailCustomer, apiTagCustomer), Some(canGetCustomersAtOneBank :: Nil), http4sPartialFunction = Some(getRetailCustomersAtOneBank) ) @@ -8185,7 +8324,7 @@ object Http4s600 { nameOf(getRetailCustomerByCustomerId), "GET", "/banks/BANK_ID/retail-customers/CUSTOMER_ID", - "Get Retail Customer by Id", + "Get Retail Customer by CUSTOMER_ID", s"""Gets the Retail Customer specified by CUSTOMER_ID. | |Returns 404 if the customer exists but is not of type INDIVIDUAL. @@ -8198,8 +8337,12 @@ object Http4s600 { |""", EmptyBody, customerWithAttributesJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, CustomerTypeMismatch, UnknownError), - apiTagCustomer :: Nil, + List( + $AuthenticatedUserIsRequired, + CustomerTypeMismatch, + UnknownError + ), + List(apiTagRetailCustomer, apiTagCustomer), Some(canGetCustomersAtOneBank :: Nil), http4sPartialFunction = Some(getRetailCustomerByCustomerId) ) @@ -8219,7 +8362,12 @@ object Http4s600 { |""", EmptyBody, customerJSONsV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + CustomerNotFoundByCustomerId, + UnknownError + ), apiTagCustomer :: Nil, Some(canGetCustomersAtOneBank :: Nil), http4sPartialFunction = Some(getCustomerChildren) @@ -8230,7 +8378,7 @@ object Http4s600 { nameOf(getCustomerLinksByCustomerId), "GET", "/banks/BANK_ID/customers/CUSTOMER_ID/customer-links", - "Get Customer Links by Customer Id", + "Get Customer Links by CUSTOMER_ID", s"""Get Customer Links by CUSTOMER_ID. | |Authentication is Required @@ -8238,7 +8386,13 @@ object Http4s600 { |""", EmptyBody, customerLinksJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + CustomerNotFoundByCustomerId, + UserHasMissingRoles, + UnknownError + ), apiTagCustomer :: Nil, Some(canGetCustomerLinks :: Nil), http4sPartialFunction = Some(getCustomerLinksByCustomerId) @@ -8282,8 +8436,12 @@ object Http4s600 { allowed_actions = List("can_see_transaction_amount", "can_see_bank_account_balance") ) )), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagView :: Nil, + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), + List(apiTagSystemView, apiTagView), Some(canGetSystemViews :: Nil), http4sPartialFunction = Some(getSystemViews) ) @@ -8293,7 +8451,7 @@ object Http4s600 { nameOf(getSystemViewById), "GET", "/management/system-views/SYS_VIEW_ID", - "Get System View by Id", + "Get System View", s"""Get a single system view by its ID. | |System views are predefined views that apply to all accounts, such as: @@ -8329,8 +8487,13 @@ object Http4s600 { "can_create_custom_view" ) ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagView :: Nil, + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + SystemViewNotFound, + UnknownError + ), + List(apiTagSystemView, apiTagView), Some(canGetSystemViews :: Nil), http4sPartialFunction = Some(getSystemViewById) ) @@ -8359,7 +8522,11 @@ object Http4s600 { ) ) ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagABAC :: Nil, Some(canGetAbacRule :: Nil), http4sPartialFunction = Some(getAbacPolicies) @@ -8406,8 +8573,10 @@ object Http4s600 { ) ) ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagMetric :: Nil, + List( + UnknownError + ), + List(apiTagMetric, apiTagApi), Some(canReadMetrics :: Nil), http4sPartialFunction = Some(getConnectorCallCounts) ) @@ -8451,8 +8620,11 @@ object Http4s600 { |""".stripMargin, EmptyBody, connectorTracesJsonV600, - List($AuthenticatedUserIsRequired, UnknownError), - apiTagMetric :: Nil, + List( + InvalidDateFormat, + UnknownError + ), + List(apiTagMetric, apiTagApi), None, http4sPartialFunction = Some(getConnectorTraces) ) @@ -8535,7 +8707,7 @@ object Http4s600 { ) ), List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagManageDynamicEntity :: Nil, + List(apiTagDynamicEntity, apiTagApi), Some(canGetDynamicEntityDiagnostics :: Nil), http4sPartialFunction = Some(getDynamicEntityDiagnostics) ) @@ -8578,7 +8750,7 @@ object Http4s600 { total_records_deleted = 42 ), List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagManageDynamicEntity :: Nil, + List(apiTagDynamicEntity, apiTagApi), Some(canCleanupOrphanedDynamicEntityRecords :: Nil), http4sPartialFunction = Some(cleanupOrphanedDynamicEntityRecords) ) @@ -8647,7 +8819,13 @@ object Http4s600 { |""", WebUiPropsPutJsonV600("https://apiexplorer.openbankproject.com"), WebUiPropsCommons("webui_api_explorer_url", "https://apiexplorer.openbankproject.com", Some("some-web-ui-props-id")), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidWebUiProps, InvalidJsonFormat, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + InvalidWebUiProps, + UnknownError + ), apiTagWebUiProps :: Nil, Some(canCreateWebUiProps :: Nil), http4sPartialFunction = Some(createOrUpdateWebUiProps) @@ -8674,7 +8852,12 @@ object Http4s600 { |""", EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidWebUiProps, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidWebUiProps, + UnknownError + ), apiTagWebUiProps :: Nil, Some(canDeleteWebUiProps :: Nil), http4sPartialFunction = Some(deleteWebUiProps) @@ -8720,8 +8903,15 @@ object Http4s600 { |""".stripMargin, createViewJsonV300, viewJsonV300, - List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, UserHasMissingRoles, InvalidJsonFormat, InvalidCustomViewFormat, UnknownError), - apiTagView :: Nil, + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + InvalidCustomViewFormat, + BankAccountNotFound, + UnknownError + ), + List(apiTagView, apiTagAccount), Some(canCreateCustomView :: Nil), http4sPartialFunction = Some(createCustomViewManagement) ) @@ -8737,7 +8927,11 @@ object Http4s600 { |${userAuthenticationMessage(!getProductsIsPublic)}""".stripMargin, EmptyBody, productTagsJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UnknownError), + List( + BankNotFound, + ProductNotFoundByProductCode, + UnknownError + ), apiTagProduct :: Nil, None, http4sPartialFunction = Some(getProductTagsV600) @@ -8754,7 +8948,14 @@ object Http4s600 { |Authentication is Required.""".stripMargin, productTagsJsonV600, productTagsJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, InvalidJsonFormat, UpdateProductError, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + BankNotFound, + ProductNotFoundByProductCode, + UnknownError + ), apiTagProduct :: Nil, None, http4sPartialFunction = Some(updateProductTagsV600) @@ -8823,15 +9024,21 @@ object Http4s600 { nameOf(getUserAttributeById), "GET", "/users/USER_ID/attributes/USER_ATTRIBUTE_ID", - "Get User Attribute by Id", + "Get User Attribute By Id", s"""Get a User Attribute by USER_ATTRIBUTE_ID for the user specified by USER_ID. | |${userAuthenticationMessage(true)} |""".stripMargin, EmptyBody, userAttributeResponseJsonV510, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UserAttributeNotFound, UnknownError), - apiTagUser :: apiTagUserAttribute :: Nil, + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UserNotFoundByUserId, + UserAttributeNotFound, + UnknownError + ), + List(apiTagUser, apiTagUserAttribute, apiTagAttribute), Some(canGetUserAttributes :: Nil), http4sPartialFunction = Some(getUserAttributeById) ) @@ -8859,8 +9066,14 @@ object Http4s600 { value = "premium" ), userAttributeResponseJsonV510, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), - apiTagUser :: apiTagUserAttribute :: Nil, + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UserNotFoundByUserId, + InvalidJsonFormat, + UnknownError + ), + List(apiTagUser, apiTagUserAttribute, apiTagAttribute), Some(canCreateUserAttribute :: Nil), http4sPartialFunction = Some(createUserAttribute) ) @@ -8881,8 +9094,15 @@ object Http4s600 { value = "enterprise" ), userAttributeResponseJsonV510, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UserAttributeNotFound, UnknownError), - apiTagUser :: apiTagUserAttribute :: Nil, + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UserNotFoundByUserId, + UserAttributeNotFound, + InvalidJsonFormat, + UnknownError + ), + List(apiTagUser, apiTagUserAttribute, apiTagAttribute), Some(canUpdateUserAttribute :: Nil), http4sPartialFunction = Some(updateUserAttribute) ) @@ -8899,8 +9119,14 @@ object Http4s600 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UserAttributeNotFound, UnknownError), - apiTagUser :: apiTagUserAttribute :: Nil, + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UserNotFoundByUserId, + UserAttributeNotFound, + UnknownError + ), + List(apiTagUser, apiTagUserAttribute, apiTagAttribute), Some(canDeleteUserAttribute :: Nil), http4sPartialFunction = Some(deleteUserAttribute) ) @@ -8910,7 +9136,7 @@ object Http4s600 { nameOf(addUserToGroup), "POST", "/users/USER_ID/group-entitlements", - "Add User to Group", + "Grant User Membership to Group Entitlements", s"""Grant the User Group Entitlements. | |This endpoint creates entitlements for every Role in the Group. If the user @@ -8946,8 +9172,13 @@ object Http4s600 { entitlements_created = List("CanGetCustomer", "CanGetAccount"), entitlements_skipped = List("CanCreateTransaction") ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), - apiTagGroup :: apiTagUser :: Nil, + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), + List(apiTagGroup, apiTagUser, apiTagEntitlement), None, http4sPartialFunction = Some(addUserToGroup) ) @@ -8973,8 +9204,12 @@ object Http4s600 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagGroup :: apiTagUser :: Nil, + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), + List(apiTagGroup, apiTagUser, apiTagEntitlement), None, http4sPartialFunction = Some(removeUserFromGroup) ) @@ -8996,8 +9231,13 @@ object Http4s600 { """.stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagEntitlement :: Nil, + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + EntitlementCannotBeDeleted, + UnknownError + ), + List(apiTagRole, apiTagUser, apiTagEntitlement), Some(canDeleteEntitlementAtAnyBank :: Nil), http4sPartialFunction = Some(deleteEntitlement) ) @@ -9042,7 +9282,7 @@ object Http4s600 { ) ), List($AuthenticatedUserIsRequired, UnknownError), - apiTagManageDynamicEntity :: apiTagApi :: Nil, + List(apiTagDynamicEntity, apiTagPersonalDynamicEntity, apiTagApi), None, http4sPartialFunction = Some(getAvailablePersonalDynamicEntities) ) @@ -9111,7 +9351,7 @@ object Http4s600 { ) ), List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagManageDynamicEntity :: Nil, + List(apiTagDynamicEntity, apiTagApi), Some(canGetDynamicEntityReferenceTypes :: Nil), http4sPartialFunction = Some(getReferenceTypes) ) @@ -9121,7 +9361,7 @@ object Http4s600 { nameOf(joinSystemChatRoom), "POST", "/chat-room-participants", - "Join Chat Room", + "Join System Chat Room", s"""Join a system-level chat room using a joining key (passed as joining_key in the JSON body). |The user is added as a participant with no special permissions. | @@ -9164,8 +9404,8 @@ object Http4s600 { """.stripMargin, counterpartyAttributeRequestJsonV600, counterpartyAttributeResponseJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), - apiTagCounterparty :: Nil, + List($AuthenticatedUserIsRequired, InvalidJsonFormat, UnknownError), + List(apiTagCounterpartyAttribute, apiTagApi), Some(canCreateCounterpartyAttribute :: Nil), http4sPartialFunction = Some(createCounterpartyAttribute) ) @@ -9184,8 +9424,8 @@ object Http4s600 { """.stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagCounterparty :: Nil, + List($AuthenticatedUserIsRequired, UnknownError), + List(apiTagCounterpartyAttribute, apiTagApi), Some(canDeleteCounterpartyAttribute :: Nil), http4sPartialFunction = Some(deleteCounterpartyAttribute) ) @@ -9195,7 +9435,7 @@ object Http4s600 { nameOf(getCounterpartyAttributeById), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/counterparties/COUNTERPARTY_ID_PARAM/attributes/COUNTERPARTY_ATTRIBUTE_ID", - "Get Counterparty Attribute By Id", + "Get Counterparty Attribute By ID", s""" | Get a specific Counterparty Attribute by its COUNTERPARTY_ATTRIBUTE_ID. | @@ -9204,8 +9444,8 @@ object Http4s600 { """.stripMargin, EmptyBody, counterpartyAttributeResponseJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagCounterparty :: Nil, + List($AuthenticatedUserIsRequired, UnknownError), + List(apiTagCounterpartyAttribute, apiTagApi), Some(canGetCounterpartyAttribute :: Nil), http4sPartialFunction = Some(getCounterpartyAttributeById) ) @@ -9224,8 +9464,8 @@ object Http4s600 { """.stripMargin, EmptyBody, counterpartyAttributesJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagCounterparty :: Nil, + List($AuthenticatedUserIsRequired, UnknownError), + List(apiTagCounterpartyAttribute, apiTagApi), Some(canGetCounterpartyAttributes :: Nil), http4sPartialFunction = Some(getAllCounterpartyAttributes) ) @@ -9244,8 +9484,8 @@ object Http4s600 { """.stripMargin, counterpartyAttributeRequestJsonV600, counterpartyAttributeResponseJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), - apiTagCounterparty :: Nil, + List($AuthenticatedUserIsRequired, InvalidJsonFormat, UnknownError), + List(apiTagCounterpartyAttribute, apiTagApi), Some(canUpdateCounterpartyAttribute :: Nil), http4sPartialFunction = Some(updateCounterpartyAttribute) ) @@ -9273,8 +9513,11 @@ object Http4s600 { account_access_id = ExampleValue.uuidExample.value, abac_rule_id = "" ), - List($AuthenticatedUserIsRequired, $BankNotFound, ViewNotFound, UnknownError), - apiTagAccount :: Nil, + List( + $BankNotFound, + UnknownError + ), + List(apiTagView, apiTagAccount), None, http4sPartialFunction = Some(hasAccountAccess) ) @@ -9311,7 +9554,7 @@ object Http4s600 { )) ), List($AuthenticatedUserIsRequired, UnknownError), - apiTagAccountAccess :: Nil, + List(apiTagAccountAccessRequest), None, http4sPartialFunction = Some(getMyAccountAccessRequests) ) @@ -9358,7 +9601,10 @@ object Http4s600 { |""", EmptyBody, WebUiPropsCommons("webui_api_explorer_url", "https://apiexplorer.openbankproject.com", Some("web-ui-props-id"), Some("config")), - List(WebUiPropsNotFoundByName, InvalidFilterParameterFormat, UnknownError), + List( + WebUiPropsNotFoundByName, + UnknownError + ), apiTagWebUiProps :: Nil, None, http4sPartialFunction = Some(getWebUiProp) @@ -9475,7 +9721,11 @@ object Http4s600 { ViewPermissionJsonV600("can_create_custom_view", "View") ) ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagSystemView :: apiTagView :: Nil, Some(canGetViewPermissionsAtAllBanks :: Nil), http4sPartialFunction = Some(getViewPermissions) @@ -9494,7 +9744,7 @@ object Http4s600 { |${userAuthenticationMessage(!getApiProductsIsPublic)}""".stripMargin, EmptyBody, apiProductsJsonV600, - List($AuthenticatedUserIsRequired, UnknownError), + List(UnknownError), apiTagApi :: apiTagApiProduct :: Nil, None, http4sPartialFunction = Some(getAllApiProductsV600) @@ -9513,7 +9763,7 @@ object Http4s600 { |${userAuthenticationMessage(!getProductsIsPublic)}""".stripMargin, EmptyBody, productsJsonV600, - List($AuthenticatedUserIsRequired, UnknownError), + List(UnknownError), apiTagProduct :: Nil, None, http4sPartialFunction = Some(getAllProductsV600) @@ -9551,7 +9801,7 @@ object Http4s600 { )) ), List($AuthenticatedUserIsRequired, UserHasMissingRoles, $BankNotFound, $BankAccountNotFound, UnknownError), - apiTagAccountAccess :: Nil, + List(apiTagAccountAccessRequest), Some(canGetAccountAccessRequestsAtOneBank :: canGetAccountAccessRequestsAtAnyBank :: Nil), http4sPartialFunction = Some(getAccountAccessRequestsForAccount) ) @@ -9584,7 +9834,7 @@ object Http4s600 { updated = APIUtil.DateWithMsExampleObject ), List($AuthenticatedUserIsRequired, UserHasMissingRoles, $BankNotFound, $BankAccountNotFound, AccountAccessRequestNotFound, UnknownError), - apiTagAccountAccess :: Nil, + List(apiTagAccountAccessRequest), Some(canGetAccountAccessRequestsAtOneBank :: canGetAccountAccessRequestsAtAnyBank :: Nil), http4sPartialFunction = Some(getAccountAccessRequestById) ) @@ -9652,7 +9902,7 @@ object Http4s600 { List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, $BankNotFound, $BankAccountNotFound, BusinessJustificationRequired, AccountAccessRequestAlreadyExists, AccountAccessRequestCannotBeCreated, UnknownError), - apiTagAccountAccess :: Nil, + List(apiTagAccountAccessRequest), Some(canCreateAccountAccessRequestAtOneBank :: canCreateAccountAccessRequestAtAnyBank :: Nil), http4sPartialFunction = Some(createAccountAccessRequest) ) @@ -9696,7 +9946,7 @@ object Http4s600 { $BankNotFound, $BankAccountNotFound, AccountAccessRequestNotFound, AccountAccessRequestStatusNotInitiated, MakerCheckerSameUser, AccountAccessRequestCannotBeUpdated, UnknownError), - apiTagAccountAccess :: Nil, + List(apiTagAccountAccessRequest), Some(canUpdateAccountAccessRequestAtOneBank :: canUpdateAccountAccessRequestAtAnyBank :: Nil), http4sPartialFunction = Some(approveAccountAccessRequest) ) @@ -9740,7 +9990,7 @@ object Http4s600 { $BankNotFound, $BankAccountNotFound, AccountAccessRequestNotFound, AccountAccessRequestStatusNotInitiated, MakerCheckerSameUser, CheckerCommentRequiredForRejection, AccountAccessRequestCannotBeUpdated, UnknownError), - apiTagAccountAccess :: Nil, + List(apiTagAccountAccessRequest), Some(canUpdateAccountAccessRequestAtOneBank :: canUpdateAccountAccessRequestAtAnyBank :: Nil), http4sPartialFunction = Some(rejectAccountAccessRequest) ) @@ -9837,7 +10087,12 @@ object Http4s600 { |""".stripMargin, postSignalMessageJsonV600, signalMessagePublishedJsonV600, - List($AuthenticatedUserIsRequired, InvalidSignalChannelName, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + InvalidSignalChannelName, + UnknownError + ), apiTagAiAgent :: apiTagSignal :: apiTagSignalling :: apiTagChannel :: Nil, None, http4sPartialFunction = Some(publishSignalMessage) @@ -9924,7 +10179,10 @@ object Http4s600 { created_at = new java.util.Date(), updated_at = new java.util.Date() ))), - List($AuthenticatedUserIsRequired, $BankNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(getBankChatRooms) @@ -9996,7 +10254,12 @@ object Http4s600 { created_at = new java.util.Date(), updated_at = new java.util.Date() ), - List($AuthenticatedUserIsRequired, $BankNotFound, ChatRoomNotFound, NotChatRoomParticipant, UnknownError), + List( + $AuthenticatedUserIsRequired, + ChatRoomNotFound, + NotChatRoomParticipant, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(getBankChatRoom) @@ -10270,7 +10533,12 @@ object Http4s600 { created_at = new java.util.Date(), updated_at = new java.util.Date() ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, $BankNotFound, ChatRoomNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + ChatRoomNotFound, + UnknownError + ), apiTagChat :: Nil, Some(canArchiveBankChatRoom :: Nil), http4sPartialFunction = Some(archiveBankChatRoom) @@ -10340,8 +10608,13 @@ object Http4s600 { last_read_at = new java.util.Date(), is_muted = false ), - List($AuthenticatedUserIsRequired, $BankNotFound, InvalidJoiningKey, - ChatRoomIsArchived, ChatRoomParticipantAlreadyExists, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJoiningKey, + ChatRoomIsArchived, + ChatRoomParticipantAlreadyExists, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(joinBankChatRoom) @@ -10361,8 +10634,12 @@ object Http4s600 { |""".stripMargin, EmptyBody, JoiningKeyJsonV600(joining_key = "new-key-abc123"), - List($AuthenticatedUserIsRequired, $BankNotFound, ChatRoomNotFound, - InsufficientChatPermission, UnknownError), + List( + $AuthenticatedUserIsRequired, + ChatRoomNotFound, + InsufficientChatPermission, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(refreshBankJoiningKey) @@ -10420,8 +10697,12 @@ object Http4s600 { created_at = new java.util.Date(), updated_at = new java.util.Date() ), - List($AuthenticatedUserIsRequired, $BankNotFound, InvalidJsonFormat, - ChatRoomAlreadyExists, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + ChatRoomAlreadyExists, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(createBankChatRoom) @@ -10552,8 +10833,12 @@ object Http4s600 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, - $BankNotFound, ChatRoomNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + ChatRoomNotFound, + UnknownError + ), apiTagChat :: Nil, Some(canDeleteBankChatRoom :: Nil), http4sPartialFunction = Some(deleteBankChatRoom) @@ -10587,7 +10872,7 @@ object Http4s600 { nameOf(setBankChatRoomOpenRoom), "PUT", "/banks/BANK_ID/chat-rooms/CHAT_ROOM_ID/open-room", - "Set Bank Chat Room Open Room", + "Set Chat Room All Users Are Participants", s"""Set whether all authenticated users are implicit participants of this chat room. | |If true, all users can read and send messages without needing an explicit Participant record. @@ -10616,8 +10901,12 @@ object Http4s600 { created_at = new java.util.Date(), updated_at = new java.util.Date() ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, - $BankNotFound, ChatRoomNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + ChatRoomNotFound, + UnknownError + ), apiTagChat :: Nil, Some(canSetBankChatRoomIsOpenRoom :: Nil), http4sPartialFunction = Some(setBankChatRoomOpenRoom) @@ -10628,7 +10917,7 @@ object Http4s600 { nameOf(setSystemChatRoomOpenRoom), "PUT", "/chat-rooms/CHAT_ROOM_ID/open-room", - "Set System Chat Room Open Room", + "Set System Chat Room All Users Are Participants", s"""Set whether all authenticated users are implicit participants of this system-level chat room. | |If true, all users can read and send messages without needing an explicit Participant record. @@ -10691,9 +10980,15 @@ object Http4s600 { last_read_at = new java.util.Date(), is_muted = false ), - List($AuthenticatedUserIsRequired, $BankNotFound, InvalidJsonFormat, - ChatRoomNotFound, InsufficientChatPermission, MustSpecifyUserIdOrConsumerId, - ChatRoomParticipantAlreadyExists, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + ChatRoomNotFound, + InsufficientChatPermission, + MustSpecifyUserIdOrConsumerId, + ChatRoomParticipantAlreadyExists, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(addBankChatRoomParticipant) @@ -10760,8 +11055,12 @@ object Http4s600 { last_read_at = new java.util.Date(), is_muted = false ))), - List($AuthenticatedUserIsRequired, $BankNotFound, - ChatRoomNotFound, NotChatRoomParticipant, UnknownError), + List( + $AuthenticatedUserIsRequired, + ChatRoomNotFound, + NotChatRoomParticipant, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(getBankChatRoomParticipants) @@ -10825,8 +11124,14 @@ object Http4s600 { last_read_at = new java.util.Date(), is_muted = false ), - List($AuthenticatedUserIsRequired, $BankNotFound, InvalidJsonFormat, - ChatRoomNotFound, InsufficientChatPermission, ChatRoomParticipantNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + ChatRoomNotFound, + InsufficientChatPermission, + ChatRoomParticipantNotFound, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(updateBankParticipantPermissions) @@ -10878,8 +11183,13 @@ object Http4s600 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, $BankNotFound, - ChatRoomNotFound, InsufficientChatPermission, ChatRoomParticipantNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + ChatRoomNotFound, + InsufficientChatPermission, + ChatRoomParticipantNotFound, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(removeBankChatRoomParticipant) @@ -10935,8 +11245,14 @@ object Http4s600 { updated_at = new java.util.Date(), reactions = List() ), - List($AuthenticatedUserIsRequired, $BankNotFound, InvalidJsonFormat, - ChatRoomNotFound, NotChatRoomParticipant, ChatRoomIsArchived, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + ChatRoomNotFound, + NotChatRoomParticipant, + ChatRoomIsArchived, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(sendBankChatMessage) @@ -11013,8 +11329,12 @@ object Http4s600 { updated_at = new java.util.Date(), reactions = List(ReactionSummaryJsonV600(emoji = "thumbsup", count = 2, user_ids = List("user-1", "user-2"))) ))), - List($AuthenticatedUserIsRequired, $BankNotFound, - ChatRoomNotFound, NotChatRoomParticipant, UnknownError), + List( + $AuthenticatedUserIsRequired, + ChatRoomNotFound, + NotChatRoomParticipant, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(getBankChatMessages) @@ -11091,8 +11411,13 @@ object Http4s600 { updated_at = new java.util.Date(), reactions = List() ), - List($AuthenticatedUserIsRequired, $BankNotFound, - ChatRoomNotFound, NotChatRoomParticipant, ChatMessageNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + ChatRoomNotFound, + NotChatRoomParticipant, + ChatMessageNotFound, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(getBankChatMessage) @@ -11165,9 +11490,15 @@ object Http4s600 { updated_at = new java.util.Date(), reactions = List() ), - List($AuthenticatedUserIsRequired, $BankNotFound, InvalidJsonFormat, - ChatRoomNotFound, NotChatRoomParticipant, ChatMessageNotFound, - CannotEditOthersMessage, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + ChatRoomNotFound, + NotChatRoomParticipant, + ChatMessageNotFound, + CannotEditOthersMessage, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(editBankChatMessage) @@ -11224,9 +11555,14 @@ object Http4s600 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, $BankNotFound, - ChatRoomNotFound, NotChatRoomParticipant, ChatMessageNotFound, - CannotDeleteMessage, UnknownError), + List( + $AuthenticatedUserIsRequired, + ChatRoomNotFound, + NotChatRoomParticipant, + ChatMessageNotFound, + CannotDeleteMessage, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(deleteBankChatMessage) @@ -11283,8 +11619,13 @@ object Http4s600 { updated_at = new java.util.Date(), reactions = List() ))), - List($AuthenticatedUserIsRequired, $BankNotFound, - ChatRoomNotFound, NotChatRoomParticipant, ChatMessageNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + ChatRoomNotFound, + NotChatRoomParticipant, + ChatMessageNotFound, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(getBankThreadReplies) @@ -11357,8 +11698,15 @@ object Http4s600 { updated_at = new java.util.Date(), reactions = List() ), - List($AuthenticatedUserIsRequired, $BankNotFound, InvalidJsonFormat, - ChatRoomNotFound, NotChatRoomParticipant, ChatRoomIsArchived, ChatMessageNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + ChatRoomNotFound, + NotChatRoomParticipant, + ChatRoomIsArchived, + ChatMessageNotFound, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(replyInBankThread) @@ -11422,9 +11770,15 @@ object Http4s600 { emoji = "thumbsup", created_at = new java.util.Date() ), - List($AuthenticatedUserIsRequired, $BankNotFound, InvalidJsonFormat, - ChatRoomNotFound, NotChatRoomParticipant, ChatMessageNotFound, - ReactionAlreadyExists, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + ChatRoomNotFound, + NotChatRoomParticipant, + ChatMessageNotFound, + ReactionAlreadyExists, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(addBankReaction) @@ -11472,9 +11826,14 @@ object Http4s600 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, $BankNotFound, - ChatRoomNotFound, NotChatRoomParticipant, ChatMessageNotFound, - ReactionNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + ChatRoomNotFound, + NotChatRoomParticipant, + ChatMessageNotFound, + ReactionNotFound, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(removeBankReaction) @@ -11522,8 +11881,13 @@ object Http4s600 { emoji = "thumbsup", created_at = new java.util.Date() ))), - List($AuthenticatedUserIsRequired, $BankNotFound, - ChatRoomNotFound, NotChatRoomParticipant, ChatMessageNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + ChatRoomNotFound, + NotChatRoomParticipant, + ChatMessageNotFound, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(getBankReactions) @@ -11573,8 +11937,12 @@ object Http4s600 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, $BankNotFound, - ChatRoomNotFound, NotChatRoomParticipant, UnknownError), + List( + $AuthenticatedUserIsRequired, + ChatRoomNotFound, + NotChatRoomParticipant, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(signalBankTyping) @@ -11613,8 +11981,12 @@ object Http4s600 { |""".stripMargin, EmptyBody, TypingUsersJsonV600(users = List(TypingUserJsonV600(user_id = "user-id-123", username = "robert.x.0.gh", provider = "https://github.com"))), - List($AuthenticatedUserIsRequired, $BankNotFound, - ChatRoomNotFound, NotChatRoomParticipant, UnknownError), + List( + $AuthenticatedUserIsRequired, + ChatRoomNotFound, + NotChatRoomParticipant, + UnknownError + ), apiTagChat :: Nil, None, http4sPartialFunction = Some(getBankTypingUsers) @@ -11861,7 +12233,7 @@ object Http4s600 { nameOf(resetPasswordUrlAnonymous), "POST", "/users/password-reset-url", - "Request Password Reset URL", + "Request Password Reset Email", s"""Request a password reset email for a user. No authentication is required. | |Authentication is NOT Required. @@ -11949,8 +12321,19 @@ object Http4s600 { """.stripMargin, transactionRequestBodyHoldJsonV600, transactionRequestWithChargeJSON400, - txReqErrors, - txReqTags, + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + $BankAccountNotFound, + InsufficientAuthorisationToCreateTransactionRequest, + InvalidTransactionRequestType, + InvalidJsonFormat, + NotPositiveAmount, + InvalidTransactionRequestCurrency, + TransactionDisabled, + UnknownError + ), + List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(createTransactionRequestHold) ) @@ -11971,8 +12354,19 @@ object Http4s600 { """.stripMargin, transactionRequestBodyCardanoJsonV600, transactionRequestWithChargeJSON400, - txReqErrors, - txReqTags, + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + $BankAccountNotFound, + InsufficientAuthorisationToCreateTransactionRequest, + InvalidTransactionRequestType, + InvalidJsonFormat, + NotPositiveAmount, + InvalidTransactionRequestCurrency, + TransactionDisabled, + UnknownError + ), + List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(createTransactionRequestCardano) ) @@ -11993,8 +12387,19 @@ object Http4s600 { """.stripMargin, transactionRequestBodyEthereumJsonV600, transactionRequestWithChargeJSON400, - txReqErrors, - txReqTags, + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + $BankAccountNotFound, + InsufficientAuthorisationToCreateTransactionRequest, + InvalidTransactionRequestType, + InvalidJsonFormat, + NotPositiveAmount, + InvalidTransactionRequestCurrency, + TransactionDisabled, + UnknownError + ), + List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(createTransactionRequestEthereumeSendTransaction) ) @@ -12004,7 +12409,7 @@ object Http4s600 { nameOf(createTransactionRequestEthSendRawTransaction), "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/owner/transaction-request-types/ETH_SEND_RAW_TRANSACTION/transaction-requests", - "Create Transaction Request (ETH_SEND_RAW_TRANSACTION)", + "CREATE TRANSACTION REQUEST (ETH_SEND_RAW_TRANSACTION )", s""" | |Send ETH via Ethereum JSON-RPC. @@ -12015,8 +12420,19 @@ object Http4s600 { """.stripMargin, transactionRequestBodyEthSendRawTransactionJsonV600, transactionRequestWithChargeJSON400, - txReqErrors, - txReqTags, + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + $BankAccountNotFound, + InsufficientAuthorisationToCreateTransactionRequest, + InvalidTransactionRequestType, + InvalidJsonFormat, + NotPositiveAmount, + InvalidTransactionRequestCurrency, + TransactionDisabled, + UnknownError + ), + List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(createTransactionRequestEthSendRawTransaction) ) @@ -12026,7 +12442,7 @@ object Http4s600 { nameOf(getUserGroupMemberships), "GET", "/users/USER_ID/group-entitlements", - "Get User Group Memberships", + "Get User's Group Memberships", s"""Get all groups a user is a member of. | |Returns groups where the user has entitlements with process = "GROUP_MEMBERSHIP". @@ -12053,7 +12469,11 @@ object Http4s600 { ) ) ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UserNotFoundByUserId, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagGroup :: apiTagUser :: apiTagEntitlement :: Nil, Some(canGetUserGroupMembershipsAtAllBanks :: canGetUserGroupMembershipsAtOneBank :: Nil), http4sPartialFunction = Some(getUserGroupMemberships) @@ -12086,7 +12506,11 @@ object Http4s600 { access_source = "ACCOUNT_ACCESS" )) ), - List($BankNotFound, $BankAccountNotFound, ViewNotFound, UnknownError), + List( + $BankNotFound, + BankAccountNotFound, + UnknownError + ), apiTagAccount :: apiTagView :: Nil, Some(canSeeAccountAccessForAnyUser :: Nil), http4sPartialFunction = Some(getUsersWithAccountAccess) @@ -12125,8 +12549,18 @@ object Http4s600 { |""", postRetailCustomerJsonV600, customerJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, $BankNotFound, - InvalidJsonFormat, InvalidJsonContent, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + InvalidJsonFormat, + InvalidJsonContent, + InvalidDateFormat, + CustomerNumberAlreadyExists, + UserNotFoundById, + CustomerAlreadyExistsForUser, + CreateConsumerError, + UnknownError + ), apiTagRetailCustomer :: apiTagCustomer :: Nil, Some(canCreateCustomer :: canCreateCustomerAtAnyBank :: Nil), http4sPartialFunction = Some(createRetailCustomer) @@ -12163,8 +12597,18 @@ object Http4s600 { |""", postCorporateCustomerJsonV600, customerJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, $BankNotFound, - InvalidJsonFormat, InvalidCustomerType, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + InvalidJsonFormat, + InvalidCustomerType, + ParentCustomerNotFound, + CustomerNumberAlreadyExists, + UserNotFoundById, + CustomerAlreadyExistsForUser, + CreateConsumerError, + UnknownError + ), apiTagCorporateCustomer :: apiTagCustomer :: Nil, Some(canCreateCustomer :: canCreateCustomerAtAnyBank :: Nil), http4sPartialFunction = Some(createCorporateCustomer) @@ -12175,7 +12619,7 @@ object Http4s600 { nameOf(getUserByUserId), "GET", "/users/user-id/USER_ID", - "Get User By User Id", + "Get User by USER_ID", s"""Get user by USER_ID | |${userAuthenticationMessage(true)} @@ -12215,7 +12659,12 @@ object Http4s600 { |""".stripMargin, EmptyBody, JSONFactory600.createTokenJSON("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczpcL1wvd3d3Lm9wZW5iYW5rcHJvamVjdC5jb20iLCJpYXQiOjE0NTU4OTQyNzYsImV4cCI6MTQ1NTg5Nzg3NiwiYXVkIjoib2JwLWFwaSIsInN1YiI6IjA2Zjc0YjUwLTA5OGYtNDYwNi1hOGNjLTBjNDc5MjAyNmI5ZCIsImNvbnN1bWVyX2tleSI6IjYwNGY3ZTAyNGQ5MWU2MzMwNGMzOGM0YzRmZjc0MjMwZGU5NDk4NTEwNjgxZWNjM2Q5MzViNWQ5MGEwOTI3ODciLCJyb2xlIjoiY2FuX2FjY2Vzc19hcGkifQ.f8xHvXP5fDxo5-LlfTj1OQS9oqHNZfFd7N-WkV2o4Cc"), - List(InvalidLoginCredentials, UsernameHasBeenLocked, UnknownError), + List( + InvalidDirectLoginParameters, + InvalidLoginCredentials, + InvalidConsumerCredentials, + UnknownError + ), apiTagUser :: Nil, None, http4sPartialFunction = Some(directLoginEndpoint) @@ -12259,8 +12708,12 @@ object Http4s600 { valid = true, message = "ABAC rule code is valid" ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, - AbacRuleCodeEmpty, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), apiTagABAC :: Nil, Some(canCreateAbacRule :: Nil), http4sPartialFunction = Some(validateAbacRule) @@ -12301,7 +12754,12 @@ object Http4s600 { AbacRuleResultJsonV600( result = true ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), apiTagABAC :: Nil, Some(canExecuteAbacRule :: Nil), http4sPartialFunction = Some(executeAbacRule) @@ -12345,7 +12803,12 @@ object Http4s600 { AbacRuleResultJsonV600( result = true ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), apiTagABAC :: Nil, Some(canExecuteAbacRule :: Nil), http4sPartialFunction = Some(executeAbacPolicy) @@ -12421,7 +12884,11 @@ object Http4s600 { "Attributes are Lists - use .find(), .exists(), .forall() etc." ) ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagABAC :: Nil, Some(canGetAbacRule :: Nil), http4sPartialFunction = Some(getAbacRuleSchema) @@ -12503,7 +12970,7 @@ object Http4s600 { nameOf(deleteSystemDynamicEntityCascade), "DELETE", "/management/system-dynamic-entities/cascade/DYNAMIC_ENTITY_ID", - "Delete System Dynamic Entity Cascade", + "Delete System Level Dynamic Entity Cascade", s"""Delete a DynamicEntity specified by DYNAMIC_ENTITY_ID and all its data records. | |This endpoint performs a cascade delete: @@ -12527,8 +12994,11 @@ object Http4s600 { |""", EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, - CannotDeleteCascadePersonalEntity, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagManageDynamicEntity :: apiTagApi :: Nil, Some(canDeleteCascadeSystemDynamicEntity :: Nil), http4sPartialFunction = Some(deleteSystemDynamicEntityCascade) @@ -12585,8 +13055,15 @@ object Http4s600 { |""", EmptyBody, investigationReportJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, InvestigationReportNotAvailable, UnknownError), - apiTagCustomer :: Nil, + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + CustomerNotFoundByCustomerId, + InvestigationReportNotAvailable, + UserHasMissingRoles, + UnknownError + ), + List(apiTagCustomer, apiTagKyc, apiTagTransaction, apiTagAccount, apiTagFinancialCrime, apiTagAiAgent), Some(canGetInvestigationReport :: Nil), http4sPartialFunction = Some(getCustomerInvestigationReport) ) @@ -12604,7 +13081,15 @@ object Http4s600 { |""", postCustomerLinkJsonV600, customerLinkJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + InvalidJsonFormat, + CustomerNotFoundByCustomerId, + UserHasMissingRoles, + CreateCustomerLinkError, + UnknownError + ), apiTagCustomer :: Nil, Some(canCreateCustomerLink :: Nil), http4sPartialFunction = Some(createCustomerLink) @@ -12615,7 +13100,7 @@ object Http4s600 { nameOf(getCustomerLinksByBankId), "GET", "/banks/BANK_ID/customer-links", - "Get Customer Links by Bank", + "Get Customer Links at Bank", s"""Get all Customer Links at a Bank. | |Authentication is Required @@ -12634,7 +13119,7 @@ object Http4s600 { nameOf(getCustomerLinkById), "GET", "/banks/BANK_ID/customer-links/CUSTOMER_LINK_ID", - "Get Customer Link by Id", + "Get Customer Link by CUSTOMER_LINK_ID", s"""Get Customer Link by CUSTOMER_LINK_ID. | |Authentication is Required @@ -12642,7 +13127,13 @@ object Http4s600 { |""", EmptyBody, customerLinkJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + CustomerLinkNotFound, + UserHasMissingRoles, + UnknownError + ), apiTagCustomer :: Nil, Some(canGetCustomerLink :: Nil), http4sPartialFunction = Some(getCustomerLinkById) @@ -12661,7 +13152,15 @@ object Http4s600 { |""", putCustomerLinkJsonV600, customerLinkJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + InvalidJsonFormat, + CustomerLinkNotFound, + UserHasMissingRoles, + UpdateCustomerLinkError, + UnknownError + ), apiTagCustomer :: Nil, Some(canUpdateCustomerLink :: Nil), http4sPartialFunction = Some(updateCustomerLink) @@ -12680,7 +13179,13 @@ object Http4s600 { |""", EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + CustomerLinkNotFound, + UserHasMissingRoles, + UnknownError + ), apiTagCustomer :: Nil, Some(canDeleteCustomerLink :: Nil), http4sPartialFunction = Some(deleteCustomerLink) @@ -12691,7 +13196,7 @@ object Http4s600 { nameOf(getCustomViewById), "GET", "/management/banks/BANK_ID/accounts/ACCOUNT_ID/views/VIEW_ID", - "Get Custom View by Id", + "Get Custom View", s"""Get a single custom view by bank, account, and view ID. | |Custom views are user-created views with names starting with underscore (_), such as: @@ -12727,8 +13232,13 @@ object Http4s600 { "can_add_comment" ) ), - List($AuthenticatedUserIsRequired, UnknownError), - apiTagView :: Nil, + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + ViewNotFound, + UnknownError + ), + List(apiTagView, apiTagSystemView), None, http4sPartialFunction = Some(getCustomViewById) ) @@ -12758,7 +13268,12 @@ object Http4s600 { new_version = 2, status = "invalidated" ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + InvalidJsonFormat, + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagCache :: apiTagSystem :: apiTagApi :: Nil, Some(canInvalidateCacheNamespace :: Nil), http4sPartialFunction = Some(invalidateCacheNamespace) @@ -12792,7 +13307,9 @@ object Http4s600 { |""".stripMargin, EmptyBody, configPropsJsonV600, - List($AuthenticatedUserIsRequired, UnknownError), + List( + UnknownError + ), apiTagApi :: Nil, None, http4sPartialFunction = Some(getConfigProps) @@ -12846,8 +13363,12 @@ object Http4s600 { |""".stripMargin, EmptyBody, ViewsJsonV600(List()), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagView :: Nil, + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), + List(apiTagView, apiTagSystemView), Some(canGetCustomViews :: Nil), http4sPartialFunction = Some(getCustomViews) ) @@ -12883,8 +13404,12 @@ object Http4s600 { ) ) ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagRole :: Nil, + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), + List(apiTagRole, apiTagEntitlement), Some(canGetRolesWithEntitlementCountsAtAllBanks :: Nil), http4sPartialFunction = Some(getRolesWithEntitlementCountsAtAllBanks) ) @@ -12955,8 +13480,13 @@ object Http4s600 { active_rate_limits = activeRateLimitsJsonV600, call_counters = redisCallCountersJsonV600 ), - List($AuthenticatedUserIsRequired, InvalidConsumerCredentials, UnknownError), - apiTagConsumer :: Nil, + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidConsumerCredentials, + UnknownError + ), + apiTagConsumer :: apiTagApi :: Nil, Some(canGetCurrentConsumer :: Nil), http4sPartialFunction = Some(getCurrentConsumer) ) @@ -12990,7 +13520,7 @@ object Http4s600 { ) ), List(UnknownError), - apiTagApi :: Nil, + List(apiTagMetric, apiTagApi), None, http4sPartialFunction = Some(getPopularApis) ) @@ -13000,7 +13530,7 @@ object Http4s600 { nameOf(getAccountDirectory), "GET", "/banks/BANK_ID/account-directory", - "Get Account Directory", + "Get Account Directory at Bank", s"""Returns a list of accounts at the bank with identifiers and metadata. | |This endpoint is designed for management UIs that need to list accounts @@ -13028,7 +13558,10 @@ object Http4s600 { view_ids = List("owner") )) ), - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $BankNotFound, + UnknownError + ), apiTagAccount :: Nil, Some(canGetAccountDirectoryAtOneBank :: Nil), http4sPartialFunction = Some(getAccountDirectory) @@ -13066,7 +13599,12 @@ object Http4s600 { list_of_roles = List("CanGetCustomer", "CanGetAccount", "CanCreateTransaction"), is_enabled = true ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), apiTagGroup :: Nil, None, http4sPartialFunction = Some(createGroup) @@ -13096,7 +13634,11 @@ object Http4s600 { list_of_roles = List("CanGetCustomer", "CanGetAccount", "CanCreateTransaction"), is_enabled = true ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagGroup :: Nil, None, http4sPartialFunction = Some(getGroup) @@ -13133,7 +13675,11 @@ object Http4s600 { ) ) ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagGroup :: Nil, None, http4sPartialFunction = Some(getGroups) @@ -13168,7 +13714,12 @@ object Http4s600 { list_of_roles = List("CanGetCustomer", "CanGetAccount", "CanCreateTransaction", "CanGetTransaction"), is_enabled = true ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), apiTagGroup :: Nil, None, http4sPartialFunction = Some(updateGroup) @@ -13191,7 +13742,11 @@ object Http4s600 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagGroup :: Nil, None, http4sPartialFunction = Some(deleteGroup) @@ -13227,8 +13782,12 @@ object Http4s600 { ) ) ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagGroup :: Nil, + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), + List(apiTagGroup, apiTagEntitlement), Some(canGetEntitlementsForAnyBank :: Nil), http4sPartialFunction = Some(getGroupEntitlements) ) @@ -13282,7 +13841,12 @@ object Http4s600 { created_by_user_id = "user123", updated_by_user_id = "user123" ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), apiTagABAC :: Nil, Some(canCreateAbacRule :: Nil), http4sPartialFunction = Some(createAbacRule) @@ -13315,7 +13879,11 @@ object Http4s600 { created_by_user_id = "user123", updated_by_user_id = "user123" ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagABAC :: Nil, Some(canGetAbacRule :: Nil), http4sPartialFunction = Some(getAbacRule) @@ -13352,7 +13920,11 @@ object Http4s600 { ) ) ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagABAC :: Nil, Some(canGetAbacRule :: Nil), http4sPartialFunction = Some(getAbacRules) @@ -13402,7 +13974,11 @@ object Http4s600 { ) ) ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagABAC :: Nil, Some(canGetAbacRule :: Nil), http4sPartialFunction = Some(getAbacRulesByPolicy) @@ -13441,7 +14017,12 @@ object Http4s600 { created_by_user_id = "user123", updated_by_user_id = "user456" ), - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), apiTagABAC :: Nil, Some(canUpdateAbacRule :: Nil), http4sPartialFunction = Some(updateAbacRule) @@ -13464,7 +14045,11 @@ object Http4s600 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), apiTagABAC :: Nil, Some(canDeleteAbacRule :: Nil), http4sPartialFunction = Some(deleteAbacRule) @@ -13497,7 +14082,7 @@ object Http4s600 { ), userAttributeResponseJsonV510, List($AuthenticatedUserIsRequired, InvalidJsonFormat, UnknownError), - apiTagUser :: Nil, + List(apiTagUser, apiTagUserAttribute, apiTagAttribute), Some(Nil), http4sPartialFunction = Some(createPersonalDataField) ) @@ -13519,7 +14104,7 @@ object Http4s600 { user_attributes = List(userAttributeResponseJsonV510) ), List($AuthenticatedUserIsRequired, UnknownError), - apiTagUser :: Nil, + List(apiTagUser, apiTagUserAttribute, apiTagAttribute), Some(Nil), http4sPartialFunction = Some(getPersonalDataFields) ) @@ -13537,7 +14122,7 @@ object Http4s600 { EmptyBody, userAttributeResponseJsonV510, List($AuthenticatedUserIsRequired, UserAttributeNotFound, UnknownError), - apiTagUser :: Nil, + List(apiTagUser, apiTagUserAttribute, apiTagAttribute), Some(Nil), http4sPartialFunction = Some(getPersonalDataFieldById) ) @@ -13558,8 +14143,13 @@ object Http4s600 { value = "green" ), userAttributeResponseJsonV510, - List($AuthenticatedUserIsRequired, InvalidJsonFormat, UserAttributeNotFound, UnknownError), - apiTagUser :: Nil, + List( + $AuthenticatedUserIsRequired, + UserAttributeNotFound, + InvalidJsonFormat, + UnknownError + ), + List(apiTagUser, apiTagUserAttribute, apiTagAttribute), Some(Nil), http4sPartialFunction = Some(updatePersonalDataField) ) @@ -13577,7 +14167,7 @@ object Http4s600 { EmptyBody, EmptyBody, List($AuthenticatedUserIsRequired, UserAttributeNotFound, UnknownError), - apiTagUser :: Nil, + List(apiTagUser, apiTagUserAttribute, apiTagAttribute), Some(Nil), http4sPartialFunction = Some(deletePersonalDataField) ) @@ -13587,7 +14177,7 @@ object Http4s600 { nameOf(getConsumerCallCounters), "GET", "/management/consumers/CONSUMER_ID/call-counters", - "Get Consumer Call Counters", + "Get Call Counts for Consumer", s""" |Get the call counters (current usage) for a specific consumer. Shows how many API calls have been made and when the counters reset. | @@ -13613,7 +14203,15 @@ object Http4s600 { |""".stripMargin, EmptyBody, redisCallCountersJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + InvalidConsumerId, + ConsumerNotFoundByConsumerId, + UserHasMissingRoles, + UpdateConsumerError, + UnknownError + ), apiTagConsumer :: Nil, Some(canGetRateLimits :: Nil), http4sPartialFunction = Some(getConsumerCallCounters) @@ -13633,7 +14231,14 @@ object Http4s600 { |""".stripMargin, callLimitPostJsonV600, callLimitJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + InvalidConsumerId, + ConsumerNotFoundByConsumerId, + UserHasMissingRoles, + UnknownError + ), apiTagConsumer :: Nil, Some(canCreateRateLimits :: Nil), http4sPartialFunction = Some(createCallLimits) @@ -13644,7 +14249,7 @@ object Http4s600 { nameOf(updateRateLimits), "PUT", "/management/consumers/CONSUMER_ID/consumer/rate-limits/RATE_LIMITING_ID", - "Update Rate Limits for a Consumer", + "Set Rate Limits / Call Limits per Consumer", s""" |Set the API rate limits / call limits for a Consumer: | @@ -13662,8 +14267,16 @@ object Http4s600 { |""".stripMargin, callLimitPostJsonV400, callLimitPostJsonV400, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), - apiTagConsumer :: Nil, + List( + AuthenticatedUserIsRequired, + InvalidJsonFormat, + InvalidConsumerId, + ConsumerNotFoundByConsumerId, + UserHasMissingRoles, + UpdateConsumerError, + UnknownError + ), + List(apiTagConsumer, apiTagRateLimits), Some(canUpdateRateLimits :: Nil), http4sPartialFunction = Some(updateRateLimits) ) @@ -13673,7 +14286,7 @@ object Http4s600 { nameOf(deleteCallLimits), "DELETE", "/management/consumers/CONSUMER_ID/consumer/rate-limits/RATE_LIMITING_ID", - "Delete Rate Limits for a Consumer", + "Delete Rate Limit by Rate Limiting ID", s""" |Delete a specific Rate Limit by Rate Limiting ID | @@ -13682,7 +14295,13 @@ object Http4s600 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidConsumerId, + ConsumerNotFoundByConsumerId, + UserHasMissingRoles, + UnknownError + ), apiTagConsumer :: Nil, Some(canDeleteRateLimits :: Nil), http4sPartialFunction = Some(deleteCallLimits) @@ -13693,7 +14312,7 @@ object Http4s600 { nameOf(getActiveRateLimitsNow), "GET", "/management/consumers/CONSUMER_ID/active-rate-limits", - "Get Active Rate Limits (now)", + "Get Active Rate Limits (Current)", s""" |Get the active rate limits for a consumer at the current date/time. Returns the aggregated rate limits from all active records at this moment. | @@ -13706,7 +14325,13 @@ object Http4s600 { |""".stripMargin, EmptyBody, activeRateLimitsJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidConsumerId, + ConsumerNotFoundByConsumerId, + UserHasMissingRoles, + UnknownError + ), apiTagConsumer :: Nil, Some(canGetRateLimits :: Nil), http4sPartialFunction = Some(getActiveRateLimitsNow) @@ -13717,7 +14342,7 @@ object Http4s600 { nameOf(getActiveRateLimitsAtDate), "GET", "/management/consumers/CONSUMER_ID/active-rate-limits/DATE_WITH_HOUR", - "Get Active Rate Limits at Date", + "Get Active Rate Limits for Hour", s""" |Get the active rate limits for a consumer for a specific hour. Returns the aggregated rate limits from all active records during that hour. | @@ -13734,7 +14359,14 @@ object Http4s600 { |""".stripMargin, EmptyBody, activeRateLimitsJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidDateFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidConsumerId, + ConsumerNotFoundByConsumerId, + UserHasMissingRoles, + InvalidDateFormat, + UnknownError + ), apiTagConsumer :: Nil, Some(canGetRateLimits :: Nil), http4sPartialFunction = Some(getActiveRateLimitsAtDate) @@ -13753,8 +14385,15 @@ object Http4s600 { |""".stripMargin, postFeaturedApiCollectionJsonV600, featuredApiCollectionJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, ApiCollectionNotFound, UnknownError), - apiTagApiCollection :: Nil, + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + ApiCollectionNotFound, + FeaturedApiCollectionAlreadyExists, + CreateFeaturedApiCollectionError, + UnknownError + ), + List(apiTagApiCollection, apiTagApi), Some(canManageFeaturedApiCollections :: Nil), http4sPartialFunction = Some(createFeaturedApiCollection) ) @@ -13776,7 +14415,7 @@ object Http4s600 { EmptyBody, featuredApiCollectionsJsonV600, List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagApiCollection :: Nil, + List(apiTagApiCollection, apiTagApi), Some(canManageFeaturedApiCollections :: Nil), http4sPartialFunction = Some(getFeaturedApiCollectionsAdmin) ) @@ -13794,8 +14433,14 @@ object Http4s600 { |""".stripMargin, putFeaturedApiCollectionJsonV600, featuredApiCollectionJsonV600, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), - apiTagApiCollection :: Nil, + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + FeaturedApiCollectionNotFound, + UpdateFeaturedApiCollectionError, + UnknownError + ), + List(apiTagApiCollection, apiTagApi), Some(canManageFeaturedApiCollections :: Nil), http4sPartialFunction = Some(updateFeaturedApiCollection) ) @@ -13813,8 +14458,14 @@ object Http4s600 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), - apiTagApiCollection :: Nil, + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + FeaturedApiCollectionNotFound, + DeleteFeaturedApiCollectionError, + UnknownError + ), + List(apiTagApiCollection, apiTagApi), Some(canManageFeaturedApiCollections :: Nil), http4sPartialFunction = Some(deleteFeaturedApiCollection) ) @@ -13832,7 +14483,13 @@ object Http4s600 { |""".stripMargin, postPutApiProductJsonV600, apiProductJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + CreateApiProductError, + UnknownError + ), apiTagApi :: apiTagApiProduct :: Nil, Some(canCreateApiProduct :: Nil), http4sPartialFunction = Some(createApiProduct) @@ -13851,7 +14508,13 @@ object Http4s600 { |""".stripMargin, postPutApiProductJsonV600, apiProductJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + CreateApiProductError, + UnknownError + ), apiTagApi :: apiTagApiProduct :: Nil, Some(canUpdateApiProduct :: Nil), http4sPartialFunction = Some(createOrUpdateApiProduct) @@ -13872,7 +14535,7 @@ object Http4s600 { |""".stripMargin, EmptyBody, apiProductJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + if (getApiProductsIsPublic) List(ApiProductNotFound, UnknownError) else List(UserHasMissingRoles, ApiProductNotFound, UnknownError), apiTagApi :: apiTagApiProduct :: Nil, Some(canGetApiProduct :: Nil), http4sPartialFunction = Some(getApiProduct) @@ -13893,7 +14556,7 @@ object Http4s600 { |""".stripMargin, EmptyBody, apiProductsJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + if (getApiProductsIsPublic) List(UnknownError) else List(UserHasMissingRoles, UnknownError), apiTagApi :: apiTagApiProduct :: Nil, Some(canGetApiProduct :: Nil), http4sPartialFunction = Some(getApiProducts) @@ -13912,7 +14575,13 @@ object Http4s600 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + ApiProductNotFound, + DeleteApiProductError, + UnknownError + ), apiTagApi :: apiTagApiProduct :: Nil, Some(canDeleteApiProduct :: Nil), http4sPartialFunction = Some(deleteApiProduct) @@ -13931,7 +14600,14 @@ object Http4s600 { |""".stripMargin, apiProductAttributeJsonV600, apiProductAttributeResponseJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + ApiProductNotFound, + CreateApiProductAttributeError, + UnknownError + ), apiTagApi :: apiTagApiProductAttribute :: Nil, Some(canCreateApiProductAttribute :: Nil), http4sPartialFunction = Some(createApiProductAttribute) @@ -13950,7 +14626,14 @@ object Http4s600 { |""".stripMargin, apiProductAttributeJsonV600, apiProductAttributeResponseJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + ApiProductNotFound, + ApiProductAttributeNotFound, + UnknownError + ), apiTagApi :: apiTagApiProductAttribute :: Nil, Some(canUpdateApiProductAttribute :: Nil), http4sPartialFunction = Some(updateApiProductAttribute) @@ -13969,7 +14652,7 @@ object Http4s600 { |""".stripMargin, EmptyBody, apiProductAttributeResponseJsonV600, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + if (getApiProductsIsPublic) List(ApiProductAttributeNotFound, UnknownError) else List(UserHasMissingRoles, ApiProductAttributeNotFound, UnknownError), apiTagApi :: apiTagApiProductAttribute :: Nil, Some(canGetApiProductAttribute :: Nil), http4sPartialFunction = Some(getApiProductAttribute) @@ -13988,7 +14671,13 @@ object Http4s600 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + ApiProductAttributeNotFound, + DeleteApiProductAttributeError, + UnknownError + ), apiTagApi :: apiTagApiProductAttribute :: Nil, Some(canDeleteApiProductAttribute :: Nil), http4sPartialFunction = Some(deleteApiProductAttribute) @@ -14039,7 +14728,13 @@ object Http4s600 { created_by_user_id = "user-id-123", updated_by_user_id = "user-id-123" ), - List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + $BankNotFound, + InvalidJsonFormat, + UnknownError + ), apiTagMandate :: Nil, Some(canCreateMandate :: Nil), http4sPartialFunction = Some(createMandate) @@ -14050,7 +14745,7 @@ object Http4s600 { nameOf(getMandates), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/mandates", - "Get Mandates", + "Get Mandates for Account", s"""Get all mandates for a bank account. | |Authentication is Required @@ -14071,7 +14766,12 @@ object Http4s600 { created_by_user_id = "user-id-123", updated_by_user_id = "user-id-123" ))), - List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + $BankNotFound, + UnknownError + ), apiTagMandate :: Nil, Some(canGetMandate :: Nil), http4sPartialFunction = Some(getMandates) @@ -14103,7 +14803,12 @@ object Http4s600 { created_by_user_id = "user-id-123", updated_by_user_id = "user-id-123" ), - List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + $BankNotFound, + UnknownError + ), apiTagMandate :: Nil, Some(canGetMandate :: Nil), http4sPartialFunction = Some(getMandate) @@ -14143,7 +14848,13 @@ object Http4s600 { created_by_user_id = "user-id-123", updated_by_user_id = "user-id-456" ), - List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + $BankNotFound, + InvalidJsonFormat, + UnknownError + ), apiTagMandate :: Nil, Some(canUpdateMandate :: Nil), http4sPartialFunction = Some(updateMandate) @@ -14161,7 +14872,12 @@ object Http4s600 { |""", EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + $BankNotFound, + UnknownError + ), apiTagMandate :: Nil, Some(canDeleteMandate :: Nil), http4sPartialFunction = Some(deleteMandate) @@ -14215,7 +14931,13 @@ object Http4s600 { is_active = true, sort_order = 1 ), - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + $BankNotFound, + InvalidJsonFormat, + UnknownError + ), apiTagMandate :: Nil, Some(canCreateMandateProvision :: Nil), http4sPartialFunction = Some(createMandateProvision) @@ -14250,7 +14972,12 @@ object Http4s600 { is_active = true, sort_order = 1 ))), - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + $BankNotFound, + UnknownError + ), apiTagMandate :: Nil, Some(canGetMandateProvision :: Nil), http4sPartialFunction = Some(getMandateProvisions) @@ -14282,7 +15009,12 @@ object Http4s600 { is_active = true, sort_order = 1 ), - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + $BankNotFound, + UnknownError + ), apiTagMandate :: Nil, Some(canGetMandateProvision :: Nil), http4sPartialFunction = Some(getMandateProvision) @@ -14326,7 +15058,13 @@ object Http4s600 { is_active = true, sort_order = 2 ), - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + $BankNotFound, + InvalidJsonFormat, + UnknownError + ), apiTagMandate :: Nil, Some(canUpdateMandateProvision :: Nil), http4sPartialFunction = Some(updateMandateProvision) @@ -14344,7 +15082,12 @@ object Http4s600 { |""", EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, $BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + $BankNotFound, + UnknownError + ), apiTagMandate :: Nil, Some(canDeleteMandateProvision :: Nil), http4sPartialFunction = Some(deleteMandateProvision) diff --git a/scripts/check_lift_http4s_resource_doc_parity.py b/scripts/check_lift_http4s_resource_doc_parity.py index b3d54cc62a..352e5e421c 100755 --- a/scripts/check_lift_http4s_resource_doc_parity.py +++ b/scripts/check_lift_http4s_resource_doc_parity.py @@ -132,14 +132,28 @@ def _skip_string_or_comment(source: str, i: int): """ n = len(source) c = source[i] - # triple-quoted plain + # triple-quoted plain. Scala's lexer is greedy about trailing `"` — + # e.g. `"""foo "bar""""` has content `foo "bar"` and closer `"""`, with + # the 4 quotes at the end being content-quote + 3 closer quotes. + # A non-greedy `find('"""', i+3)` would split at the wrong boundary, + # leaving a stray `"` that misaligns every subsequent string in the file. if source.startswith('"""', i): j = source.find('"""', i + 3) - return n if j == -1 else j + 3 + if j == -1: + return n + k = j + 3 + while k < n and source[k] == '"': + k += 1 + return k # interpolated triple-quote: s"""...""" or f"""...""" if c in ("s", "f") and source.startswith('"""', i + 1): j = source.find('"""', i + 4) - return n if j == -1 else j + 3 + if j == -1: + return n + k = j + 3 + while k < n and source[k] == '"': + k += 1 + return k # plain double-quoted string (handle escapes) if c == '"': j = i + 1 @@ -266,14 +280,37 @@ def parse_resourcedoc(body: str): def endpoint_name(part_fn_name: str) -> str: - """`nameOf(getBanks)` -> `getBanks`. Literal `"root"` -> `root`.""" + """`nameOf(getBanks)` -> `getBanks`. Literal `"root"` -> `root`. + + Also evaluates chained `.replace("a", "b")` calls so e.g. + `nameOf(createConsentByConsentRequestId).replace("Id", "IdEmail")` + becomes `createConsentByConsentRequestIdEmail` — matches what runs at + runtime when http4s derives names for related ResourceDoc entries. + """ s = (part_fn_name or "").strip() - m = re.match(r"^nameOf\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)\s*$", s) + # First extract the base name from `nameOf(...)` or a string literal. + rest = s + m = re.match(r"^nameOf\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)", s) if m: - return m.group(1) - if s.startswith('"') and s.endswith('"'): - return s[1:-1] - return s + base = m.group(1) + rest = s[m.end():] + elif s.startswith('"'): + m2 = re.match(r'^"([^"]*)"', s) + if not m2: + return s + base = m2.group(1) + rest = s[m2.end():] + else: + return s + # Apply any trailing `.replace("a", "b")` calls in order. + rep_re = re.compile(r'^\s*\.\s*replace\s*\(\s*"([^"]*)"\s*,\s*"([^"]*)"\s*\)') + while True: + m = rep_re.match(rest) + if not m: + break + base = base.replace(m.group(1), m.group(2)) + rest = rest[m.end():] + return base def normalize(s: str) -> str: diff --git a/scripts/restore_resource_doc_bodies.py b/scripts/restore_resource_doc_bodies.py new file mode 100644 index 0000000000..e7d3df6235 --- /dev/null +++ b/scripts/restore_resource_doc_bodies.py @@ -0,0 +1,487 @@ +#!/usr/bin/env python3 +""" +restore_resource_doc_bodies.py + +Companion to scripts/rehydrate_resource_docs.py — that script lifts +description / exampleRequestBody / successResponseBody. This script lifts the +remaining "metadata" fields that public docs consumers also see: + - summary + - errorResponseBodies + - tags + +Reads the commented Lift ResourceDocs in APIMethodsXYZ.scala and overwrites +the matching field in Http4sXYZ.scala. Endpoints are matched by `nameOf(...)`. + +Usage: + python3 scripts/restore_resource_doc_bodies.py v6_0_0 + python3 scripts/restore_resource_doc_bodies.py v6_0_0 --dry-run + python3 scripts/restore_resource_doc_bodies.py v6_0_0 --fields=tags + python3 scripts/restore_resource_doc_bodies.py v6_0_0 --only=getXyz + +Read+writes the http4s file in place. Does NOT touch the Lift file. +""" + +import argparse +import re +import sys +from collections import OrderedDict +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[1] +API_ROOT = REPO_ROOT / "obp-api" / "src" / "main" / "scala" / "code" / "api" + +POSITIONAL_FIELDS = [ + "partialFunction", # 0 + "implementedInApiVersion", # 1 + "partialFunctionName", # 2 — endpoint identifier (nameOf(...)) + "requestVerb", # 3 + "requestUrl", # 4 + "summary", # 5 + "description", # 6 + "exampleRequestBody", # 7 + "successResponseBody", # 8 + "errorResponseBodies", # 9 + "tags", # 10 +] + +NAMED_ARG_FIELDS = { + "roles", + "http4sPartialFunction", + "isFeatured", + "specialInstructions", + "createdByBankId", + "authMode", +} + +DEFAULT_FIELDS_TO_RESTORE = [ + "summary", + "errorResponseBodies", + "tags", +] + + +def uncomment(source: str) -> str: + out = [] + for line in source.splitlines(): + m = re.match(r"^(\s*)//\s?(.*)$", line) + if m: + out.append(m.group(1) + m.group(2)) + else: + out.append(line) + return "\n".join(out) + + +def _skip_string_or_comment(source: str, i: int): + n = len(source) + c = source[i] + # Scala's lexer is greedy about trailing `"` in `"""..."""` — consume + # any extra `"` chars after the closer to keep string boundaries + # aligned through the rest of the source. + if source.startswith('"""', i): + j = source.find('"""', i + 3) + if j == -1: + return n + k = j + 3 + while k < n and source[k] == '"': + k += 1 + return k + if c in ("s", "f") and source.startswith('"""', i + 1): + j = source.find('"""', i + 4) + if j == -1: + return n + k = j + 3 + while k < n and source[k] == '"': + k += 1 + return k + if c == '"': + j = i + 1 + while j < n: + if source[j] == "\\": + j += 2 + continue + if source[j] == '"': + return j + 1 + j += 1 + return n + if c == "/" and i + 1 < n and source[i + 1] == "/": + j = source.find("\n", i) + return n if j == -1 else j + 1 + if c == "/" and i + 1 < n and source[i + 1] == "*": + j = source.find("*/", i + 2) + return n if j == -1 else j + 2 + return None + + +def scan_matching_close(source: str, start: int, open_ch: str, close_ch: str): + depth = 1 + i = start + n = len(source) + while i < n: + skipped = _skip_string_or_comment(source, i) + if skipped is not None: + i = skipped + continue + c = source[i] + if c == open_ch: + depth += 1 + elif c == close_ch: + depth -= 1 + if depth == 0: + return i + i += 1 + return None + + +def find_resourcedoc_blocks(source: str): + """Yield (header_start, body_start, body_end, body_text) for each + `(static)?[rR]esourceDocs += ResourceDoc(...)` call.""" + pattern = re.compile( + r"(?:static)?[rR]esourceDocs\s*\+=\s*ResourceDoc\s*\(" + ) + i = 0 + while True: + m = pattern.search(source, i) + if not m: + return + body_start = m.end() + close = scan_matching_close(source, body_start, "(", ")") + if close is None: + i = body_start + continue + yield m.start(), body_start, close, source[body_start:close] + i = close + 1 + + +def split_top_level_args(body: str): + """Return list of (start, end, text).""" + out = [] + depth = 0 + cur = 0 + i = 0 + n = len(body) + while i < n: + skipped = _skip_string_or_comment(body, i) + if skipped is not None: + i = skipped + continue + c = body[i] + if c in "([{": + depth += 1 + elif c in ")]}": + depth -= 1 + elif c == "," and depth == 0: + out.append((cur, i, body[cur:i])) + cur = i + 1 + i += 1 + tail = body[cur:] + if tail.strip(): + out.append((cur, n, tail)) + return out + + +def split_named(arg_text: str): + """Return (name, value_text_with_leading_ws_stripped) for named args, or + (None, arg_text) for positional.""" + m = re.match(r"^(\s*)([A-Za-z_][A-Za-z0-9_]*)(\s*=\s*)", arg_text) + if m and m.group(2) in NAMED_ARG_FIELDS: + return m.group(2), arg_text[m.end():] + return None, arg_text + + +def parse_args(body: str): + """Return (positional, named). Each positional entry is {'start','end','text'}; + named entries map name -> value_text.""" + raw = split_top_level_args(body) + positional = [] + named = OrderedDict() + for start, end, text in raw: + name, val = split_named(text) + if name is None: + positional.append({"start": start, "end": end, "text": text.strip()}) + else: + named[name] = val.strip() + return positional, named + + +def endpoint_name(part_fn_name: str) -> str: + """Like in check_lift_http4s_resource_doc_parity.py: also evaluate + chained `.replace("a", "b")` calls so derived names match.""" + s = (part_fn_name or "").strip() + rest = s + m = re.match(r"^nameOf\s*\(\s*([A-Za-z_][A-Za-z0-9_]*)\s*\)", s) + if m: + base = m.group(1) + rest = s[m.end():] + elif s.startswith('"'): + m2 = re.match(r'^"([^"]*)"', s) + if not m2: + return s + base = m2.group(1) + rest = s[m2.end():] + else: + return s + rep_re = re.compile(r'^\s*\.\s*replace\s*\(\s*"([^"]*)"\s*,\s*"([^"]*)"\s*\)') + while True: + m = rep_re.match(rest) + if not m: + break + base = base.replace(m.group(1), m.group(2)) + rest = rest[m.end():] + return base + + +def find_pair_for_version(version_dir: Path): + api_methods = list(version_dir.glob("APIMethods*.scala")) + http4s = list(version_dir.glob("Http4s*.scala")) + if not api_methods or not http4s: + return None + def pick(files, prefix): + ranked = sorted( + files, + key=lambda p: ( + 0 if re.match(rf"^{prefix}\d+\.scala$", p.name) else 1, + len(p.name), + ), + ) + return ranked[0] + return pick(api_methods, "APIMethods"), pick(http4s, "Http4s") + + +def collect_lift_docs(path: Path): + """Return {endpoint_name -> {field_name -> raw_text}}.""" + source = path.read_text(encoding="utf-8") + active = re.search(r"^\s*(?:static)?[rR]esourceDocs\s*\+=\s*ResourceDoc\s*\(", source, re.M) + commented = re.search(r"^\s*//\s*(?:static)?[rR]esourceDocs\s*\+=\s*ResourceDoc\s*\(", source, re.M) + if not active and commented: + source = uncomment(source) + docs = OrderedDict() + for _, _, _, body in find_resourcedoc_blocks(source): + positional, _ = parse_args(body) + if len(positional) < 3: + continue + name = endpoint_name(positional[2]["text"]) + field_values = OrderedDict() + for idx, fname in enumerate(POSITIONAL_FIELDS): + if idx < len(positional): + field_values[fname] = positional[idx]["text"] + docs.setdefault(name, field_values) + return docs + + +def reindent_value(value_text: str, target_indent: str) -> str: + """Re-indent multi-line value so subsequent lines start at target_indent.""" + if "\n" not in value_text: + return value_text + lines = value_text.split("\n") + min_indent = None + for line in lines[1:]: + if line.strip(): + il = len(line) - len(line.lstrip()) + if min_indent is None or il < min_indent: + min_indent = il + if min_indent is None: + min_indent = 0 + out = [lines[0]] + for line in lines[1:]: + if not line.strip(): + out.append("") + continue + stripped = line[min_indent:] if len(line) >= min_indent else line.lstrip() + out.append(target_indent + stripped) + return "\n".join(out) + + +def normalize_for_diff(s: str, fname: str) -> str: + """Normalize a value just enough to detect 'no semantic change'. Mirrors + what scripts/check_lift_http4s_resource_doc_parity.py does so we don't + rewrite when the audit would already consider them equal.""" + if s is None: + return "" + t = s + if fname in ("errorResponseBodies", "tags"): + # `X :: Y :: Nil` vs `List(X, Y)`: extract items and sort/join. + m = re.match(r"^\s*List\s*\(\s*(.*?)\s*\)\s*$", t, re.S) + if m: + items = [x.strip() for _, _, x in split_top_level_args(m.group(1))] + else: + parts = [p.strip() for p in re.split(r"::", t)] + items = [p for p in parts if p and p != "Nil"] + return "[" + ",".join(re.sub(r"\s+", "", x) for x in items) + "]" + # Drop leading `s` of `s"""..."""` and the `.stripMargin` postfix; strip + # leading `|` markers; collapse whitespace. + t = re.sub(r"\bs(\"\"\")", r"\1", t) + t = re.sub(r"\.stripMargin\b", "", t) + t = re.sub(r"^\s*\|", "", t, flags=re.M) + t = re.sub(r"\s+", "", t) + return t + + +def render_new_body(positional, named, new_field_text, fname_to_replace, + field_indent): + """Render the body with the named field replaced by new_field_text. + + Other positional args keep their original text exactly; named args also + kept verbatim. Layout: positional 0..3 on one line, 4..5 on next, 6+ one + per line. + """ + pos_texts = [] + for idx, fname in enumerate(POSITIONAL_FIELDS): + if idx >= len(positional): + break + if fname == fname_to_replace: + pos_texts.append(new_field_text) + else: + pos_texts.append(positional[idx]["text"]) + for idx in range(len(POSITIONAL_FIELDS), len(positional)): + pos_texts.append(positional[idx]["text"]) + + named_texts = [f"{n} = {v}" for n, v in named.items()] + + lines = [] + if len(pos_texts) >= 5: + lines.append(", ".join(pos_texts[0:4]) + ",") + line5 = pos_texts[4] + ("," if len(pos_texts) > 5 or named_texts else "") + if len(pos_texts) > 5: + line5 = pos_texts[4] + ", " + pos_texts[5] + "," + for v in pos_texts[6:]: + lines.append(line5) + line5 = v + "," + lines.append(line5) + else: + for v in pos_texts: + lines.append(v + ",") + for nt in named_texts: + lines.append(nt + ",") + if lines: + last = lines[-1] + if last.endswith(","): + lines[-1] = last[:-1] + + body = ("\n" + field_indent).join(lines) + return "\n" + field_indent + body + ")" + + +def process_file(http4s_path: Path, lift_docs: dict, dry_run: bool, + fields_to_restore, only_names=None): + """Surgical per-field replacement: for each field that needs to change, + locate its substring in the body and swap it in-place. Other fields are + untouched (preserves existing layout & minimises diff).""" + source = http4s_path.read_text(encoding="utf-8") + # (abs_start_in_file, abs_end_in_file, new_substring) + edits = [] + matched = 0 + skipped_no_lift = 0 + changed_endpoints = 0 + per_field_changes = {f: 0 for f in fields_to_restore} + + for header_start, body_start, body_end, body in find_resourcedoc_blocks(source): + positional, _ = parse_args(body) + if len(positional) < 3: + continue + name = endpoint_name(positional[2]["text"]) + if only_names and name not in only_names: + continue + lift = lift_docs.get(name) + if lift is None: + skipped_no_lift += 1 + continue + matched += 1 + + # Determine field indent from the FIRST line break we find before the + # current text of any positional field — fall back to " ". + # (Field indent only matters when the new value has embedded newlines.) + endpoint_changed = False + for fname in fields_to_restore: + idx = POSITIONAL_FIELDS.index(fname) + if idx >= len(positional): + continue + if fname not in lift: + continue + cur_text = positional[idx]["text"] + new_text = lift[fname].strip() + if normalize_for_diff(cur_text, fname) == normalize_for_diff(new_text, fname): + continue + + # Compute absolute file offsets of the substring to replace — + # preserve leading whitespace from the original arg slot. + arg_slice = body[positional[idx]["start"]:positional[idx]["end"]] + leading_ws_len = len(arg_slice) - len(arg_slice.lstrip()) + value_start_abs = body_start + positional[idx]["start"] + leading_ws_len + value_end_abs = body_start + positional[idx]["end"] + + # Re-indent multi-line new value to align with the leading + # whitespace context — derive target_indent from the line on + # which the original value starts. + line_start = source.rfind("\n", 0, value_start_abs) + 1 + target_indent = source[line_start:value_start_abs] + # If target_indent is all whitespace, use it verbatim. Else fall + # back to a sensible default (8 spaces). + if target_indent.strip(): + target_indent = " " + new_text_reindented = reindent_value(new_text, target_indent) + + edits.append((value_start_abs, value_end_abs, new_text_reindented)) + per_field_changes[fname] += 1 + endpoint_changed = True + + if endpoint_changed: + changed_endpoints += 1 + + new_source = source + for start, end, txt in sorted(edits, key=lambda e: -e[0]): + new_source = new_source[:start] + txt + new_source[end:] + + prefix = "[dry-run] " if dry_run else "" + print(f" {prefix}endpoints matched: {matched} changed: {changed_endpoints} " + f"skipped (no lift match): {skipped_no_lift}") + for f in fields_to_restore: + print(f" {f}: {per_field_changes[f]} fields rewritten") + + if not dry_run and changed_endpoints: + http4s_path.write_text(new_source, encoding="utf-8") + print(f" wrote {http4s_path}") + + +def main(): + ap = argparse.ArgumentParser(description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter) + ap.add_argument("versions", nargs="+", help="Versions like v6_0_0") + ap.add_argument("--dry-run", action="store_true", + help="Report intended changes without writing files") + ap.add_argument("--only", action="append", default=None, + help="Restrict to endpoint name(s). Repeatable.") + ap.add_argument("--fields", default=None, + help=f"Comma-separated list of fields to restore. " + f"Default: {','.join(DEFAULT_FIELDS_TO_RESTORE)}") + args = ap.parse_args() + + fields_to_restore = (args.fields.split(",") if args.fields + else DEFAULT_FIELDS_TO_RESTORE) + invalid = [f for f in fields_to_restore if f not in POSITIONAL_FIELDS] + if invalid: + print(f"ERROR: unknown fields: {invalid}", file=sys.stderr) + sys.exit(2) + + for v in args.versions: + version_dir = API_ROOT / v + if not version_dir.is_dir(): + print(f"[skip] {v}: no such directory under {API_ROOT}", + file=sys.stderr) + continue + pair = find_pair_for_version(version_dir) + if not pair: + print(f"[skip] {v}: no APIMethods+Http4s pair found", + file=sys.stderr) + continue + lift_path, http4s_path = pair + print(f"\n=== {v} ===") + print(f" lift: {lift_path.relative_to(REPO_ROOT)}") + print(f" http4s: {http4s_path.relative_to(REPO_ROOT)}") + print(f" fields: {fields_to_restore}") + lift_docs = collect_lift_docs(lift_path) + process_file(http4s_path, lift_docs, args.dry_run, + fields_to_restore, set(args.only) if args.only else None) + + +if __name__ == "__main__": + main()