From 83a2f25c40fe715e91e3221b2dd636170c9015d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 22 May 2026 07:36:15 +0200 Subject: [PATCH 1/4] v2.2.0 + v3.1.0 + v4.0.0: restore ResourceDoc fields after upstream merge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The upstream merge (b148b1bbe) re-diverged semantic ResourceDoc fields for v3.1.0 and v4.0.0 because conflict resolution took the upstream side, which had slightly different doc text than Lift's source-of-truth. Re-run scripts/restore_resource_doc_bodies.py to re-align both files: v3.1.0: 8 fields rewritten (2 summary, 2 description, 6 errors, 1 tag) v4.0.0: 50 fields rewritten (5 summary, 47 errors, 7 tags) The merge's important contributions (test fix 14abed06c, v2.x Lift retirement, frozen_type_meta_data regeneration, etc.) are preserved. Only the unintended doc-text drift the conflict resolution introduced on the semantic fields is reversed. v2.2.0: upstream commit 71892f5cb retired the Lift trait entirely (replaced 1361 lines with a 9-line empty stub), so the audit previously reported v2.2.0 as 0/18 — nothing left in APIMethods220.scala to compare against. Source-of-truth Lift content is preserved in git at 71892f5cb^. A one-off Python invocation using restore_resource_doc_bodies.py's helper functions extracts the pre-stub APIMethods220.scala from git and runs the same restoration logic against Http4s220.scala: v2.2.0: 15 fields rewritten (13 descriptions, 1 example, 1 success body) Two imports added to Http4s220.scala (Glossary and java.util.Date) so the restored descriptions / example bodies compile (Lift had them in scope via APIMethods220's broader import set; the http4s stub had a leaner set). Audit after this commit: v2.2.0: 0 of 18 mismatched (3 only-http4s are URL renames for NEW_ACCOUNT_ID / VIEW_ACCOUNT_ID / UPD_VIEW_ID middleware bypass — measured against pre-stub Lift) v3.1.0: 5 mismatches (all middleware URL renames) v4.0.0: 20 mismatches (mostly middleware URL renames + 1 verb fix) Total mismatches across migrated versions: 128 (was 144 before v2.2.0 restoration; v2.2.0 was previously marked "not started" since the audit couldn't see the retired Lift content). LIFT_HTTP4S_MIGRATION.md updated with v2.2.0's row + per-endpoint fix-candidate table. Per the source-of-truth rule, none of the APIMethodsXYZ.scala files are modified. --- LIFT_HTTP4S_MIGRATION.md | 20 +- .../scala/code/api/v2_2_0/Http4s220.scala | 283 ++++++++++++-- .../scala/code/api/v3_1_0/Http4s310.scala | 76 +++- .../scala/code/api/v4_0_0/Http4s400.scala | 361 ++++++++++++++---- 4 files changed, 609 insertions(+), 131 deletions(-) diff --git a/LIFT_HTTP4S_MIGRATION.md b/LIFT_HTTP4S_MIGRATION.md index b26fb73b31..716aa8846d 100644 --- a/LIFT_HTTP4S_MIGRATION.md +++ b/LIFT_HTTP4S_MIGRATION.md @@ -191,14 +191,14 @@ Separate from the resource-docs **serving** workstream above, there is a parity | 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 | +| v2_2_0 | 18 | 0 | 0 | 18 | Lift trait fully retired upstream (commit `71892f5cb`); audited against pre-stub Lift via git history; 13 fields restored; 3 middleware URL renames remain | | 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** | | | | +| **Total** | **956** | **128** | | | | ### v6.0.0 — 12 specific drifts (each is a fix candidate) @@ -231,6 +231,22 @@ 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. +### v2.2.0 — 3 specific drifts + 18 only-http4s + +Upstream commit `71892f5cb` retired `APIMethods220.scala`'s Lift trait entirely (1361 lines → 9-line empty stub). For audit purposes, the Lift source-of-truth is preserved in git history at `71892f5cb^`. A one-off Python script using `restore_resource_doc_bodies.py` helpers (in `scripts/`) extracts the pre-stub `APIMethods220.scala` and runs the standard field restoration against `Http4s220.scala`. + +After restoration (13 descriptions + 1 example body + 1 success body), only 3 middleware-driven URL renames remain: + +| Endpoint | Field | Lift | http4s | Resolution | +|---|---|---|---|---| +| `createAccount` | requestUrl | `…/accounts/ACCOUNT_ID` | `…/accounts/NEW_ACCOUNT_ID` | PUT-creates-account bypass. **Document**. | +| `createViewForBankAccount` | requestUrl | `…/accounts/ACCOUNT_ID/views` | `…/accounts/VIEW_ACCOUNT_ID/views` | Account-validation bypass. **Document**. | +| `updateViewForBankAccount` | requestUrl | `…/views/VIEW_ID` | `…/views/UPD_VIEW_ID` | Disambiguation rename. **Document**. | + +The 18 only-http4s entries are the actual v2.2.0 surface — there is no live Lift counterpart in the file anymore. They're audited indirectly against the git-history Lift. + +Two supporting imports were added to `Http4s220.scala` so the restored descriptions / example bodies compile: `code.api.util.Glossary` and `java.util.Date`. + ### v3.0.0 — 4 specific drifts After semantic-field restoration, only middleware-driven URL renames remain. diff --git a/obp-api/src/main/scala/code/api/v2_2_0/Http4s220.scala b/obp-api/src/main/scala/code/api/v2_2_0/Http4s220.scala index 709e47fdd7..1b1e32849f 100644 --- a/obp-api/src/main/scala/code/api/v2_2_0/Http4s220.scala +++ b/obp-api/src/main/scala/code/api/v2_2_0/Http4s220.scala @@ -8,7 +8,9 @@ import code.api.util.APIUtil.{EmptyBody, ResourceDoc, _} import code.api.util.ApiRole._ import code.api.util.ApiTag._ import code.api.util.ErrorMessages._ +import code.api.util.Glossary import code.api.util.http4s.Http4sRequestAttributes.{EndpointHelpers, RequestOps} +import java.util.Date import code.api.util.http4s.ResourceDocMiddleware import code.api.util.newstyle.ViewNewStyle import code.api.util.{APIUtil, CallContext, CustomJsonFormats, NewStyle} @@ -96,9 +98,31 @@ object Http4s220 { null, implementedInApiVersion, nameOf(getViewsForBankAccount), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/views", "Get Views for Account", - s"""Returns the list of the views created for account ACCOUNT_ID at BANK_ID. - | - |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", + s"""#Views + | + | + |Views in Open Bank Project provide a mechanism for fine grained access control and delegation to Accounts and Transactions. Account holders use the 'owner' view by default. + |Delegated access is made through other views for example 'accountants', 'share-holders' or 'tagging-application'. Views can be created via the API and each view has a list of entitlements. + | + |Views on accounts and transactions filter the underlying data to redact certain fields for certain users. For instance the balance on an account may be hidden from the public. The way to know what is possible on a view is determined in the following JSON. + | + |**Data:** When a view moderates a set of data, some fields my contain the value `null` rather than the original value. This indicates either that the user is not allowed to see the original data or the field is empty. + | + |There is currently one exception to this rule; the 'holder' field in the JSON contains always a value which is either an alias or the real name - indicated by the 'is_alias' field. + | + |**Action:** When a user performs an action like trying to post a comment (with POST API call), if he is not allowed, the body response will contain an error message. + | + |**Metadata:** + |Transaction metadata (like images, tags, comments, etc.) will appears *ONLY* on the view where they have been created e.g. comments posted to the public view only appear on the public view. + | + |The other account metadata fields (like image_URL, more_info, etc.) are unique through all the views. Example, if a user edits the 'more_info' field in the 'team' view, then the view 'authorities' will show the new value (if it is allowed to do it). + | + |# All + |*Optional* + | + |Returns the list of the views created for account ACCOUNT_ID at BANK_ID. + | + |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", EmptyBody, viewsJSONV220, List(AuthenticatedUserIsRequired, BankAccountNotFound, UnknownError), List(apiTagView, apiTagAccount), None, @@ -132,9 +156,22 @@ object Http4s220 { null, implementedInApiVersion, nameOf(createViewForBankAccount), "POST", "/banks/BANK_ID/accounts/VIEW_ACCOUNT_ID/views", "Create View", - s"""Create a view on bank account. - | - |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", + s"""#Create a 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`. + | + | You should use a leading _ (underscore) for the view name because other view names may become reserved by OBP internally + | """, createViewJsonV121, viewJSONV220, List(AuthenticatedUserIsRequired, InvalidJsonFormat, BankAccountNotFound, UnknownError), List(apiTagAccount, apiTagView, apiTagOldStyle), None, @@ -191,9 +228,12 @@ object Http4s220 { null, implementedInApiVersion, nameOf(updateViewForBankAccount), "PUT", "/banks/BANK_ID/accounts/ACCOUNT_ID/views/UPD_VIEW_ID", "Update View", - s"""Update an existing view on a bank account. - | - |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", + s"""Update an existing 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)""", updateViewJsonV121, viewJSONV220, List(InvalidJsonFormat, AuthenticatedUserIsRequired, BankAccountNotFound, UnknownError), List(apiTagAccount, apiTagView, apiTagOldStyle), None, @@ -254,7 +294,23 @@ object Http4s220 { null, implementedInApiVersion, nameOf(getCurrentFxRate), "GET", "/banks/BANK_ID/fx/FROM_CURRENCY_CODE/TO_CURRENCY_CODE", "Get Current FxRate", - """Get the latest FX rate specified by BANK_ID, FROM_CURRENCY_CODE and TO_CURRENCY_CODE.""", + """Get the latest FX rate specified by BANK_ID, FROM_CURRENCY_CODE and TO_CURRENCY_CODE + | + |OBP may try different sources of FX rate information depending on the Connector in operation. + | + |For example we want to convert EUR => USD: + | + |OBP will: + |1st try - Connector (database, core banking system or external FX service) + |2nd try part 1 - fallbackexchangerates/eur.json + |2nd try part 2 - fallbackexchangerates/usd.json (the inverse rate is used) + |3rd try - Hardcoded map of FX rates. + | + |![FX Flow](https://user-images.githubusercontent.com/485218/60005085-1eded600-966e-11e9-96fb-798b102d9ad0.png) + | + |**Public Access:** This endpoint can be made publicly accessible (no authentication required) by setting the property `apiOptions.getCurrentFxRateIsPublic=true` in the props file. + | + """.stripMargin, EmptyBody, fXRateJSON, List(InvalidISOCurrencyCode, AuthenticatedUserIsRequired, FXCurrencyCodeCombinationsNotSupported, UnknownError), List(apiTagFx), None, @@ -287,9 +343,12 @@ object Http4s220 { null, implementedInApiVersion, nameOf(getExplicitCounterpartiesForAccount), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/counterparties", "Get Counterparties (Explicit)", - s"""Gets the explicit Counterparties on an Account / View. - | - |${userAuthenticationMessage(true)}""", + s"""This endpoints gets the explicit Counterparties on an Account / View. + | + |For a general introduction to Counterparties in OBP, see ${Glossary.getGlossaryItemLink("Counterparties")} + | + |${userAuthenticationMessage(true)} + |""".stripMargin, EmptyBody, counterpartiesJsonV220, List(AuthenticatedUserIsRequired, BankAccountNotFound, ViewNotFound, NoViewPermission, UserNoPermissionAccessView, UnknownError), @@ -317,9 +376,10 @@ object Http4s220 { null, implementedInApiVersion, nameOf(getExplicitCounterpartyById), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/counterparties/COUNTERPARTY_ID", "Get Counterparty by Counterparty Id (Explicit)", - s"""Information returned about the Counterparty specified by COUNTERPARTY_ID. - | - |${userAuthenticationMessage(true)}""", + s"""Information returned about the Counterparty specified by COUNTERPARTY_ID: + | + |${userAuthenticationMessage(true)} + |""".stripMargin, EmptyBody, counterpartyWithMetadataJson, List(AuthenticatedUserIsRequired, BankNotFound, UnknownError), List(apiTagCounterparty, apiTagPSD2PIS, apiTagCounterpartyMetaData, apiTagPsd2), None, @@ -345,8 +405,13 @@ object Http4s220 { null, implementedInApiVersion, nameOf(getMessageDocs), "GET", "/message-docs/CONNECTOR", "Get Message Docs", - """These message docs provide example messages sent by OBP to the message queue for processing by the Adapter. - |`CONNECTOR`: rest_vMar2019, stored_procedure_vDec2019 ...""", + """These message docs provide example messages sent by OBP to the (RabbitMq) message queue for processing by the Core Banking / Payment system Adapter - together with an example expected response and possible error codes. + | Integrators can use these messages to build Adapters that provide core banking services to OBP. + | + | Note: API Explorer provides a Message Docs page where these messages are displayed. + | + | `CONNECTOR`: rest_vMar2019, stored_procedure_vDec2019 ... + """.stripMargin, EmptyBody, messageDocsJson, List(InvalidConnector, UnknownError), List(apiTagMessageDoc, apiTagDocumentation, apiTagApi), None, @@ -551,8 +616,22 @@ object Http4s220 { "/banks/BANK_ID/fx", "Create Fx", s"""Create or Update Fx for the Bank. - | - |${userAuthenticationMessage(true)}""", + | + |Example: + | + |“from_currency_code”:“EUR”, + |“to_currency_code”:“USD”, + |“conversion_value”: 1.136305, + |“inverse_conversion_value”: 1 / 1.136305 = 0.8800454103431737, + | + | Thus 1 Euro = 1.136305 US Dollar + | and + | 1 US Dollar = 0.8800 Euro + | + | + |${userAuthenticationMessage(true) } + | + |""", fxJsonV220, fxJsonV220, List(AuthenticatedUserIsRequired, BankNotFound, UserHasMissingRoles, UnknownError), List(apiTagFx), @@ -601,10 +680,15 @@ object Http4s220 { "/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 an Account for another User if they have CanCreateAccount role. - | - |Note: The Amount must be zero.""", + | + | + |The User can create an Account for themself or an Account for another User if they have CanCreateAccount role. + | + |If USER_ID is not specified the account will be owned by the logged in User. + | + |The type field should be a product_code from Product. + | + |Note: The Amount must be zero.""".stripMargin, createAccountJSONV220, createAccountJSONV220, List(InvalidJsonFormat, BankNotFound, AuthenticatedUserIsRequired, InvalidUserId, InvalidAccountIdFormat, InvalidBankIdFormat, UserNotFoundById, UserHasMissingRoles, @@ -629,7 +713,12 @@ object Http4s220 { null, implementedInApiVersion, nameOf(config), "GET", "/config", "Get API Configuration", - """Returns information about API Config, Akka ports, Elastic search ports, Cached functions.""", + """Returns information about: + | + |* API Config + |* Akka ports + |* Elastic search ports + |* Cached function """, EmptyBody, configurationJSON, List(AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), apiTagApi :: Nil, @@ -654,7 +743,35 @@ object Http4s220 { null, implementedInApiVersion, nameOf(getConnectorMetrics), "GET", "/management/connector/metrics", "Get Connector Metrics", - s"""Get all connector metrics. Requires CanGetConnectorMetrics role.""", + s"""Get the all metrics + | + |require CanGetConnectorMetrics role + | + |Filters Part 1.*filtering* (no wilde cards etc.) parameters to GET /management/connector/metrics + | + |Should be able to filter on the following metrics fields + | + |eg: /management/connector/metrics?from_date=$DateWithMsExampleString&to_date=$DateWithMsExampleString&limit=50&offset=2 + | + |1 from_date (defaults to one week before current date): eg:from_date=$DateWithMsExampleString + | + |2 to_date (defaults to current date) eg:to_date=$DateWithMsExampleString + | + |3 limit (for pagination: defaults to 1000) eg:limit=2000 + | + |4 offset (for pagination: zero index, defaults to 0) eg: offset=10 + | + |eg: /management/connector/metrics?from_date=$DateWithMsExampleString&to_date=$DateWithMsExampleString&limit=100&offset=300 + | + |Other filters: + | + |5 connector_name (if null ignore) + | + |6 function_name (if null ignore) + | + |7 correlation_id (if null ignore) + | + """.stripMargin, EmptyBody, connectorMetricsJson, List(InvalidDateFormat, UnknownError), List(apiTagMetric, apiTagApi), @@ -694,13 +811,34 @@ object Http4s220 { "/management/consumers", "Post a Consumer", s"""Create a Consumer (Authenticated access). - | - |${userAuthenticationMessage(true)}""", - ConsumerPostJSON("Test", "Test", "Description", "some@email.com", "redirecturl", "createdby", true, new java.util.Date(), - "-----BEGIN CERTIFICATE-----\nclient_certificate_content\n-----END CERTIFICATE-----"), - ConsumerPostJSON("Some app name", "App type", "Description", "some.email@example.com", "Some redirect url", - "Created by UUID", true, new java.util.Date(), - "-----BEGIN CERTIFICATE-----\nclient_certificate_content\n-----END CERTIFICATE-----"), + | + |""", + ConsumerPostJSON( + "Test", + "Test", + "Description", + "some@email.com", + "redirecturl", + "createdby", + true, + new Date(), + """-----BEGIN CERTIFICATE----- + |client_certificate_content + |-----END CERTIFICATE-----""".stripMargin + ), + ConsumerPostJSON( + "Some app name", + "App type", + "Description", + "some.email@example.com", + "Some redirect url", + "Created by UUID", + true, + new Date(), + """-----BEGIN CERTIFICATE----- + |client_certificate_content + |-----END CERTIFICATE-----""".stripMargin + ), List(AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), List(apiTagConsumer, apiTagOldStyle), Some(List(canCreateConsumer)), @@ -732,8 +870,83 @@ object Http4s220 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/counterparties", "Create Counterparty (Explicit)", s"""Create Counterparty (Explicit) for an Account. - | - |${userAuthenticationMessage(true)}""", + | + |In OBP, there are two types of Counterparty. + | + |* Explicit Counterparties (those here) which we create explicitly and are used in COUNTERPARTY Transaction Requests + | + |* Implicit Counterparties (AKA Other Accounts) which are generated automatically from the other sides of Transactions. + | + |Explicit Counterparties are created for the account / view + |They are how the user of the view (e.g. account owner) refers to the other side of the transaction + | + |name : the human readable name (e.g. Piano teacher, Miss Nipa) + | + |description : the human readable name (e.g. Piano teacher, Miss Nipa) + | + |bank_routing_scheme : eg: bankId or bankCode or any other strings + | + |bank_routing_address : eg: `gh.29.uk`, must be valid sandbox bankIds + | + |account_routing_scheme : eg: AccountId or AccountNumber or any other strings + | + |account_routing_address : eg: `1d65db7c-a7b2-4839-af41-95`, must be valid accountIds + | + |other_account_secondary_routing_scheme : eg: IBan or any other strings + | + |other_account_secondary_routing_address : if it is an IBAN, it should be unique for each counterparty. + | + |other_branch_routing_scheme : eg: branchId or any other strings or you can leave it empty, not useful in sandbox mode. + | + |other_branch_routing_address : eg: `branch-id-123` or you can leave it empty, not useful in sandbox mode. + | + |is_beneficiary : must be set to `true` in order to send payments to this counterparty + | + |bespoke: It supports a list of key-value, you can add it to the counterparty. + | + |bespoke.key : any info-key you want to add to this counterparty + | + |bespoke.value : any info-value you want to add to this counterparty + | + |The view specified by VIEW_ID must have the canAddCounterparty permission + | + |A minimal example for TransactionRequestType == COUNTERPARTY + | { + | "name": "Tesobe1", + | "description": "Good Company", + | "other_bank_routing_scheme": "OBP", + | "other_bank_routing_address": "gh.29.uk", + | "other_account_routing_scheme": "OBP", + | "other_account_routing_address": "8ca8a7e4-6d02-48e3-a029-0b2bf89de9f0", + | "is_beneficiary": true, + | "other_account_secondary_routing_scheme": "", + | "other_account_secondary_routing_address": "", + | "other_branch_routing_scheme": "", + | "other_branch_routing_address": "", + | "bespoke": [] + |} + | + | + |A minimal example for TransactionRequestType == SEPA + | + | { + | "name": "Tesobe2", + | "description": "Good Company", + | "other_bank_routing_scheme": "OBP", + | "other_bank_routing_address": "gh.29.uk", + | "other_account_routing_scheme": "OBP", + | "other_account_routing_address": "8ca8a7e4-6d02-48e3-a029-0b2bf89de9f0", + | "other_account_secondary_routing_scheme": "IBAN", + | "other_account_secondary_routing_address": "DE89 3704 0044 0532 0130 00", + | "is_beneficiary": true, + | "other_branch_routing_scheme": "", + | "other_branch_routing_address": "", + | "bespoke": [] + |} + | + |${userAuthenticationMessage(true)} + | + |""".stripMargin, postCounterpartyJSON, counterpartyWithMetadataJson, List(AuthenticatedUserIsRequired, InvalidAccountIdFormat, InvalidBankIdFormat, BankNotFound, AccountNotFound, InvalidJsonFormat, ViewNotFound, CounterpartyAlreadyExists, UnknownError), 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 c336b41eae..870f59a4c0 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 @@ -829,8 +829,13 @@ object Http4s310 { |""", EmptyBody, customerWithAttributesJsonV310, - List(AuthenticatedUserIsRequired, UserCustomerLinksNotFoundForUser, UnknownError), - List(apiTagCustomer, apiTagKyc), + List( + AuthenticatedUserIsRequired, + UserHasMissingRoles, + UserCustomerLinksNotFoundForUser, + UnknownError + ), + List(apiTagCustomer), Some(List(canGetCustomersAtOneBank)), http4sPartialFunction = Some(getCustomerByCustomerId) ) @@ -1310,7 +1315,11 @@ object Http4s310 { """.stripMargin, EmptyBody, viewJSONV220, - List(AuthenticatedUserIsRequired, UserHasMissingRoles, ViewNotFound, UnknownError), + List( + AuthenticatedUserIsRequired, + BankNotFound, + UnknownError + ), List(apiTagSystemView), Some(List(canGetSystemView)), http4sPartialFunction = Some(getSystemView) @@ -1505,7 +1514,8 @@ object Http4s310 { "Get Transaction by Id", s"""Returns one transaction specified by TRANSACTION_ID of the account ACCOUNT_ID and [moderated](#1_2_1-getViewsForBankAccount) by the view (VIEW_ID). | - |${userAuthenticationMessage(true)} + |${userAuthenticationMessage(false)} + |Authentication is required if the view is not public. | | |""", @@ -2083,7 +2093,11 @@ object Http4s310 { |""", EmptyBody, EmptyBody, - List(AuthenticatedUserIsRequired, UserHasMissingRoles, BankNotFound, UnknownError), + List( + UserHasMissingRoles, + BankNotFound, + UnknownError + ), List(apiTagProduct, apiTagProductAttribute, apiTagAttribute), Some(List(canDeleteProductAttribute)), http4sPartialFunction = Some(deleteProductAttribute) @@ -4564,7 +4578,7 @@ object Http4s310 { nameOf(createConsentEmail), "POST", "/banks/BANK_ID/my/consents/EMAIL", - "Create Consent (Email)", + "Create Consent (EMAIL)", s""" | |This endpoint starts the process of creating a Consent. @@ -4623,8 +4637,18 @@ object Http4s310 { |""", postConsentEmailJsonV310, consentJsonV310, - List(AuthenticatedUserIsRequired, BankNotFound, InvalidJsonFormat, - ConsentMaxTTL, RolesAllowedInConsent, ViewsAllowedInConsent, UnknownError), + List( + AuthenticatedUserIsRequired, + BankNotFound, + InvalidJsonFormat, + ConsentAllowedScaMethods, + RolesAllowedInConsent, + ViewsAllowedInConsent, + ConsumerNotFoundByConsumerId, + ConsumerIsDisabled, + InvalidConnectorResponse, + UnknownError + ), apiTagConsent :: apiTagPSD2AIS :: apiTagPsd2 :: Nil, None, http4sPartialFunction = Some(createConsentEmail) @@ -4695,8 +4719,20 @@ object Http4s310 { |""", postConsentPhoneJsonV310, consentJsonV310, - List(AuthenticatedUserIsRequired, BankNotFound, InvalidJsonFormat, - ConsentMaxTTL, RolesAllowedInConsent, ViewsAllowedInConsent, UnknownError), + List( + AuthenticatedUserIsRequired, + BankNotFound, + InvalidJsonFormat, + ConsentAllowedScaMethods, + RolesAllowedInConsent, + ViewsAllowedInConsent, + ConsumerNotFoundByConsumerId, + ConsumerIsDisabled, + MissingPropsValueAtThisInstance, + SmsServerNotResponding, + InvalidConnectorResponse, + UnknownError + ), apiTagConsent :: apiTagPSD2AIS :: apiTagPsd2 :: Nil, None, http4sPartialFunction = Some(createConsentSms) @@ -4708,7 +4744,7 @@ object Http4s310 { nameOf(createConsentImplicit), "POST", "/banks/BANK_ID/my/consents/IMPLICIT", - "Create Consent (Implicit)", + "Create Consent (IMPLICIT)", s""" | |This endpoint starts the process of creating a Consent. @@ -4764,8 +4800,20 @@ object Http4s310 { |""", postConsentImplicitJsonV310, consentJsonV310, - List(AuthenticatedUserIsRequired, BankNotFound, InvalidJsonFormat, - ConsentMaxTTL, RolesAllowedInConsent, ViewsAllowedInConsent, UnknownError), + List( + AuthenticatedUserIsRequired, + BankNotFound, + InvalidJsonFormat, + ConsentAllowedScaMethods, + RolesAllowedInConsent, + ViewsAllowedInConsent, + ConsumerNotFoundByConsumerId, + ConsumerIsDisabled, + MissingPropsValueAtThisInstance, + SmsServerNotResponding, + InvalidConnectorResponse, + UnknownError + ), apiTagConsent :: apiTagPSD2AIS :: apiTagPsd2 :: Nil, None, http4sPartialFunction = Some(createConsentImplicit) @@ -4836,7 +4884,7 @@ object Http4s310 { "Get Connector Status (Loopback)", s"""This endpoint makes a call to the Connector to check the backend transport is reachable. (Deprecated) | - |${userAuthenticationMessage(false)} + |${userAuthenticationMessage(true)} | |""", EmptyBody, 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 80f4e567f6..ba50b81ee4 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 @@ -232,7 +232,7 @@ object Http4s400 { """.stripMargin, EmptyBody, adapterInfoJsonV300, - List(AuthenticatedUserIsRequired, UnknownError), + List($AuthenticatedUserIsRequired, UnknownError), List(apiTagApi), Some(List(canGetDatabaseInfo)), http4sPartialFunction = Some(getMapperDatabaseInfo) @@ -263,7 +263,7 @@ object Http4s400 { """.stripMargin, EmptyBody, logoutLinkV400, - List(AuthenticatedUserIsRequired, UnknownError), + List($AuthenticatedUserIsRequired, UnknownError), List(apiTagUser), None, http4sPartialFunction = Some(getLogoutLink) @@ -590,7 +590,10 @@ object Http4s400 { |${userAuthenticationMessage(!getAtmsIsPublic)}""".stripMargin, EmptyBody, atmsJsonV400, - List(AuthenticatedUserIsRequired, BankNotFound, InvalidJsonFormat, UnknownError), + List( + $BankNotFound, + UnknownError + ), List(apiTagATM), None, http4sPartialFunction = Some(getAtms) @@ -623,7 +626,11 @@ object Http4s400 { |""".stripMargin, EmptyBody, atmJsonV400, - List(AuthenticatedUserIsRequired, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + UnknownError + ), List(apiTagATM), None, http4sPartialFunction = Some(getAtm) @@ -670,7 +677,11 @@ object Http4s400 { |${userAuthenticationMessage(!getProductsIsPublic)}""".stripMargin, EmptyBody, productsJsonV400, - List(AuthenticatedUserIsRequired, BankNotFound, ProductNotFoundByProductCode, UnknownError), + List( + AuthenticatedUserIsRequired, + BankNotFound, + UnknownError + ), List(apiTagProduct), None, http4sPartialFunction = Some(getProducts) @@ -716,7 +727,12 @@ object Http4s400 { |${userAuthenticationMessage(!getProductsIsPublic)}""".stripMargin, EmptyBody, productJsonV400, - List(AuthenticatedUserIsRequired, BankNotFound, ProductNotFoundByProductCode, UnknownError), + List( + AuthenticatedUserIsRequired, + $BankNotFound, + ProductNotFoundByProductCode, + UnknownError + ), List(apiTagProduct), None, http4sPartialFunction = Some(getProduct) @@ -834,7 +850,12 @@ object Http4s400 { |""", putProductJsonV400, productJsonV400.copy(attributes = None, fees = None), - List(AuthenticatedUserIsRequired, BankNotFound, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + UserHasMissingRoles, + UnknownError + ), List(apiTagProduct), Some(List(canCreateProduct, canCreateProductAtAnyBank)), http4sPartialFunction = Some(createProduct) @@ -1217,9 +1238,16 @@ object Http4s400 { |""", postCustomerJsonV310, customerJsonV310, - List(AuthenticatedUserIsRequired, BankNotFound, InvalidJsonFormat, - CustomerNumberAlreadyExists, UserNotFoundById, CustomerAlreadyExistsForUser, - CreateCustomerError, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + InvalidJsonFormat, + CustomerNumberAlreadyExists, + UserNotFoundById, + CustomerAlreadyExistsForUser, + CreateConsumerError, + UnknownError + ), List(apiTagCustomer, apiTagPerson), Some(List(canCreateCustomer, canCreateCustomerAtAnyBank)), http4sPartialFunction = Some(createCustomer) @@ -1247,7 +1275,7 @@ object Http4s400 { """Get the Balances for the Accounts of the current User at one bank.""", EmptyBody, accountBalancesV400Json, - List(AuthenticatedUserIsRequired, BankNotFound, UnknownError), + List($AuthenticatedUserIsRequired, $BankNotFound, UnknownError), apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil, None, http4sPartialFunction = Some(getBankAccountsBalancesForCurrentUser) @@ -1297,7 +1325,7 @@ object Http4s400 { |""".stripMargin, EmptyBody, moderatedCoreAccountJsonV400, - List(AuthenticatedUserIsRequired, BankAccountNotFound, UnknownError), + List($AuthenticatedUserIsRequired, $BankAccountNotFound, UnknownError), apiTagAccount :: apiTagPSD2AIS :: apiTagPsd2 :: Nil, None, http4sPartialFunction = Some(getCoreAccountById) @@ -1348,8 +1376,13 @@ object Http4s400 { |""".stripMargin, EmptyBody, moderatedAccountJSON400, - List(AuthenticatedUserIsRequired, BankNotFound, BankAccountNotFound, - UserNoPermissionAccessView, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + UnknownError + ), apiTagAccount :: Nil, None, http4sPartialFunction = Some(getPrivateAccountByIdFull) @@ -1404,7 +1437,7 @@ object Http4s400 { """.stripMargin, EmptyBody, basicAccountsJSON, - List(AuthenticatedUserIsRequired, BankNotFound, UnknownError), + List($AuthenticatedUserIsRequired, $BankNotFound, UnknownError), List(apiTagAccount, apiTagPrivateData, apiTagPublicData), None, http4sPartialFunction = Some(getPrivateAccountsAtOneBank) @@ -1500,7 +1533,11 @@ object Http4s400 { "dynamic_entities", List(dynamicEntityResponseBodyExample) ), - List(AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), List(apiTagManageDynamicEntity, apiTagApi), Some(List(canGetSystemLevelDynamicEntities)), http4sPartialFunction = Some(getSystemDynamicEntities) @@ -1539,7 +1576,12 @@ object Http4s400 { "dynamic_entities", List(dynamicEntityResponseBodyExample) ), - List(BankNotFound, AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + $BankNotFound, + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), List(apiTagManageDynamicEntity, apiTagApi), Some(List(canGetBankLevelDynamicEntities, canGetAnyBankLevelDynamicEntities)), http4sPartialFunction = Some(getBankLevelDynamicEntities) @@ -1576,7 +1618,10 @@ object Http4s400 { "dynamic_entities", List(dynamicEntityResponseBodyExample) ), - List(AuthenticatedUserIsRequired, UnknownError), + List( + $AuthenticatedUserIsRequired, + UnknownError + ), List(apiTagManageDynamicEntity, apiTagApi), None, http4sPartialFunction = Some(getMyDynamicEntities) @@ -1815,7 +1860,11 @@ object Http4s400 { |""", EmptyBody, EmptyBody, - List(AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), List(apiTagManageDynamicEntity, apiTagApi), Some(List(canDeleteSystemLevelDynamicEntity)), http4sPartialFunction = Some(deleteSystemDynamicEntity) @@ -1846,7 +1895,12 @@ object Http4s400 { |""", EmptyBody, EmptyBody, - List(BankNotFound, AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List( + $BankNotFound, + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + UnknownError + ), List(apiTagManageDynamicEntity, apiTagApi), Some(List(canDeleteBankLevelDynamicEntity)), http4sPartialFunction = Some(deleteBankLevelDynamicEntity) @@ -1936,7 +1990,10 @@ object Http4s400 { |""", EmptyBody, EmptyBody, - List(AuthenticatedUserIsRequired, InvalidMyDynamicEntityUser, UnknownError), + List( + $AuthenticatedUserIsRequired, + UnknownError + ), List(apiTagManageDynamicEntity, apiTagApi), None, http4sPartialFunction = Some(deleteMyDynamicEntity) @@ -2050,8 +2107,13 @@ object Http4s400 { |""", dynamicEndpointRequestBodyExample, dynamicEndpointResponseBodyExample, - List(AuthenticatedUserIsRequired, UserHasMissingRoles, DynamicEndpointExists, - InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + DynamicEndpointExists, + InvalidJsonFormat, + UnknownError + ), List(apiTagManageDynamicEndpoint, apiTagApi), Some(List(canCreateDynamicEndpoint)), http4sPartialFunction = Some(createDynamicEndpoint) @@ -2094,8 +2156,14 @@ object Http4s400 { |""", dynamicEndpointRequestBodyExample, dynamicEndpointResponseBodyExample, - List(BankNotFound, AuthenticatedUserIsRequired, UserHasMissingRoles, DynamicEndpointExists, - InvalidJsonFormat, UnknownError), + List( + $BankNotFound, + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + DynamicEndpointExists, + InvalidJsonFormat, + UnknownError + ), List(apiTagManageDynamicEndpoint, apiTagApi), Some(List(canCreateBankLevelDynamicEndpoint, canCreateDynamicEndpoint)), http4sPartialFunction = Some(createBankLevelDynamicEndpoint) @@ -2127,8 +2195,13 @@ object Http4s400 { |""", dynamicEndpointHostJson400, dynamicEndpointHostJson400, - List(AuthenticatedUserIsRequired, UserHasMissingRoles, - DynamicEntityNotFoundByDynamicEntityId, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + DynamicEntityNotFoundByDynamicEntityId, + InvalidJsonFormat, + UnknownError + ), List(apiTagManageDynamicEndpoint, apiTagApi), Some(List(canUpdateDynamicEndpoint)), http4sPartialFunction = Some(updateDynamicEndpointHost) @@ -2161,8 +2234,14 @@ object Http4s400 { |""", dynamicEndpointHostJson400, dynamicEndpointHostJson400, - List(BankNotFound, AuthenticatedUserIsRequired, UserHasMissingRoles, - DynamicEntityNotFoundByDynamicEntityId, InvalidJsonFormat, UnknownError), + List( + $BankNotFound, + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + DynamicEntityNotFoundByDynamicEntityId, + InvalidJsonFormat, + UnknownError + ), List(apiTagManageDynamicEndpoint, apiTagApi), Some(List(canUpdateBankLevelDynamicEndpoint, canUpdateDynamicEndpoint)), http4sPartialFunction = Some(updateBankLevelDynamicEndpointHost) @@ -2192,8 +2271,13 @@ object Http4s400 { |""", EmptyBody, dynamicEndpointResponseBodyExample, - List(AuthenticatedUserIsRequired, UserHasMissingRoles, - DynamicEndpointNotFoundByDynamicEndpointId, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + DynamicEndpointNotFoundByDynamicEndpointId, + InvalidJsonFormat, + UnknownError + ), List(apiTagManageDynamicEndpoint, apiTagApi), Some(List(canGetDynamicEndpoint)), http4sPartialFunction = Some(getDynamicEndpoint) @@ -2225,7 +2309,12 @@ object Http4s400 { "dynamic_endpoints", List(dynamicEndpointResponseBodyExample) ), - List(AuthenticatedUserIsRequired, UserHasMissingRoles, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), List(apiTagManageDynamicEndpoint, apiTagApi), Some(List(canGetDynamicEndpoints)), http4sPartialFunction = Some(getDynamicEndpoints) @@ -2251,8 +2340,14 @@ object Http4s400 { |""", EmptyBody, dynamicEndpointResponseBodyExample, - List(BankNotFound, AuthenticatedUserIsRequired, UserHasMissingRoles, - DynamicEndpointNotFoundByDynamicEndpointId, InvalidJsonFormat, UnknownError), + List( + $BankNotFound, + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + DynamicEndpointNotFoundByDynamicEndpointId, + InvalidJsonFormat, + UnknownError + ), List(apiTagManageDynamicEndpoint, apiTagApi), Some(List(canGetBankLevelDynamicEndpoint, canGetDynamicEndpoint)), http4sPartialFunction = Some(getBankLevelDynamicEndpoint) @@ -2284,8 +2379,13 @@ object Http4s400 { "dynamic_endpoints", List(dynamicEndpointResponseBodyExample) ), - List(BankNotFound, AuthenticatedUserIsRequired, UserHasMissingRoles, - InvalidJsonFormat, UnknownError), + List( + $BankNotFound, + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), List(apiTagManageDynamicEndpoint, apiTagApi), Some(List(canGetBankLevelDynamicEndpoints, canGetDynamicEndpoints)), http4sPartialFunction = Some(getBankLevelDynamicEndpoints) @@ -2310,7 +2410,11 @@ object Http4s400 { s"""Delete a DynamicEndpoint specified by DYNAMIC_ENDPOINT_ID.""".stripMargin, EmptyBody, EmptyBody, - List(AuthenticatedUserIsRequired, DynamicEndpointNotFoundByDynamicEndpointId, UnknownError), + List( + $AuthenticatedUserIsRequired, + DynamicEndpointNotFoundByDynamicEndpointId, + UnknownError + ), List(apiTagManageDynamicEndpoint, apiTagApi), Some(List(canDeleteDynamicEndpoint)), http4sPartialFunction = Some(deleteDynamicEndpoint) @@ -2335,8 +2439,12 @@ object Http4s400 { s"""Delete a Bank Level DynamicEndpoint specified by DYNAMIC_ENDPOINT_ID.""".stripMargin, EmptyBody, EmptyBody, - List(BankNotFound, AuthenticatedUserIsRequired, - DynamicEndpointNotFoundByDynamicEndpointId, UnknownError), + List( + $BankNotFound, + $AuthenticatedUserIsRequired, + DynamicEndpointNotFoundByDynamicEndpointId, + UnknownError + ), List(apiTagManageDynamicEndpoint, apiTagApi), Some(List(canDeleteBankLevelDynamicEndpoint, canDeleteDynamicEndpoint)), http4sPartialFunction = Some(deleteBankLevelDynamicEndpoint) @@ -2373,7 +2481,11 @@ object Http4s400 { "dynamic_endpoints", List(dynamicEndpointResponseBodyExample) ), - List(AuthenticatedUserIsRequired, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, + UnknownError + ), List(apiTagManageDynamicEndpoint, apiTagApi), None, http4sPartialFunction = Some(getMyDynamicEndpoints) @@ -2640,8 +2752,14 @@ object Http4s400 { """.stripMargin, updateAccountJsonV400, successMessage, - List(InvalidJsonFormat, $AuthenticatedUserIsRequired, $BankAccountNotFound, - "user does not have access to owner view on account", UnknownError), + List( + InvalidJsonFormat, + $AuthenticatedUserIsRequired, + $BankNotFound, + UnknownError, + $BankAccountNotFound, + "user does not have access to owner view on account" + ), List(apiTagAccount), None, http4sPartialFunction = Some(updateAccountLabel) @@ -2918,8 +3036,8 @@ object Http4s400 { |""".stripMargin, EmptyBody, moderatedFirehoseAccountsJsonV400, - List(AuthenticatedUserIsRequired, AccountFirehoseNotAllowedOnThisInstance, UnknownError), - List(apiTagAccountFirehose, apiTagAccount, apiTagFirehoseData, apiTagAccount), + List($BankNotFound), + List(apiTagAccount, apiTagAccountFirehose, apiTagFirehoseData), None, http4sPartialFunction = Some(getFirehoseAccountsAtOneBank) ) @@ -3783,9 +3901,13 @@ object Http4s400 { |""".stripMargin, EmptyBody, counterpartiesJson400, - List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, - ViewNotFound, CreateOrUpdateCounterpartyMetadataError, UnknownError), - List(apiTagCounterparty, apiTagAccount), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + $BankAccountNotFound, + UnknownError + ), + List(apiTagCounterparty, apiTagPSD2PIS, apiTagPsd2, apiTagAccount), Some(List(canGetCounterpartiesAtAnyBank, canGetCounterparties)), http4sPartialFunction = Some(getCounterpartiesForAnyAccount) ) @@ -3832,9 +3954,16 @@ object Http4s400 { |""".stripMargin, EmptyBody, counterpartyWithMetadataJson400, - List($AuthenticatedUserIsRequired, InvalidAccountIdFormat, InvalidBankIdFormat, - $BankNotFound, $BankAccountNotFound, InvalidJsonFormat, ViewNotFound, - CounterpartyNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidAccountIdFormat, + InvalidBankIdFormat, + $BankNotFound, + $BankAccountNotFound, + InvalidJsonFormat, + ViewNotFound, + UnknownError + ), List(apiTagCounterparty, apiTagAccount), Some(List(canGetCounterpartyAtAnyBank, canGetCounterparty)), http4sPartialFunction = Some(getCounterpartyByNameForAnyAccount) @@ -4116,8 +4245,15 @@ object Http4s400 { |""".stripMargin, EmptyBody, EmptyBody, - List($AuthenticatedUserIsRequired, $BankAccountNotFound, $BankNotFound, - InvalidAccountIdFormat, InvalidBankIdFormat, NoViewPermission, UnknownError), + List( + $AuthenticatedUserIsRequired, + InvalidAccountIdFormat, + InvalidBankIdFormat, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + UnknownError + ), List(apiTagCounterparty, apiTagAccount), None, http4sPartialFunction = Some(deleteExplicitCounterparty) @@ -5138,15 +5274,21 @@ object Http4s400 { nameOf(getUserInvitationAnonymous), "POST", "/banks/BANK_ID/user-invitations", - "Get User Invitation (Anonymous)", + "Get User Invitation Information", s"""Get User Invitation Information. | |${userAuthenticationMessage(false)} |""", PostUserInvitationAnonymousJsonV400(secret_key = 5819479115482092878L), userInvitationJsonV400, - List($BankNotFound, InvalidJsonFormat, UnknownError), - List(apiTagUserInvitation), + List( + $BankNotFound, + UserCustomerLinksNotFoundForUser, + CannotGetUserInvitation, + CannotFindUserInvitation, + UnknownError + ), + List(apiTagUserInvitation, apiTagKyc), None, http4sPartialFunction = Some(getUserInvitationAnonymous) ) @@ -5329,8 +5471,15 @@ object Http4s400 { "eyJhbGciOiJIUzI1NiJ9.eyJlbnRpdGxlbWVudHMiOltdLCJjcmVhdGVkQnlVc2VySWQiOiJhYjY1MzlhOS1iMTA1LTQ0ODktYTg4My0wYWQ4ZDZjNjE2NTciLCJzdWIiOiIyMWUxYzhjYy1mOTE4LTRlYWMtYjhlMy01ZTVlZWM2YjNiNGIiLCJhdWQiOiJlanpuazUwNWQxMzJyeW9tbmhieDFxbXRvaHVyYnNiYjBraWphanNrIiwibmJmIjoxNTUzNTU0ODk5LCJpc3MiOiJodHRwczpcL1wvd3d3Lm9wZW5iYW5rcHJvamVjdC5jb20iLCJleHAiOjE1NTM1NTg0OTksImlhdCI6MTU1MzU1NDg5OSwianRpIjoiMDlmODhkNWYtZWNlNi00Mzk4LThlOTktNjYxMWZhMWNkYmQ1Iiwidmlld3MiOlt7ImFjY291bnRfaWQiOiJtYXJrb19wcml2aXRlXzAxIiwiYmFua19pZCI6ImdoLjI5LnVrLngiLCJ2aWV3X2lkIjoib3duZXIifSx7ImFjY291bnRfaWQiOiJtYXJrb19wcml2aXRlXzAyIiwiYmFua19pZCI6ImdoLjI5LnVrLngiLCJ2aWV3X2lkIjoib3duZXIifV19.8cc7cBEf2NyQvJoukBCmDLT7LXYcuzTcSYLqSpbxLp4", status = "AUTHORISED" ), - List($AuthenticatedUserIsRequired, $BankNotFound, UserNotFoundByUserId, - ConsentUserAlreadyAdded, InvalidJsonFormat, ConsentNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserNotFoundByUserId, + $BankNotFound, + ConsentUserAlreadyAdded, + InvalidJsonFormat, + ConsentNotFound, + UnknownError + ), apiTagConsent :: apiTagPSD2AIS :: Nil, None, http4sPartialFunction = Some(addConsentUser) @@ -5519,9 +5668,18 @@ object Http4s400 { |""", postDirectDebitJsonV400, directDebitJsonV400, - List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, - NoViewPermission, InvalidJsonFormat, CustomerNotFoundByCustomerId, - UserNotFoundByUserId, CounterpartyNotFoundByCounterpartyId, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + $BankAccountNotFound, + NoViewPermission, + $UserNoPermissionAccessView, + InvalidJsonFormat, + CustomerNotFoundByCustomerId, + UserNotFoundByUserId, + CounterpartyNotFoundByCounterpartyId, + UnknownError + ), List(apiTagDirectDebit, apiTagAccount), None, http4sPartialFunction = Some(createDirectDebit) @@ -5562,9 +5720,19 @@ object Http4s400 { |""", postStandingOrderJsonV400, standingOrderJsonV400, - List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, - NoViewPermission, InvalidJsonFormat, InvalidNumber, InvalidISOCurrencyCode, - CustomerNotFoundByCustomerId, UserNotFoundByUserId, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + $BankAccountNotFound, + NoViewPermission, + InvalidJsonFormat, + InvalidNumber, + InvalidISOCurrencyCode, + CustomerNotFoundByCustomerId, + UserNotFoundByUserId, + $UserNoPermissionAccessView, + UnknownError + ), List(apiTagStandingOrder, apiTagAccount), None, http4sPartialFunction = Some(createStandingOrder) @@ -5600,7 +5768,7 @@ object Http4s400 { nameOf(createSystemAccountNotificationWebhook), "POST", "/web-hooks/account/notifications/on-create-transaction", - "Create System Level Account Notification Webhook", + "Create system level Account Notification Webhook", s""" |Create a notification Webhook that will fire for all accounts on the system. | @@ -6119,7 +6287,7 @@ object Http4s400 { nameOf(updateAtm), "PUT", "/banks/BANK_ID/atms/ATM_ID", - "Update ATM", + "UPDATE ATM", s"""Update ATM.""", atmJsonV400.copy(id = None), atmJsonV400, @@ -6498,8 +6666,15 @@ object Http4s400 { """.stripMargin, EmptyBody, transactionRequestWithChargeJSON210, - List($AuthenticatedUserIsRequired, $BankNotFound, $BankAccountNotFound, $UserNoPermissionAccessView, UnknownError), - List(apiTagTransactionRequest), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + $BankAccountNotFound, + $UserNoPermissionAccessView, + GetTransactionRequestsException, + UnknownError + ), + List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(getTransactionRequest) ) @@ -6558,8 +6733,12 @@ object Http4s400 { |""", EmptyBody, accountsMinimalJson400, - List($AuthenticatedUserIsRequired, CustomerNotFoundByCustomerId, UnknownError), - List(apiTagCustomer), + List( + $AuthenticatedUserIsRequired, + CustomerNotFound, + UnknownError + ), + List(apiTagAccount), Some(List(canGetAccountsMinimalForCustomerAtOneBank, canGetAccountsMinimalForCustomerAtAnyBank)), http4sPartialFunction = Some(getAccountsMinimalByCustomerId) ) @@ -6581,8 +6760,12 @@ object Http4s400 { |""", postCustomerPhoneNumberJsonV400, customerJsonV310, - List($AuthenticatedUserIsRequired, $BankNotFound, InvalidJsonFormat, UnknownError), - List(apiTagCustomer), + List( + $AuthenticatedUserIsRequired, + UserCustomerLinksNotFoundForUser, + UnknownError + ), + List(apiTagCustomer, apiTagKyc), Some(List(canGetCustomersAtOneBank)), http4sPartialFunction = Some(getCustomersByCustomerPhoneNumber) ) @@ -7763,7 +7946,12 @@ object Http4s400 { |""", EmptyBody, EmptyBody, - List(UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + UserHasMissingRoles, + UnknownError + ), List(apiTagCustomer, apiTagCustomerAttribute, apiTagAttribute), Some(List(canDeleteCustomerAttributeAtOneBank, canDeleteCustomerAttributeAtAnyBank)), http4sPartialFunction = Some(deleteCustomerAttribute) @@ -7821,7 +8009,12 @@ object Http4s400 { |""", EmptyBody, BooleanBody(true), - List(UserHasMissingRoles, UnknownError), + List( + $AuthenticatedUserIsRequired, + $BankNotFound, + UserHasMissingRoles, + UnknownError + ), List(apiTagProduct), Some(List(canDeleteProductFee)), http4sPartialFunction = Some(deleteProductFee) @@ -8094,8 +8287,8 @@ object Http4s400 { """.stripMargin, EmptyBody, entitlementsJsonV400, - List($AuthenticatedUserIsRequired, BankNotFound, UserHasMissingRoles, UnknownError), - List(apiTagRole, apiTagEntitlement, apiTagUser, apiTagBank), + List($AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), + List(apiTagRole, apiTagEntitlement, apiTagUser), Some(List(canGetEntitlementsForOneBank, canGetEntitlementsForAnyBank)), http4sPartialFunction = Some(getEntitlementsForBank) ) @@ -8106,7 +8299,7 @@ object Http4s400 { nameOf(getMyPersonalUserAttributes), "GET", "/my/user/attributes", - "Get my personal User Attributes", + "Get My Personal User Attributes", s"""Get My Personal User Attributes. | |${userAuthenticationMessage(true)} @@ -8435,13 +8628,13 @@ object Http4s400 { nameOf(getCustomerMessages), "GET", "/banks/BANK_ID/customers/CUSTOMER_ID/messages", - "Get Messages for Customer", + "Get Customer Messages for a Customer", s"""Get messages for the customer specified by CUSTOMER_ID ${userAuthenticationMessage(true)} """, EmptyBody, customerMessagesJsonV400, - List($AuthenticatedUserIsRequired, $BankNotFound, UnknownError), + List(AuthenticatedUserIsRequired, $BankNotFound, UnknownError), List(apiTagMessage, apiTagCustomer), Some(List(canGetCustomerMessages)), http4sPartialFunction = Some(getCustomerMessages) @@ -8461,7 +8654,10 @@ object Http4s400 { |""".stripMargin, createMessageJsonV400, successMessage, - List($AuthenticatedUserIsRequired, $BankNotFound), + List( + AuthenticatedUserIsRequired, + $BankNotFound + ), List(apiTagMessage, apiTagCustomer, apiTagPerson), Some(List(canCreateCustomerMessage)), http4sPartialFunction = Some(createCustomerMessage) @@ -8984,7 +9180,12 @@ object Http4s400 { |""".stripMargin, endpointTagJson400, bankLevelEndpointTagResponseJson400, - List($AuthenticatedUserIsRequired, UserHasMissingRoles, EndpointTagAlreadyExists, InvalidJsonFormat, UnknownError), + List( + $AuthenticatedUserIsRequired, + UserHasMissingRoles, + InvalidJsonFormat, + UnknownError + ), List(apiTagApi), Some(List(canCreateSystemLevelEndpointTag)), http4sPartialFunction = Some(createSystemLevelEndpointTag) From 813f7a4934c5f22a440fdccc087088a9e3b769b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 22 May 2026 07:42:18 +0200 Subject: [PATCH 2/4] v1.2.1 + v2.0.0 + v2.1.0: restore ResourceDoc fields from Lift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Run scripts/restore_resource_doc_bodies.py against the three remaining "untouched" versions to bring them in line with their Lift source-of-truth. Per-version rewrite counts: v1.2.1: 62 fields (1 summary, 41 description, 1 success body, 20 errors) v2.0.0: 23 fields (19 description, 1 example body, 3 errors) v2.1.0: 17 fields (1 summary, 12 description, 1 example body, 2 errors, 1 tag) Supporting code carried over from APIMethodsXYZ.scala into the matching Http4sXYZ.scala so restored descriptions compile: v1.2.1 (Http4s121.scala): + private val generalRevokeAccessToViewText (verbatim from APIMethods121.scala:906) — referenced by revoke-access descriptions. v2.0.0 (Http4s200.scala): + import alias `AmountOfMoneyJSON121` for AmountOfMoneyJsonV121 (used inline in createAccount's example body). + 3 private vals for createUserCustomerLinks entitlements text (createUserCustomerLinksEntitlementsRequiredForSpecificBank, ...ForAnyBank, ...requiredEntitlementsText) — referenced by restored description. v2.1.0 (Http4s210.scala): + import code.sandbox.SandboxData (used in sandbox-data-import example body). + private val getTransactionTypesIsPublic (Props lookup) — used by ${userAuthenticationMessage(!getTransactionTypesIsPublic)} in the restored description. + private val createCustomeEntitlementsRequiredText — alias of the correctly-named createCustomerEntitlementsRequiredText. Lift's APIMethods210.scala has a typo (missing the `r` in "Custome"), and the restored description interpolates the typo'd name. We preserve the bug-for-bug name to keep the restoration verbatim. Audit after this commit: v1.2.1: 48 → 6 mismatches (all middleware URL renames) v2.0.0: 19 → 1 (1 middleware URL rename) v2.1.0: 13 → 1 (1 middleware URL rename) plus 5 only-lift + 2 only-http4s (transaction-request-type variants — separate workstream) Grand total across all versions: 128 → 60 mismatches. Per the source-of-truth rule, none of the APIMethodsXYZ.scala files are modified. LIFT_HTTP4S_MIGRATION.md updated with the new per-version rows. --- LIFT_HTTP4S_MIGRATION.md | 8 +- .../scala/code/api/v1_2_1/Http4s121.scala | 479 +++++++++++++----- .../scala/code/api/v2_0_0/Http4s200.scala | 307 +++++++++-- .../scala/code/api/v2_1_0/Http4s210.scala | 222 +++++++- 4 files changed, 800 insertions(+), 216 deletions(-) diff --git a/LIFT_HTTP4S_MIGRATION.md b/LIFT_HTTP4S_MIGRATION.md index 716aa8846d..d7d16dcccb 100644 --- a/LIFT_HTTP4S_MIGRATION.md +++ b/LIFT_HTTP4S_MIGRATION.md @@ -186,11 +186,11 @@ Separate from the resource-docs **serving** workstream above, there is a parity | Version | shared | mismatch | only-lift | only-http4s | Status | |---|---|---|---|---|---| -| v1_2_1 | 70 | 48 | 0 | 0 | not started | +| v1_2_1 | 70 | 6 | 0 | 0 | semantic fields restored; 6 structural drifts remain | | 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_0_0 | 37 | 1 | 0 | 0 | semantic fields restored; 1 structural drift remains | +| v2_1_0 | 23 | 1 | 5 | 2 | semantic fields restored; 1 structural drift remains | | v2_2_0 | 18 | 0 | 0 | 18 | Lift trait fully retired upstream (commit `71892f5cb`); audited against pre-stub Lift via git history; 13 fields restored; 3 middleware URL renames remain | | 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 | @@ -198,7 +198,7 @@ Separate from the resource-docs **serving** workstream above, there is a parity | 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** | **128** | | | | +| **Total** | **956** | **60** | | | | ### v6.0.0 — 12 specific drifts (each is a fix candidate) diff --git a/obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala b/obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala index 6c9ee8a56c..ea9d7fc12f 100644 --- a/obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala +++ b/obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala @@ -43,6 +43,27 @@ object Http4s121 { val versionStatus: String = ApiVersionStatus.DEPRECATED.toString val resourceDocs: ArrayBuffer[ResourceDoc] = ArrayBuffer[ResourceDoc]() + // Carried over verbatim from APIMethods121.scala:906 — referenced inside + // restored `s"""..."""` doc interpolations for revoke-access endpoints. + private val generalRevokeAccessToViewText: String = + """ + |The User is identified by PROVIDER_ID at their PROVIDER. + | + |The Account is specified by BANK_ID and ACCOUNT_ID. + | + |The View is specified by VIEW_ID. + | + | + |PROVIDER (may be a URL so) must be URL Encoded. + | + |PROVIDER_ID is normally equivalent to USERNAME. However, see Get User by ID or GET Current User for Provider information. + | + |Attempting to revoke access to a public view will return an error message. + | + |An Account Owner cannot revoke access to an Owner View unless at least one other User has Owner View access. + | + """.stripMargin + object Implementations1_2_1 { val prefixPath = Root / ApiPathZero.toString / implementedInApiVersion.toString @@ -192,7 +213,7 @@ object Http4s121 { |* Website""", EmptyBody, bankJSON, - List(BankNotFound, UnknownError), + List(AuthenticatedUserIsRequired, UnknownError, BankNotFound), apiTagBank :: apiTagPsd2 :: apiTagOldStyle :: Nil, http4sPartialFunction = Some(bankById) ) @@ -280,9 +301,12 @@ object Http4s121 { "GET", "/accounts/public", "Get public accounts at all banks (Anonymous access)", - """Returns the list of public accounts at all banks. - |For each account the API returns the ID and the available views. - |Authentication via OAuth is not required.""".stripMargin, + """ + |Returns the list of private accounts the user has access to at all banks. + |For each account the API returns the ID and the available views. + |Authentication via OAuth is required. + | + |""".stripMargin, EmptyBody, accountJSON, List(UnknownError), @@ -379,7 +403,7 @@ object Http4s121 { |Authentication via OAuth is not required.""".stripMargin, EmptyBody, accountJSON, - List(UnknownError, BankNotFound), + List(AuthenticatedUserIsRequired, UnknownError, BankNotFound), apiTagAccountPublic :: apiTagAccount :: apiTagPublicData :: apiTagOldStyle :: Nil, http4sPartialFunction = Some(publicAccountsAtOneBank) ) @@ -409,18 +433,21 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/account", "Get account by id", s"""Information returned about an account specified by ACCOUNT_ID as moderated by the view (VIEW_ID): - | - |* Number - |* Owners - |* Type - |* Balance - |* IBAN - |* Available views - | - |${userAuthenticationMessage(false)} - | - |Authentication is required if the 'is_public' field in view (VIEW_ID) is not set to `true`. - |""".stripMargin, + | + |* Number + |* Owners + |* Type + |* Balance + |* IBAN + |* Available views + | + |More details about the data moderation by the view [here](#1_2_1-getViewsForBankAccount). + | + |${userAuthenticationMessage(false)} + | + |Authentication is required if the 'is_public' field in view (VIEW_ID) is not set to `true`. + | + |""".stripMargin, EmptyBody, moderatedAccountJSON, List(AuthenticatedUserIsRequired, UnknownError, BankAccountNotFound), @@ -501,9 +528,30 @@ object Http4s121 { "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/views", "Get Views for Account", - s"""Returns the list of the views created for account ACCOUNT_ID at BANK_ID. - | - |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", + s"""#Views + | + | + |Views in Open Bank Project provide a mechanism for fine grained access control and delegation to Accounts and Transactions. Account holders use the 'owner' view by default. Delegated access is made through other views for example 'accountants', 'share-holders' or 'tagging-application'. Views can be created via the API and each view has a list of entitlements. + | + |Views on accounts and transactions filter the underlying data to redact certain fields for certain users. For instance the balance on an account may be hidden from the public. The way to know what is possible on a view is determined in the following JSON. + | + |**Data:** When a view moderates a set of data, some fields my contain the value `null` rather than the original value. This indicates either that the user is not allowed to see the original data or the field is empty. + | + |There is currently one exception to this rule; the 'holder' field in the JSON contains always a value which is either an alias or the real name - indicated by the 'is_alias' field. + | + |**Action:** When a user performs an action like trying to post a comment (with POST API call), if he is not allowed, the body response will contain an error message. + | + |**Metadata:** + |Transaction metadata (like images, tags, comments, etc.) will appears *ONLY* on the view where they have been created e.g. comments posted to the public view only appear on the public view. + | + |The other account metadata fields (like image_URL, more_info, etc.) are unique through all the views. Example, if a user edits the 'more_info' field in the 'team' view, then the view 'authorities' will show the new value (if it is allowed to do it). + | + |# All + |*Optional* + | + |Returns the list of the views created for account ACCOUNT_ID at BANK_ID. + | + |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", EmptyBody, viewsJSONV121, List(AuthenticatedUserIsRequired, BankAccountNotFound, UnknownError, "user does not have owner access"), @@ -557,9 +605,19 @@ object Http4s121 { "POST", "/banks/BANK_ID/accounts/BANK_ACCOUNT_ID/views", "Create View", - s"""Create a view on bank account - | - |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", + s"""#Create a 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`.""", createViewJsonV121, viewJSONV121, List(AuthenticatedUserIsRequired, InvalidJsonFormat, BankAccountNotFound, UnknownError, "user does not have owner access"), @@ -615,8 +673,11 @@ object Http4s121 { "/banks/BANK_ID/accounts/BANK_ACCOUNT_ID/views/CUSTOM_VIEW_ID", "Update View", s"""Update an existing view on a bank account - | - |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", + | + |${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)""", updateViewJsonV121, viewJSONV121, List(InvalidJsonFormat, AuthenticatedUserIsRequired, BankAccountNotFound, ViewNotFound, UnknownError, "user does not have owner access"), @@ -684,9 +745,9 @@ object Http4s121 { "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/permissions", "Get access", - s"""Returns the list of the permissions at BANK_ID for account ACCOUNT_ID. - | - |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", + s"""Returns the list of the permissions at BANK_ID for account ACCOUNT_ID, with each time a pair composed of the user and the views that he has access to. + | + |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", EmptyBody, permissionsJSON, List(AuthenticatedUserIsRequired, UnknownError), @@ -725,8 +786,9 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/permissions/PROVIDER_ID/USER_ID", "Get access for specific user", s"""Returns the list of the views at BANK_ID for account ACCOUNT_ID that a USER_ID at their provider PROVIDER_ID has access to. - | - |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", + |All url parameters must be [%-encoded](http://en.wikipedia.org/wiki/Percent-encoding), which is often especially relevant for USER_ID and PROVIDER_ID. + | + |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", EmptyBody, viewsJSONV121, List(AuthenticatedUserIsRequired, BankAccountNotFound, UnknownError, "user does not have access to owner view on account"), @@ -768,10 +830,12 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/permissions/PROVIDER/PROVIDER_ID/views", "Grant User access to a list of views", s"""Grants the user identified by PROVIDER_ID at their provider PROVIDER access to a list of views at BANK_ID for account ACCOUNT_ID. - | - |${userAuthenticationMessage(true)} - | - |The User needs to have access to the owner view.""", + | + |All url parameters must be [%-encoded](http://en.wikipedia.org/wiki/Percent-encoding), which is often especially relevant for PROVIDER_ID and PROVIDER. + | + |${userAuthenticationMessage(true)} + | + |The User needs to have access to the owner view.""", viewIdsJson, viewsJSONV121, List(AuthenticatedUserIsRequired, BankAccountNotFound, UnknownError, "wrong format JSON", "could not save the privilege", "user does not have access to owner view on account"), @@ -805,9 +869,14 @@ object Http4s121 { "/banks/BANK_ID/accounts/BANK_ACCOUNT_ID/permissions/PROVIDER/PROVIDER_ID/views/GRANT_VIEW_ID", "Grant User access to View", s"""Grants the User identified by PROVIDER_ID at PROVIDER access to the view VIEW_ID at BANK_ID for account ACCOUNT_ID. - | - |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", + | + |All url parameters must be [%-encoded](http://en.wikipedia.org/wiki/Percent-encoding), which is often especially relevant for PROVIDER and PROVIDER_ID. + | + |${userAuthenticationMessage(true)} and the user needs to have access to the owner view. + | + |Granting access to a public view will return an error message, as the user already has access.""", EmptyBody, + // No Json body required viewJSONV121, List(AuthenticatedUserIsRequired, BankAccountNotFound, UnknownError, UserLacksPermissionCanGrantAccessToViewForTargetAccount, "could not save the privilege", "user does not have access to owner view on account"), List(apiTagView, apiTagAccount, apiTagUser, apiTagOwnerRequired), @@ -835,8 +904,10 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/permissions/PROVIDER/PROVIDER_ID/views/VIEW_ID", "Revoke access to one View", s"""Revokes access to a View on an Account for a certain User. - | - |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", + | + |$generalRevokeAccessToViewText + | + |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", EmptyBody, EmptyBody, List(AuthenticatedUserIsRequired, BankAccountNotFound, "could not save the privilege", "user does not have access to owner view on account", UnknownError), @@ -864,9 +935,11 @@ object Http4s121 { "DELETE", "/banks/BANK_ID/accounts/ACCOUNT_ID/permissions/PROVIDER/PROVIDER_ID/views", "Revoke access to all Views on Account", - s"""Revokes access to all Views on an Account for a certain User. - | - |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", + s""""Revokes access to all Views on an Account for a certain User. + | + |$generalRevokeAccessToViewText + | + |${userAuthenticationMessage(true)} and the user needs to have access to the owner view.""", EmptyBody, EmptyBody, List(AuthenticatedUserIsRequired, BankAccountNotFound, UnknownError, "user does not have access to owner view on account"), @@ -920,11 +993,12 @@ object Http4s121 { "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID", "Get Other Account by Id", - """Returns data about the Other Account that has shared at least one transaction with ACCOUNT_ID at BANK_ID. - |Authentication is required if the view is not public.""", + s"""Returns data about the Other Account that has shared at least one transaction with ACCOUNT_ID at BANK_ID. + |${userAuthenticationMessage(false)} + |Authentication is required if the view is not public.""", EmptyBody, otherAccountJSON, - List(AuthenticatedUserIsRequired, BankAccountNotFound, UnknownError), + List(BankAccountNotFound, UnknownError), List(apiTagCounterparty, apiTagAccount), http4sPartialFunction = Some(getOtherAccountByIdForBankAccount) ) @@ -951,7 +1025,9 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/metadata", "Get Other Account Metadata", """Get metadata of one other account. - |Authentication via OAuth is required if the view is not public.""", + |Returns only the metadata about one other bank account (OTHER_ACCOUNT_ID) that had shared at least one transaction with ACCOUNT_ID at BANK_ID. + | + |Authentication via OAuth is required if the view is not public.""", EmptyBody, otherAccountMetadataJSON, List(AuthenticatedUserIsRequired, UnknownError, "the view does not allow metadata access"), @@ -976,9 +1052,16 @@ object Http4s121 { null, implementedInApiVersion, nameOf(getCounterpartyPublicAlias), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/public_alias", "Get public alias of other bank account", - s"""Returns the public alias of the other account OTHER_ACCOUNT_ID.""", + s"""Returns the public alias of the other account OTHER_ACCOUNT_ID. + |${userAuthenticationMessage(false)} + |${userAuthenticationMessage(true)} if the view is not public.""", EmptyBody, aliasJSON, - List(AuthenticatedUserIsRequired, BankAccountNotFound, UnknownError, "the view does not allow metadata access", "the view does not allow public alias access"), + List( + BankAccountNotFound, + UnknownError, + "the view does not allow metadata access", + "the view does not allow public alias access" + ), List(apiTagCounterpartyMetaData, apiTagCounterparty), http4sPartialFunction = Some(getCounterpartyPublicAlias) ) @@ -1003,9 +1086,25 @@ object Http4s121 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(addCounterpartyPublicAlias), "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/public_alias", - "Add public alias to other bank account", s"""Creates the public alias for the other account OTHER_ACCOUNT_ID.""", + "Add public alias to other bank account", s"""Creates the public alias for the other account OTHER_ACCOUNT_ID. + | + |${userAuthenticationMessage(false)} + |Authentication is required if the view is not public. + | + |Note: Public aliases are automatically generated for new 'other accounts / counterparties', so this call should only be used if + |the public alias was deleted. + | + |The VIEW_ID parameter should be a view the caller is permitted to access to and that has permission to create public aliases.""", aliasJSON, successMessage, - List(AuthenticatedUserIsRequired, BankAccountNotFound, InvalidJsonFormat, UnknownError, "the view does not allow metadata access", "the view does not allow adding a public alias", "Alias cannot be added", "public alias added"), + List( + BankAccountNotFound, + InvalidJsonFormat, + UnknownError, + "the view does not allow metadata access", + "the view does not allow adding a public alias", + "Alias cannot be added", + "public alias added" + ), List(apiTagCounterpartyMetaData, apiTagCounterparty), http4sPartialFunction = Some(addCounterpartyPublicAlias) ) @@ -1030,7 +1129,10 @@ object Http4s121 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(updateCounterpartyPublicAlias), "PUT", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/public_alias", - "Update public alias of other bank account", s"""Updates the public alias of the other account / counterparty OTHER_ACCOUNT_ID.""", + "Update public alias of other bank account", s"""Updates the public alias of the other account / counterparty OTHER_ACCOUNT_ID. + | + |${userAuthenticationMessage(false)} + |Authentication is required if the view is not public.""", aliasJSON, successMessage, List(BankAccountNotFound, InvalidJsonFormat, AuthenticatedUserIsRequired, "the view does not allow metadata access", "the view does not allow updating the public alias", "Alias cannot be updated", UnknownError), List(apiTagCounterpartyMetaData, apiTagCounterparty), @@ -1057,9 +1159,18 @@ object Http4s121 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(deleteCounterpartyPublicAlias), "DELETE", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/public_alias", - "Delete Counterparty Public Alias", s"""Deletes the public alias of the other account OTHER_ACCOUNT_ID.""", + "Delete Counterparty Public Alias", s"""Deletes the public alias of the other account OTHER_ACCOUNT_ID. + | + |${userAuthenticationMessage(false)} + |Authentication is required if the view is not public.""", EmptyBody, EmptyBody, - List(AuthenticatedUserIsRequired, BankAccountNotFound, "the view does not allow metadata access", "the view does not allow deleting the public alias", "Alias cannot be deleted", UnknownError), + List( + BankAccountNotFound, + "the view does not allow metadata access", + "the view does not allow deleting the public alias", + "Alias cannot be deleted", + UnknownError + ), List(apiTagCounterpartyMetaData, apiTagCounterparty), http4sPartialFunction = Some(deleteCounterpartyPublicAlias) ) @@ -1080,7 +1191,10 @@ object Http4s121 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(getOtherAccountPrivateAlias), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/private_alias", - "Get Other Account Private Alias", s"""Returns the private alias of the other account OTHER_ACCOUNT_ID.""", + "Get Other Account Private Alias", s"""Returns the private alias of the other account OTHER_ACCOUNT_ID. + | + |${userAuthenticationMessage(false)} + |Authentication is required if the view is not public.""", EmptyBody, aliasJSON, List(AuthenticatedUserIsRequired, BankAccountNotFound, "the view does not allow metadata access", "the view does not allow private alias access", UnknownError), List(apiTagCounterpartyMetaData, apiTagCounterparty), @@ -1107,7 +1221,10 @@ object Http4s121 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(addOtherAccountPrivateAlias), "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/private_alias", - "Create Other Account Private Alias", s"""Creates a private alias for the other account OTHER_ACCOUNT_ID.""", + "Create Other Account Private Alias", s"""Creates a private alias for the other account OTHER_ACCOUNT_ID. + | + |${userAuthenticationMessage(false)} + |Authentication is required if the view is not public.""", aliasJSON, successMessage, List(AuthenticatedUserIsRequired, BankAccountNotFound, InvalidJsonFormat, "the view does not allow metadata access", "the view does not allow adding a private alias", "Alias cannot be added", UnknownError), List(apiTagCounterpartyMetaData, apiTagCounterparty), @@ -1134,7 +1251,10 @@ object Http4s121 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(updateCounterpartyPrivateAlias), "PUT", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/private_alias", - "Update Counterparty Private Alias", s"""Updates the private alias of the counterparty OTHER_ACCOUNT_ID.""", + "Update Counterparty Private Alias", s"""Updates the private alias of the counterparty (AKA other account) OTHER_ACCOUNT_ID. + | + |${userAuthenticationMessage(false)} + |Authentication is required if the view is not public.""", aliasJSON, successMessage, List(AuthenticatedUserIsRequired, BankAccountNotFound, InvalidJsonFormat, "the view does not allow metadata access", "the view does not allow updating the private alias", "Alias cannot be updated", UnknownError), List(apiTagCounterpartyMetaData, apiTagCounterparty), @@ -1161,7 +1281,10 @@ object Http4s121 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(deleteCounterpartyPrivateAlias), "DELETE", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/private_alias", - "Delete Counterparty Private Alias", s"""Deletes the private alias of the other account OTHER_ACCOUNT_ID.""", + "Delete Counterparty Private Alias", s"""Deletes the private alias of the other account OTHER_ACCOUNT_ID. + | + |${userAuthenticationMessage(false)} + |Authentication is required if the view is not public.""", EmptyBody, EmptyBody, List(AuthenticatedUserIsRequired, BankAccountNotFound, "the view does not allow metadata access", "the view does not allow deleting the private alias", "Alias cannot be deleted", UnknownError), List(apiTagCounterpartyMetaData, apiTagCounterparty), @@ -1188,9 +1311,17 @@ object Http4s121 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(addCounterpartyMoreInfo), "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/metadata/more_info", - "Add Counterparty More Info", "Add a description of the counter party from the perspective of the account e.g. My dentist", + "Add Counterparty More Info", "Add a description of the counter party from the perpestive of the account e.g. My dentist", moreInfoJSON, successMessage, - List(AuthenticatedUserIsRequired, BankAccountNotFound, InvalidJsonFormat, NoViewPermission, "the view does not allow adding more info", "More Info cannot be added", UnknownError), + List( + AuthenticatedUserIsRequired, + BankAccountNotFound, + InvalidJsonFormat, + NoViewPermission, + "the view " + viewIdSwagger + "does not allow adding more info", + "More Info cannot be added", + UnknownError + ), List(apiTagCounterpartyMetaData, apiTagCounterparty), http4sPartialFunction = Some(addCounterpartyMoreInfo) ) @@ -1215,7 +1346,7 @@ object Http4s121 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(updateCounterpartyMoreInfo), "PUT", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/metadata/more_info", - "Update Counterparty More Info", "Update the more info description of the counter party", + "Update Counterparty More Info", "Update the more info description of the counter party from the perpestive of the account e.g. My dentist", moreInfoJSON, successMessage, List(AuthenticatedUserIsRequired, BankAccountNotFound, InvalidJsonFormat, "the view does not allow metadata access", "the view does not allow updating more info", "More Info cannot be updated", UnknownError), List(apiTagCounterpartyMetaData, apiTagCounterparty), @@ -1379,7 +1510,13 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/metadata/image_url", "Update Counterparty Image Url", "Update the url that points to the logo of the counterparty", imageUrlJSON, successMessage, - List(AuthenticatedUserIsRequired, BankAccountNotFound, InvalidJsonFormat, "the view does not allow metadata access", "the view does not allow updating an image url", "URL cannot be updated", UnknownError), + List( + BankAccountNotFound, + InvalidJsonFormat, + "the view does not allow metadata access", + "the view does not allow updating an image url", + "URL cannot be updated", + UnknownError), List(apiTagCounterpartyMetaData, apiTagCounterparty), http4sPartialFunction = Some(updateCounterpartyImageUrl) ) @@ -1406,7 +1543,7 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/metadata/image_url", "Delete Counterparty Image URL", "Delete image url of other bank account", EmptyBody, EmptyBody, - List(AuthenticatedUserIsRequired, BankAccountNotFound, UnknownError), + List(UnknownError), List(apiTagCounterpartyMetaData, apiTagCounterparty), http4sPartialFunction = Some(deleteCounterpartyImageUrl) ) @@ -1433,7 +1570,13 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/metadata/open_corporates_url", "Add Open Corporates URL to Counterparty", "Add open corporates url to other bank account", openCorporateUrlJSON, successMessage, - List(AuthenticatedUserIsRequired, BankAccountNotFound, InvalidJsonFormat, "the view does not allow metadata access", "the view does not allow adding an open corporate url", "URL cannot be added", UnknownError), + List( + BankAccountNotFound, + InvalidJsonFormat, + "the view does not allow metadata access", + "the view does not allow adding an open corporate url", + "URL cannot be added", + UnknownError), List(apiTagCounterpartyMetaData, apiTagCounterparty), http4sPartialFunction = Some(addCounterpartyOpenCorporatesUrl) ) @@ -1681,12 +1824,13 @@ object Http4s121 { null, implementedInApiVersion, nameOf(getTransactionsForBankAccount), "GET", "/banks/BANK_ID/accounts/BANK_ACCOUNT_ID/TRANSACTIONS_VIEW_ID/transactions", "Get Transactions for Account (Full)", - s"""Returns transactions list of the account specified by ACCOUNT_ID and moderated by the view (VIEW_ID). - | - |Authentication via OAuth is required if the view is not public. - | - |${urlParametersDocument(true, true)} - |""", + s"""Returns transactions list of the account specified by ACCOUNT_ID and [moderated](#1_2_1-getViewsForBankAccount) by the view (VIEW_ID). + | + |Authentication via OAuth is required if the view is not public. + | + |${urlParametersDocument(true, true)} + | + |""", EmptyBody, transactionsJSON, List(BankAccountNotFound, UnknownError), List(apiTagTransaction, apiTagAccount, apiTagPsd2, apiTagOldStyle), @@ -1713,10 +1857,13 @@ object Http4s121 { null, implementedInApiVersion, nameOf(getTransactionByIdForBankAccount), "GET", "/banks/BANK_ID/accounts/BANK_ACCOUNT_ID/TRANSACTIONS_VIEW_ID/transactions/TRANSACTION_ID/transaction", "Get Transaction by Id", - """Returns one transaction specified by TRANSACTION_ID of the account ACCOUNT_ID and moderated by the view (VIEW_ID). - | - |Authentication is required if the view is not public. - |""", + s"""Returns one transaction specified by TRANSACTION_ID of the account ACCOUNT_ID and [moderated](#1_2_1-getViewsForBankAccount) by the view (VIEW_ID). + | + |${userAuthenticationMessage(false)} + |Authentication is required if the view is not public. + | + | + |""", EmptyBody, transactionJSON, List(BankAccountNotFound, UnknownError), List(apiTagTransaction, apiTagPsd2, apiTagOldStyle), @@ -1741,11 +1888,15 @@ object Http4s121 { null, implementedInApiVersion, nameOf(getTransactionNarrative), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/narrative", "Get a Transaction Narrative", - """Returns the account owner description of the transaction moderated by the view. - | - |Authentication via OAuth is required if the view is not public.""", + """Returns the account owner description of the transaction [moderated](#1_2_1-getViewsForBankAccount) by the view. + | + |Authentication via OAuth is required if the view is not public.""", EmptyBody, transactionNarrativeJSON, - List(AuthenticatedUserIsRequired, BankAccountNotFound, NoViewPermission, ViewNotFound, UnknownError), + List( + BankAccountNotFound, + NoViewPermission, + ViewNotFound, + UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), http4sPartialFunction = Some(getTransactionNarrative) ) @@ -1788,12 +1939,21 @@ object Http4s121 { null, implementedInApiVersion, nameOf(addTransactionNarrative), "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/narrative", "Add a Transaction Narrative", - """Creates a description of the transaction TRANSACTION_ID. - | - |Authentication is required if the view is not public. - |""", + s"""Creates a description of the transaction TRANSACTION_ID. + | + |Note: Unlike other items of metadata, there is only one "narrative" per transaction accross all views. + |If you set narrative via a view e.g. view-x it will be seen via view-y (as long as view-y has permission to see the narrative). + | + |${userAuthenticationMessage(false)} + |Authentication is required if the view is not public. + |""", transactionNarrativeJSON, successMessage, - List(AuthenticatedUserIsRequired, InvalidJsonFormat, BankAccountNotFound, NoViewPermission, ViewNotFound, UnknownError), + List( + InvalidJsonFormat, + BankAccountNotFound, + NoViewPermission, + ViewNotFound, + UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), http4sPartialFunction = Some(addTransactionNarrative) ) @@ -1840,7 +2000,11 @@ object Http4s121 { | |Authentication via OAuth is required if the view is not public.""", transactionNarrativeJSON, successMessage, - List(AuthenticatedUserIsRequired, InvalidJsonFormat, BankAccountNotFound, NoViewPermission, ViewNotFound, UnknownError), + List(InvalidJsonFormat, + BankAccountNotFound, + NoViewPermission, + ViewNotFound, + UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), http4sPartialFunction = Some(updateTransactionNarrative) ) @@ -1892,9 +2056,9 @@ object Http4s121 { null, implementedInApiVersion, nameOf(getCommentsForViewOnTransaction), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/comments", "Get Transaction Comments", - """Returns the transaction TRANSACTION_ID comments made on a view (VIEW_ID). - | - |Authentication via OAuth is required if the view is not public.""", + """Returns the transaction TRANSACTION_ID comments made on a [view](#1_2_1-getViewsForBankAccount) (VIEW_ID). + | + |Authentication via OAuth is required if the view is not public.""", EmptyBody, transactionCommentsJSON, List(AuthenticatedUserIsRequired, BankAccountNotFound, NoViewPermission, ViewNotFound, UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), @@ -1939,9 +2103,11 @@ object Http4s121 { null, implementedInApiVersion, nameOf(addCommentForViewOnTransaction), "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/comments", "Add a Transaction Comment", - """Posts a comment about a transaction TRANSACTION_ID on a view VIEW_ID. - | - |Authentication is required since the comment is linked with the user.""", + """Posts a comment about a transaction TRANSACTION_ID on a [view](#1_2_1-getViewsForBankAccount) VIEW_ID. + | + |${authenticationRequiredMessage(false)} + | + |Authentication is required since the comment is linked with the user.""", postTransactionCommentJSON, transactionCommentJSON, List(AuthenticatedUserIsRequired, InvalidJsonFormat, BankAccountNotFound, NoViewPermission, ViewNotFound, UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), @@ -1969,9 +2135,9 @@ object Http4s121 { null, implementedInApiVersion, nameOf(deleteCommentForViewOnTransaction), "DELETE", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/comments/COMMENT_ID", "Delete a Transaction Comment", - """Delete the comment COMMENT_ID about the transaction TRANSACTION_ID made on a view. - | - |Authentication via OAuth is required. The user must either have owner privileges for this account, or must be the user that posted the comment.""", + """Delete the comment COMMENT_ID about the transaction TRANSACTION_ID made on [view](#1_2_1-getViewsForBankAccount). + | + |Authentication via OAuth is required. The user must either have owner privileges for this account, or must be the user that posted the comment.""", EmptyBody, EmptyBody, List(BankAccountNotFound, NoViewPermission, ViewNotFound, AuthenticatedUserIsRequired, UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), @@ -1996,10 +2162,15 @@ object Http4s121 { null, implementedInApiVersion, nameOf(getTagsForViewOnTransaction), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/tags", "Get Transaction Tags", - """Returns the transaction TRANSACTION_ID tags made on a view (VIEW_ID). - |Authentication via OAuth is required if the view is not public.""", + """Returns the transaction TRANSACTION_ID tags made on a [view](#1_2_1-getViewsForBankAccount) (VIEW_ID). + Authentication via OAuth is required if the view is not public.""", EmptyBody, transactionTagJSON, - List(AuthenticatedUserIsRequired, BankAccountNotFound, NoViewPermission, ViewNotFound, UnknownError), + List( + BankAccountNotFound, + NoViewPermission, + ViewNotFound, + UnknownError + ), List(apiTagTransactionMetaData, apiTagTransaction), http4sPartialFunction = Some(getTagsForViewOnTransaction) ) @@ -2042,11 +2213,11 @@ object Http4s121 { null, implementedInApiVersion, nameOf(addTagForViewOnTransaction), "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/tags", "Add a Transaction Tag", - s"""Posts a tag about a transaction TRANSACTION_ID on a view VIEW_ID. - | - |${userAuthenticationMessage(true)} - | - |Authentication is required as the tag is linked with the user.""", + s"""Posts a tag about a transaction TRANSACTION_ID on a [view](#1_2_1-getViewsForBankAccount) VIEW_ID. + | + |${userAuthenticationMessage(true)} + | + |Authentication is required as the tag is linked with the user.""", postTransactionTagJSON, transactionTagJSON, List(AuthenticatedUserIsRequired, BankAccountNotFound, InvalidJsonFormat, NoViewPermission, ViewNotFound, UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), @@ -2074,11 +2245,14 @@ object Http4s121 { null, implementedInApiVersion, nameOf(deleteTagForViewOnTransaction), "DELETE", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/tags/TAG_ID", "Delete a Transaction Tag", - """Deletes the tag TAG_ID about the transaction TRANSACTION_ID made on a view. - |Authentication via OAuth is required. The user must either have owner privileges for this account, - |or must be the user that posted the tag.""", + """Deletes the tag TAG_ID about the transaction TRANSACTION_ID made on [view](#1_2_1-getViewsForBankAccount). + |Authentication via OAuth is required. The user must either have owner privileges for this account, + |or must be the user that posted the tag. + |""".stripMargin, EmptyBody, EmptyBody, - List(AuthenticatedUserIsRequired, NoViewPermission, ViewNotFound, UnknownError), + List(NoViewPermission, + ViewNotFound, + UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), http4sPartialFunction = Some(deleteTagForViewOnTransaction) ) @@ -2101,8 +2275,8 @@ object Http4s121 { null, implementedInApiVersion, nameOf(getImagesForViewOnTransaction), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/images", "Get Transaction Images", - """Returns the transaction TRANSACTION_ID images made on a view (VIEW_ID). - |Authentication via OAuth is required if the view is not public.""", + """Returns the transaction TRANSACTION_ID images made on a [view](#1_2_1-getViewsForBankAccount) (VIEW_ID). + Authentication via OAuth is required if the view is not public.""", EmptyBody, transactionImagesJSON, List(AuthenticatedUserIsRequired, BankAccountNotFound, NoViewPermission, ViewNotFound, UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), @@ -2150,13 +2324,19 @@ object Http4s121 { null, implementedInApiVersion, nameOf(addImageForViewOnTransaction), "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/images", "Add a Transaction Image", - s"""Posts an image about a transaction TRANSACTION_ID on a view VIEW_ID. - | - |${userAuthenticationMessage(true)} - | - |The image is linked with the user.""", + s"""Posts an image about a transaction TRANSACTION_ID on a [view](#1_2_1-getViewsForBankAccount) VIEW_ID. + | + |${userAuthenticationMessage(true) } + | + |The image is linked with the user.""", postTransactionImageJSON, transactionImageJSON, - List(AuthenticatedUserIsRequired, InvalidJsonFormat, BankAccountNotFound, NoViewPermission, ViewNotFound, InvalidUrl, UnknownError), + List( + InvalidJsonFormat, + BankAccountNotFound, + NoViewPermission, + ViewNotFound, + InvalidUrl, + UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), http4sPartialFunction = Some(addImageForViewOnTransaction) ) @@ -2182,11 +2362,19 @@ object Http4s121 { null, implementedInApiVersion, nameOf(deleteImageForViewOnTransaction), "DELETE", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/images/IMAGE_ID", "Delete a Transaction Image", - """Deletes the image IMAGE_ID about the transaction TRANSACTION_ID made on a view. - | - |Authentication via OAuth is required. The user must either have owner privileges for this account, or must be the user that posted the image.""", + """Deletes the image IMAGE_ID about the transaction TRANSACTION_ID made on [view](#1_2_1-getViewsForBankAccount). + | + |Authentication via OAuth is required. The user must either have owner privileges for this account, or must be the user that posted the image.""", EmptyBody, EmptyBody, - List(BankAccountNotFound, NoViewPermission, AuthenticatedUserIsRequired, UnknownError), + List( + BankAccountNotFound, + NoViewPermission, + AuthenticatedUserIsRequired, + "You must be able to see images in order to delete them", + "Image not found for this transaction", + "Deleting images not permitted for this view", + "Deleting images not permitted for the current user", + UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), http4sPartialFunction = Some(deleteImageForViewOnTransaction) ) @@ -2212,12 +2400,15 @@ object Http4s121 { null, implementedInApiVersion, nameOf(getWhereTagForViewOnTransaction), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/where", "Get a Transaction where Tag", - """Returns the "where" Geo tag added to the transaction TRANSACTION_ID made on a view (VIEW_ID). - |It represents the location where the transaction has been initiated. - | - |Authentication via OAuth is required if the view is not public.""", + """Returns the "where" Geo tag added to the transaction TRANSACTION_ID made on a [view](#1_2_1-getViewsForBankAccount) (VIEW_ID). + |It represents the location where the transaction has been initiated. + | + |Authentication via OAuth is required if the view is not public.""", EmptyBody, transactionWhereJSON, - List(AuthenticatedUserIsRequired, BankAccountNotFound, NoViewPermission, ViewNotFound, UnknownError), + List(BankAccountNotFound, + NoViewPermission, + ViewNotFound, + UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), http4sPartialFunction = Some(getWhereTagForViewOnTransaction) ) @@ -2263,11 +2454,11 @@ object Http4s121 { null, implementedInApiVersion, nameOf(addWhereTagForViewOnTransaction), "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/where", "Add a Transaction where Tag", - s"""Creates a "where" Geo tag on a transaction TRANSACTION_ID in a view. - | - |${userAuthenticationMessage(true)} - | - |The geo tag is linked with the user.""", + s"""Creates a "where" Geo tag on a transaction TRANSACTION_ID in a [view](#1_2_1-getViewsForBankAccount). + | + |${userAuthenticationMessage(true)} + | + |The geo tag is linked with the user.""", postTransactionWhereJSON, successMessage, List(AuthenticatedUserIsRequired, BankAccountNotFound, InvalidJsonFormat, ViewNotFound, NoViewPermission, "Coordinates not possible", UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), @@ -2315,11 +2506,11 @@ object Http4s121 { null, implementedInApiVersion, nameOf(updateWhereTagForViewOnTransaction), "PUT", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/where", "Update a Transaction where Tag", - s"""Updates the "where" Geo tag on a transaction TRANSACTION_ID in a view. - | - |${userAuthenticationMessage(true)} - | - |The geo tag is linked with the user.""", + s"""Updates the "where" Geo tag on a transaction TRANSACTION_ID in a [view](#1_2_1-getViewsForBankAccount). + | + |${userAuthenticationMessage(true)} + | + |The geo tag is linked with the user.""", postTransactionWhereJSON, successMessage, List(AuthenticatedUserIsRequired, BankAccountNotFound, InvalidJsonFormat, ViewNotFound, NoViewPermission, "Coordinates not possible", UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), @@ -2346,14 +2537,22 @@ object Http4s121 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(deleteWhereTagForViewOnTransaction), "DELETE", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/where", - "Delete a Transaction where Tag", - s"""Deletes the where tag of the transaction TRANSACTION_ID made on a view. - | - |${userAuthenticationMessage(true)} - | - |The user must either have owner privileges for this account, or must be the user that posted the geo tag.""", + "Delete a Transaction Tag", + s"""Deletes the where tag of the transaction TRANSACTION_ID made on [view](#1_2_1-getViewsForBankAccount). + | + |${userAuthenticationMessage(true)} + | + |The user must either have owner privileges for this account, or must be the user that posted the geo tag.""", EmptyBody, EmptyBody, - List(AuthenticatedUserIsRequired, BankAccountNotFound, NoViewPermission, ViewNotFound, "there is no tag to delete", "Delete not completed", UnknownError), + List( + AuthenticatedUserIsRequired, + BankAccountNotFound, + NoViewPermission, + AuthenticatedUserIsRequired, + ViewNotFound, + "there is no tag to delete", + "Delete not completed", + UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), http4sPartialFunction = Some(deleteWhereTagForViewOnTransaction) ) @@ -2379,10 +2578,10 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/other_account", "Get Other Account of Transaction", """Get other account of a transaction. - |Returns details of the other party involved in the transaction, moderated by the view (VIEW_ID). - |Authentication via OAuth is required if the view is not public.""", + |Returns details of the other party involved in the transaction, moderated by the [view](#1_2_1-getViewsForBankAccount) (VIEW_ID). + Authentication via OAuth is required if the view is not public.""", EmptyBody, otherAccountJSON, - List(AuthenticatedUserIsRequired, BankAccountNotFound, UnknownError), + List(BankAccountNotFound, UnknownError), List(apiTagTransaction, apiTagCounterparty), http4sPartialFunction = Some(getOtherAccountForTransaction) ) diff --git a/obp-api/src/main/scala/code/api/v2_0_0/Http4s200.scala b/obp-api/src/main/scala/code/api/v2_0_0/Http4s200.scala index 231e472728..36341bf42a 100644 --- a/obp-api/src/main/scala/code/api/v2_0_0/Http4s200.scala +++ b/obp-api/src/main/scala/code/api/v2_0_0/Http4s200.scala @@ -30,6 +30,7 @@ import code.views.Views import com.github.dwickern.macros.NameOf.nameOf import com.openbankproject.commons.ExecutionContext.Implicits.global import com.openbankproject.commons.model.{AccountId, AmountOfMoneyJsonV121, BankId, BankIdAccountId, CustomerFaceImage} +import com.openbankproject.commons.model.{AmountOfMoneyJsonV121 => AmountOfMoneyJSON121} import com.openbankproject.commons.util.{ApiVersion, ApiVersionStatus, ScannedApiVersion} import net.liftweb.common._ import net.liftweb.http.InMemoryResponse @@ -52,6 +53,16 @@ object Http4s200 { type HttpF[A] = OptionT[IO, A] + // Carried over verbatim from APIMethods200.scala — referenced inside + // restored `s"""..."""` doc interpolations for createUserCustomerLinks. + private val createUserCustomerLinksEntitlementsRequiredForSpecificBank = canCreateUserCustomerLink :: Nil + private val createUserCustomerLinksEntitlementsRequiredForAnyBank = canCreateUserCustomerLinkAtAnyBank :: Nil + private val createUserCustomerLinksrequiredEntitlementsText = + createUserCustomerLinksEntitlementsRequiredForSpecificBank.mkString(" and ") + + " OR " + + createUserCustomerLinksEntitlementsRequiredForAnyBank.mkString(" and ") + + " entitlements are required." + object Implementations2_0_0 { val prefixPath = Root / ApiPathZero.toString / implementedInApiVersion.toString @@ -210,10 +221,11 @@ object Http4s200 { "Get Accounts at Bank", s""" |Returns the list of accounts at BANK_ID that the user has access to. - |For each account the API returns the account ID and the views available to the user. + |For each account the API returns the account ID and the views available to the user.. |Each account must have at least one private View. | - |${userAuthenticationMessage(true)}""".stripMargin, + |${userAuthenticationMessage(true)} + """.stripMargin, EmptyBody, basicAccountsJSON, List(BankNotFound, UnknownError), List(apiTagAccount, apiTagPrivateData, apiTagPublicData), None, @@ -259,10 +271,15 @@ object Http4s200 { null, implementedInApiVersion, nameOf(corePrivateAccountsAtOneBank), "GET", "/my/banks/BANK_ID/accounts", "Get Accounts at Bank (Private)", s"""Get private accounts at one bank (Authenticated access). - |Returns the list of accounts containing private views for the user at BANK_ID. - |For each account the API returns the ID and label. - | - |${userAuthenticationMessage(true)}""".stripMargin, + |Returns the list of accounts containing private views for the user at BANK_ID. + |For each account the API returns the ID and label. To also see the list of Views, see privateAccountsAtOneBank + | + | + |This call MAY have an alias /bank/accounts but ONLY if defaultBank is set in Props + | + |${userAuthenticationMessage(true)} + | + |""".stripMargin, EmptyBody, coreAccountsJSON, List(AuthenticatedUserIsRequired, UnknownError), List(apiTagAccount, apiTagPrivateData, apiTagPsd2), None, @@ -286,9 +303,15 @@ object Http4s200 { null, implementedInApiVersion, nameOf(privateAccountsAtOneBank), "GET", "/banks/BANK_ID/accounts/private", "Get private accounts at one bank", s"""Returns the list of private accounts at BANK_ID that the user has access to. - |For each account the API returns the ID and the available views. - | - |${userAuthenticationMessage(true)}""".stripMargin, + |For each account the API returns the ID and the available views. + | + |If you want to see more information on the Views, use the Account Detail call. + |If you want less information about the account, use the /my accounts call + | + | + |${userAuthenticationMessage(true)} + | + |""".stripMargin, EmptyBody, basicAccountsJSON, List(AuthenticatedUserIsRequired, BankNotFound, UnknownError), List(apiTagAccount, apiTagPsd2), None, @@ -487,9 +510,9 @@ object Http4s200 { null, implementedInApiVersion, nameOf(addKycDocument), "PUT", "/banks/BANK_ID/customers/CUSTOMER_ID/kyc_documents/KYC_DOCUMENT_ID", "Add KYC Document", - "Add a KYC document for the customer specified by CUSTOMER_ID.", + "Add a KYC document for the customer specified by CUSTOMER_ID. KYC Documents contain the document type (e.g. passport), place of issue, expiry etc. ", postKycDocumentJSON, kycDocumentJSON, - List(AuthenticatedUserIsRequired, InvalidJsonFormat, BankNotFound, CustomerNotFoundByCustomerId, UnknownError), + List(AuthenticatedUserIsRequired, InvalidJsonFormat, BankNotFound, CustomerNotFoundByCustomerId,"Server error: could not add KycDocument", UnknownError), List(apiTagKyc, apiTagCustomer), Some(List(canAddKycDocument)), http4sPartialFunction = Some(addKycDocument)) @@ -516,7 +539,7 @@ object Http4s200 { null, implementedInApiVersion, nameOf(addKycMedia), "PUT", "/banks/BANK_ID/customers/CUSTOMER_ID/kyc_media/KYC_MEDIA_ID", "Add KYC Media", - "Add some KYC media for the customer specified by CUSTOMER_ID.", + "Add some KYC media for the customer specified by CUSTOMER_ID. KYC Media resources relate to KYC Documents and KYC Checks and contain media urls for scans of passports, utility bills etc", postKycMediaJSON, kycMediaJSON, List(AuthenticatedUserIsRequired, InvalidJsonFormat, CustomerNotFoundByCustomerId, ServerAddDataError, UnknownError), List(apiTagKyc, apiTagCustomer), @@ -545,7 +568,7 @@ object Http4s200 { null, implementedInApiVersion, nameOf(addKycCheck), "PUT", "/banks/BANK_ID/customers/CUSTOMER_ID/kyc_check/KYC_CHECK_ID", "Add KYC Check", - "Add a KYC check for the customer specified by CUSTOMER_ID.", + "Add a KYC check for the customer specified by CUSTOMER_ID. KYC Checks store details of checks on a customer made by the KYC team, their comments and a satisfied status", postKycCheckJSON, kycCheckJSON, List(AuthenticatedUserIsRequired, InvalidJsonFormat, BankNotFound, CustomerNotFoundByCustomerId, ServerAddDataError, UnknownError), List(apiTagKyc, apiTagCustomer), @@ -573,7 +596,7 @@ object Http4s200 { null, implementedInApiVersion, nameOf(addKycStatus), "PUT", "/banks/BANK_ID/customers/CUSTOMER_ID/kyc_statuses", "Add KYC Status", - "Add a kyc_status for the customer specified by CUSTOMER_ID.", + "Add a kyc_status for the customer specified by CUSTOMER_ID. KYC Status is a timeline of the KYC status of the customer", postKycStatusJSON, kycStatusJSON, List(AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidBankIdFormat, UnknownError, BankNotFound, ServerAddDataError, CustomerNotFoundByCustomerId), List(apiTagKyc, apiTagCustomer), @@ -707,9 +730,23 @@ object Http4s200 { null, implementedInApiVersion, nameOf(accountById), "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/account", "Get Account by Id (Full)", - s"""Information returned about an account specified by ACCOUNT_ID as moderated by the view (VIEW_ID). - | - |${userAuthenticationMessage(true)} if the 'is_public' field in view (VIEW_ID) is not set to `true`.""".stripMargin, + s"""Information returned about an account specified by ACCOUNT_ID as moderated by the view (VIEW_ID): + | + |* Number + |* Owners + |* Type + |* Balance + |* IBAN + |* Available views (sorted by short_name) + | + |More details about the data moderation by the view [here](#1_2_1-getViewsForBankAccount). + | + |PSD2 Context: PSD2 requires customers to have access to their account information via third party applications. + |This call provides balance and other account information via delegated authentication using OAuth. + | + |${userAuthenticationMessage(true)} if the 'is_public' field in view (VIEW_ID) is not set to `true`. + | + |""".stripMargin, EmptyBody, moderatedAccountJSON, List(BankNotFound, AccountNotFound, ViewNotFound, UserNoPermissionAccessView, UnknownError), List(apiTagAccount, apiTagOldStyle), None, @@ -784,10 +821,11 @@ object Http4s200 { "/banks/BANK_ID/accounts/ACCOUNT_ID/permissions/PROVIDER/PROVIDER_ID", "Get Account access for User", s"""Returns the list of the views at BANK_ID for account ACCOUNT_ID that a user identified by PROVIDER_ID at their provider PROVIDER has access to. - | - |${userAuthenticationMessage(true)} - | - |The user needs to have access to the owner view.""", + |All url parameters must be [%-encoded](http://en.wikipedia.org/wiki/Percent-encoding), which is often especially relevant for USER_ID and PROVIDER. + | + |${userAuthenticationMessage(true)} + | + |The user needs to have access to the owner view.""", EmptyBody, viewsJSONV121, List(AuthenticatedUserIsRequired, BankNotFound, AccountNotFound, UnknownError), List(apiTagView, apiTagAccount, apiTagUser, apiTagOldStyle), None, @@ -839,13 +877,18 @@ object Http4s200 { "/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 an Account for another User if they have CanCreateAccount role. - | - |If USER_ID is not specified the account will be owned by the logged in User. - | - |Note: The Amount must be zero.""".stripMargin, - CreateAccountJSON("A user_id", "CURRENT", "Label", AmountOfMoneyJsonV121("EUR", "0")), + | + | + |The User can create an Account for themself or an Account for another User if they have CanCreateAccount role. + | + |If USER_ID is not specified the account will be owned by the logged in User. + | + |ACCOUNT_ID SHOULD be a UUID. ACCOUNT_ID MUST NOT be the ACCOUNT_NUMBER. + | + |TYPE SHOULD be the PRODUCT_CODE from Product. + | + |Note: The Amount must be zero.""".stripMargin, + CreateAccountJSON("A user_id","CURRENT", "Label", AmountOfMoneyJSON121("EUR", "0")), coreAccountJSON, List(AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidUserId, InvalidAccountIdFormat, InvalidBankIdFormat, UserNotFoundById, InvalidAccountBalanceAmount, InvalidAccountType, InvalidAccountInitialBalance, @@ -872,7 +915,17 @@ object Http4s200 { null, implementedInApiVersion, nameOf(getTransactionTypes), "GET", "/banks/BANK_ID/transaction-types", "Get Transaction Types at Bank", - s"""Get Transaction Types for the bank specified by BANK_ID. + // TODO get the documentation of the parameters from the scala doc of the case class we return + s"""Get Transaction Types for the bank specified by BANK_ID: + | + |Lists the possible Transaction Types available at the bank (as opposed to Transaction Request Types which are the possible ways Transactions can be created by this API Server). + | + | * id : Unique transaction type id across the API instance. SHOULD be a UUID. MUST be unique. + | * bank_id : The bank that supports this TransactionType + | * short_code : A short code (SHOULD have no-spaces) which MUST be unique across the bank. May be stored with Transactions to link here + | * summary : A succinct summary + | * description : A longer description + | * charge : The charge to the customer for each one of these | |${userAuthenticationMessage(!getTransactionTypesIsPublic)}""".stripMargin, EmptyBody, transactionTypesJsonV200, @@ -923,9 +976,30 @@ object Http4s200 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(createUser), "POST", "/users", "Create User", - s"""Creates OBP user. No authorisation required.""", + s"""Creates OBP user. + | No authorisation required. + | + | Mimics current webform to Register. + | + | Requires username(email), password, first_name, last_name, and email. + | + | Validation checks performed: + | - Password must meet strong password requirements (InvalidStrongPasswordFormat error if not) + | - Username must be unique (409 error if username already exists) + | - All required fields must be present in valid JSON format + | + | Email validation behavior: + | - Controlled by property 'authUser.skipEmailValidation' (default: false) + | - When false: User is created with validated=false and a validation email is sent to the user's email address + | - When true: User is created with validated=true and no validation email is sent + | - Default entitlements are granted immediately regardless of validation status + | + | Note: If email validation is required (skipEmailValidation=false), the user must click the validation link + | in the email before they can log in, even though entitlements are already granted. + | + |""", createUserJson, userJsonV200, - List(InvalidJsonFormat, InvalidStrongPasswordFormat, DuplicateUsername, ExternalUserCheckFailed, UnknownError), + List(AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidStrongPasswordFormat, DuplicateUsername, ExternalUserCheckFailed, "Error occurred during user creation.", UnknownError), List(apiTagUser, apiTagOnboarding), None, http4sPartialFunction = Some(createUser)) @@ -978,9 +1052,13 @@ object Http4s200 { null, implementedInApiVersion, nameOf(createCustomer), "POST", "/banks/BANK_ID/customers", "Create Customer", - s"""Add a customer linked to the user specified by user_id. - |Dates need to be in the format 2013-01-21T23:08:00Z - |${userAuthenticationMessage(true)}""", + s"""Add a customer linked to the user specified by user_id + |The Customer resource stores the customer number, legal name, email, phone number, their date of birth, relationship status, education attained, a url for a profile image, KYC status etc. + |This call may require additional permissions/role in the future. + |For now the authenticated user can create at most one linked customer. + |Dates need to be in the format 2013-01-21T23:08:00Z + |${userAuthenticationMessage(true)} + |""", createCustomerJson, customerJsonV140, List(InvalidBankIdFormat, AuthenticatedUserIsRequired, BankNotFound, CustomerNumberAlreadyExists, UserHasMissingRoles, UserNotFoundById, CreateConsumerError, CustomerAlreadyExistsForUser, @@ -1031,7 +1109,9 @@ object Http4s200 { """Get users by email address | |Login is required. - |CanGetAnyUser entitlement is required.""".stripMargin, + |CanGetAnyUser entitlement is required, + | + """.stripMargin, EmptyBody, usersJsonV200, List(AuthenticatedUserIsRequired, UserHasMissingRoles, UserNotFoundByEmail, UnknownError), List(apiTagUser, apiTagOldStyle), @@ -1077,8 +1157,11 @@ object Http4s200 { "/banks/BANK_ID/user_customer_links", "Create User Customer Link", s"""Link a User to a Customer - | - |${userAuthenticationMessage(true)}""", + | + |${userAuthenticationMessage(true)} + | + |$createUserCustomerLinksrequiredEntitlementsText + |""", createUserCustomerLinkJson, userCustomerLinkJson, List(AuthenticatedUserIsRequired, InvalidBankIdFormat, BankNotFound, InvalidJsonFormat, CustomerNotFoundByCustomerId, UserHasMissingRoles, CustomerAlreadyExistsForUser, @@ -1129,10 +1212,14 @@ object Http4s200 { "/users/USER_ID/entitlements", "Add Entitlement for a User", """Create Entitlement. Grant Role to User. - | - |Entitlements are used to grant System or Bank level roles to Users. - | - |Authentication is required and the user needs to be a Super Admin.""".stripMargin, + | + |Entitlements are used to grant System or Bank level roles to Users. (For Account level privileges, see Views) + | + |For a System level Role (.e.g CanGetAnyUser), set bank_id to an empty string i.e. "bank_id":"" + | + |For a Bank level Role (e.g. CanCreateAccount), set bank_id to a valid value e.g. "bank_id":"my-bank-id" + | + |Authentication is required and the user needs to be a Super Admin. Super Admins are listed in the Props file.""", code.api.ResourceDocs1_4_0.SwaggerDefinitionsJSON.createEntitlementJSON, entitlementJSON, List(AuthenticatedUserIsRequired, UserNotFoundById, UserNotSuperAdmin, InvalidJsonFormat, IncorrectRoleName, EntitlementIsBankRole, EntitlementIsSystemRole, EntitlementAlreadyExists, UnknownError), @@ -1203,7 +1290,11 @@ object Http4s200 { "Delete Entitlement", """Delete Entitlement specified by ENTITLEMENT_ID for an user specified by USER_ID | - |Authentication is required and the user needs to be a Super Admin.""".stripMargin, + |Authentication is required and the user needs to be a Super Admin. + |Super Admins are listed in the Props file. + | + | + """.stripMargin, EmptyBody, EmptyBody, List(AuthenticatedUserIsRequired, UserHasMissingRoles, EntitlementNotFound, UnknownError), List(apiTagRole, apiTagUser, apiTagEntitlement), @@ -1262,11 +1353,72 @@ object Http4s200 { null, implementedInApiVersion, nameOf(elasticSearchWarehouse), "GET", "/search/warehouse", "Search Warehouse Data Via Elasticsearch", - """Search warehouse data via Elastic Search. - | - |Login is required. - |CanSearchWarehouse entitlement is required.""", + """ + |Search warehouse data via Elastic Search. + | + |Login is required. + | + |CanSearchWarehouse entitlement is required to search warehouse data! + | + |Send your email, name, project name and user_id to the admins to get access. + | + |Elastic (search) is used in the background. See links below for syntax. + | + | + |parameters: + | + | esType - elasticsearch type + | + | simple query: + | + | q - plain_text_query + | + | df - default field to search + | + | sort - field to sort on + | + | size - number of hits returned, default 10 + | + | from - show hits starting from + | + | json query: + | + | source - JSON_query_(URL-escaped) + | + | + |Example usage: + | + |GET /search/warehouse/q=findThis + | + |or: + | + |GET /search/warehouse/source={"query":{"query_string":{"query":"findThis"}}} + | + | + |Note!! + | + |The whole JSON query string MUST be URL-encoded: + | + |* For { use %7B + |* For } use %7D + |* For : use %3A + |* For " use %22 + | + |etc.. + | + | + | + |Only q, source and esType are passed to Elastic + | + |Elastic simple query: https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html + | + |Elastic JSON query: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html + | + |You can specify the esType thus: /search/warehouse/esType=type&q=a + | + """, EmptyBody, emptyElasticSearch, + //TODO what is output here? List(AuthenticatedUserIsRequired, BankNotFound, UserHasMissingRoles, UnknownError), List(apiTagSearchWarehouse, apiTagOldStyle), Some(List(canSearchWarehouse)), @@ -1298,10 +1450,65 @@ object Http4s200 { null, implementedInApiVersion, nameOf(elasticSearchMetrics), "GET", "/search/metrics", "Search API Metrics via Elasticsearch", - """Search the API calls made to this API instance via Elastic Search. - | - |Login is required. - |CanSearchMetrics entitlement is required.""", + """ + |Search the API calls made to this API instance via Elastic Search. + | + |Login is required. + | + |CanSearchMetrics entitlement is required to search metrics data. + | + | + |parameters: + | + | esType - elasticsearch type + | + | simple query: + | + | q - plain_text_query + | + | df - default field to search + | + | sort - field to sort on + | + | size - number of hits returned, default 10 + | + | from - show hits starting from + | + | json query: + | + | source - JSON_query_(URL-escaped) + | + | + |example usage: + | + | /search/metrics/q=findThis + | + |or: + | + | /search/metrics/source={"query":{"query_string":{"query":"findThis"}}} + | + | + |Note!! + | + |The whole JSON query string MUST be URL-encoded: + | + |* For { use %7B + |* For } use %7D + |* For : use %3A + |* For " use %22 + | + |etc.. + | + | + | + |Only q, source and esType are passed to Elastic + | + |Elastic simple query: https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html + | + |Elastic JSON query: https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html + | + | + """, EmptyBody, emptyElasticSearch, List(AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), List(apiTagMetric, apiTagApi, apiTagOldStyle), diff --git a/obp-api/src/main/scala/code/api/v2_1_0/Http4s210.scala b/obp-api/src/main/scala/code/api/v2_1_0/Http4s210.scala index 64495cc76f..d80406219d 100644 --- a/obp-api/src/main/scala/code/api/v2_1_0/Http4s210.scala +++ b/obp-api/src/main/scala/code/api/v2_1_0/Http4s210.scala @@ -27,7 +27,7 @@ import code.entitlement.Entitlement import code.metrics.APIMetrics import code.model.{BankX, Consumer, UserX} import code.products.Products -import code.sandbox.{OBPDataImport, SandboxDataImport} +import code.sandbox.{OBPDataImport, SandboxData, SandboxDataImport} import code.usercustomerlinks.UserCustomerLink import code.users.Users import com.github.dwickern.macros.NameOf.nameOf @@ -65,6 +65,14 @@ object Http4s210 { val createCustomerEntitlementsRequiredText: String = createCustomerEntitlementsRequiredForSpecificBank.mkString(" and ") + " OR " + createCustomerEntitlementsRequiredForAnyBank.mkString(" and ") + // Alias preserves the typo'd name used in Lift's APIMethods210.scala + // (`createCustomeEntitlementsRequiredText` — missing the `r` in + // "Custome"). Restored descriptions interpolate the typo'd reference, + // and we keep the bug-for-bug compatibility so the restoration runs + // verbatim against Lift's source-of-truth. + private val createCustomeEntitlementsRequiredText: String = createCustomerEntitlementsRequiredText + private val getTransactionTypesIsPublic = + APIUtil.getPropsAsBoolValue("apiOptions.getTransactionTypesIsPublic", true) object Implementations2_1_0 { val prefixPath = Root / ApiPathZero.toString / implementedInApiVersion.toString @@ -117,9 +125,17 @@ object Http4s210 { null, implementedInApiVersion, nameOf(sandboxDataImport), "POST", "/sandbox/data-import", "Create sandbox", s"""Import bulk data into the sandbox (Authenticated access). - | - |${userAuthenticationMessage(true)}""", - SandboxDataImport(Nil, Nil, Nil, Nil, Nil, Nil, Nil, Nil), successMessage, + | + |This call can be used to create banks, users, accounts and transactions which are stored in the local RDBMS. + | + |The user needs to have CanCreateSandbox entitlement. + | + |Note: This is a monolithic call. You could also use a combination of endpoints including create bank, create user, create account and create transaction request to create similar data. + | + |An example of an import set of data (json) can be found [here](https://raw.githubusercontent.com/OpenBankProject/OBP-API/develop/obp-api/src/main/scala/code/api/sandbox/example_data/2016-04-28/example_import.json) + |${userAuthenticationMessage(true)} + |""", + SandboxData.importJson, successMessage, List(AuthenticatedUserIsRequired, InvalidJsonFormat, DataImportDisabled, UserHasMissingRoles, UnknownError), List(apiTagSandbox), Some(List(canCreateSandbox)), @@ -428,10 +444,35 @@ object Http4s210 { resourceDocs += ResourceDoc( null, implementedInApiVersion, nameOf(answerTransactionRequestChallenge) + trType.toLowerCase.capitalize, "POST", s"/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transaction-request-types/$trType/transaction-requests/TRANSACTION_REQUEST_ID/challenge", - s"Answer Transaction Request Challenge ($trType)", - """In Sandbox mode, any string that can be converted to a positive integer will be accepted as an answer.""", + "Answer Transaction Request Challenge", + """In Sandbox mode, any string that can be converted to a positive integer will be accepted as an answer. + | + |This endpoint expects the following data as provided in the createTransactionRequest response body: + | + |1)`TRANSACTION_REQUEST_TYPE` : as per the selected createTransactionRequest type, part of the request URL. + | + |2)`TRANSACTION_REQUEST_ID` : the value of the `id` field of the createTransactionRequest response body. + | + |3) `id` : the value of `challenge.id` in the createTransactionRequest response body. + | + |4) `answer` : Defaults to `123`, if running in sandbox mode. In production mode, the value will be sent via the configured SCA method. + | + """.stripMargin, challengeAnswerJSON, transactionRequestWithChargeJson, - answerChallengeCommonErrors, answerChallengeTags, None, + List( + AuthenticatedUserIsRequired, + InvalidBankIdFormat, + InvalidAccountIdFormat, + InvalidJsonFormat, + BankNotFound, + UserNoPermissionAccessView, + TransactionRequestStatusNotInitiated, + TransactionRequestTypeHasChanged, + InvalidTransactionRequestChallengeId, + AllowedAttemptsUsedUp, + TransactionDisabled, + UnknownError + ), List(apiTagTransactionRequest, apiTagPSD2PIS, apiTagPsd2), None, http4sPartialFunction = Some(answerTransactionRequestChallenge)) } @@ -522,7 +563,25 @@ object Http4s210 { "Get Transaction Requests.", """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.""", + |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. + | + """.stripMargin, EmptyBody, transactionRequestWithChargeJSONs210, List(AuthenticatedUserIsRequired, BankNotFound, AccountNotFound, UserHasMissingRoles, UnknownError), List(apiTagTransactionRequest, apiTagPsd2, apiTagOldStyle), None, @@ -777,9 +836,12 @@ object Http4s210 { s"""Get all users | |Login is required. - |CanGetAnyUser entitlement is required. + |CanGetAnyUser entitlement is required, + | + |${urlParametersDocument(false, false)} + |* locked_status (if null ignore) | - |${urlParametersDocument(false, false)}""", + """.stripMargin, EmptyBody, usersJsonV200, List(AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), List(apiTagUser), @@ -812,7 +874,17 @@ object Http4s210 { null, implementedInApiVersion, nameOf(createTransactionType), "PUT", "/banks/BANK_ID/transaction-types", "Create Transaction Type at bank", - s"""Create Transaction Types for the bank specified by BANK_ID.""", + // TODO get the documentation of the parameters from the scala doc of the case class we return + s"""Create Transaction Types for the bank specified by BANK_ID: + | + | * id : Unique transaction type id across the API instance. SHOULD be a UUID. MUST be unique. + | * bank_id : The bank that supports this TransactionType + | * short_code : A short code (SHOULD have no-spaces) which MUST be unique across the bank. May be stored with Transactions to link here + | * summary : A succinct summary + | * description : A longer description + | * charge : The charge to the customer for each one of these + | + |${userAuthenticationMessage(getTransactionTypesIsPublic)}""".stripMargin, transactionTypeJsonV200, transactionType, List(AuthenticatedUserIsRequired, BankNotFound, InvalidJsonFormat, InsufficientAuthorisationToCreateTransactionType, UnknownError), @@ -844,7 +916,13 @@ object Http4s210 { 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.""", + 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 + | + |${userAuthenticationMessage(!getAtmsIsPublic)}""".stripMargin, EmptyBody, atmJson, List(AuthenticatedUserIsRequired, BankNotFound, AtmNotFoundByAtmId, UnknownError), List(apiTagATM, apiTagOldStyle), None, @@ -874,7 +952,15 @@ object Http4s210 { null, implementedInApiVersion, nameOf(getBranch), "GET", "/banks/BANK_ID/branches/BRANCH_ID", "Get Bank Branch", - s"""Returns information about branch for a single bank specified by BANK_ID and BRANCH_ID.""", + s"""Returns information about branches for a single bank specified by BANK_ID and BRANCH_ID including: + | meta.license.id and eta.license.name fields must not be empty. + | + |* Name + |* Address + |* Geo Location + |* License the data under this endpoint is released under + | + |${userAuthenticationMessage(!getBranchesIsPublic)}""".stripMargin, EmptyBody, branchJson, List(AuthenticatedUserIsRequired, BranchNotFoundByBranchId, UnknownError), List(apiTagBranch, apiTagOldStyle), None, @@ -900,7 +986,18 @@ object Http4s210 { null, implementedInApiVersion, nameOf(getProduct), "GET", "/banks/BANK_ID/products/PRODUCT_CODE", "Get Bank Product", - s"""Returns information about the financial products offered by a bank specified by BANK_ID and PRODUCT_CODE.""", + s"""Returns information about the financial products offered by a bank specified by BANK_ID and PRODUCT_CODE including: + | + |* Name + |* Code + |* Category + |* Family + |* Super Family + |* More info URL + |* Description + |* Terms and Conditions + |* License the data under this endpoint is released under + |${userAuthenticationMessage(!getProductsIsPublic)}""".stripMargin, EmptyBody, productJsonV210, List(AuthenticatedUserIsRequired, ProductNotFoundByProductCode, UnknownError), List(apiTagProduct), None, @@ -925,7 +1022,18 @@ object Http4s210 { null, implementedInApiVersion, nameOf(getProducts), "GET", "/banks/BANK_ID/products", "Get Bank Products", - s"""Returns information about the financial products offered by a bank specified by BANK_ID.""", + s"""Returns information about the financial products offered by a bank specified by BANK_ID including: + | + |* Name + |* Code + |* Category + |* Family + |* Super Family + |* More info URL + |* Description + |* Terms and Conditions + |* License the data under this endpoint is released under + |${userAuthenticationMessage(!getProductsIsPublic)}""".stripMargin, EmptyBody, productsJsonV210, List(AuthenticatedUserIsRequired, BankNotFound, ProductNotFoundByProductCode, UnknownError), List(apiTagProduct), None, @@ -981,10 +1089,13 @@ object Http4s210 { "/banks/BANK_ID/customers", "Create Customer", s"""Add a customer linked to the user specified by user_id - | - |${userAuthenticationMessage(true)} - | - |$createCustomerEntitlementsRequiredText""", + |The Customer resource stores the customer number, legal name, email, phone number, their date of birth, relationship status, education attained, a url for a profile image, KYC status etc. + |Dates need to be in the format 2013-01-21T23:08:00Z + | + |${userAuthenticationMessage(true)} + | + |$createCustomeEntitlementsRequiredText + |""", postCustomerJsonV210, customerJsonV210, List(AuthenticatedUserIsRequired, BankNotFound, InvalidJsonFormat, CustomerNumberAlreadyExists, UserNotFoundById, CustomerAlreadyExistsForUser, CreateConsumerError, UnknownError), @@ -1040,7 +1151,14 @@ object Http4s210 { | |${userAuthenticationMessage(true)}""", EmptyBody, customerJSONs, - List(AuthenticatedUserIsRequired, BankNotFound, UserCustomerLinksNotFoundForUser, CustomerNotFoundByCustomerId, UnknownError), + List( + AuthenticatedUserIsRequired, + BankNotFound, + UserCustomerLinksNotFoundForUser, + UserCustomerLinksNotFoundForUser, + CustomerNotFoundByCustomerId, + UnknownError + ), List(apiTagCustomer), None, http4sPartialFunction = Some(getCustomersForCurrentUserAtBank)) @@ -1147,7 +1265,13 @@ object Http4s210 { null, implementedInApiVersion, nameOf(updateConsumerRedirectUrl), "PUT", "/management/consumers/CONSUMER_ID/consumer/redirect_url", "Update Consumer RedirectUrl", - s"""Update an existing redirectUrl for a Consumer specified by CONSUMER_ID.""", + s"""Update an existing redirectUrl for a Consumer specified by CONSUMER_ID. + | + | CONSUMER_ID can be obtained after you register the application. + | + | Or use the endpoint 'Get Consumers' to get it + | + """.stripMargin, consumerRedirectUrlJSON, consumerJSON, List(AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), List(apiTagConsumer), @@ -1176,7 +1300,61 @@ object Http4s210 { "Get Metrics", s"""Get the all metrics | - |require CanReadMetrics role""", + |require CanReadMetrics role + | + |Filters Part 1.*filtering* (no wilde cards etc.) parameters to GET /management/metrics + | + |Should be able to filter on the following metrics fields + | + |eg: /management/metrics?from_date=$DateWithMsExampleString&to_date=$DateWithMsExampleString&limit=50&offset=2 + | + |1 from_date (defaults to one week before current date): eg:from_date=$DateWithMsExampleString + | + |2 to_date (defaults to current date) eg:to_date=$DateWithMsExampleString + | + |3 limit (for pagination: defaults to 50) eg:limit=200 + | + |4 offset (for pagination: zero index, defaults to 0) eg: offset=10 + | + |5 sort_by (defaults to date field) eg: sort_by=date + | possible values: + | "url", + | "date", + | "username" (or "user_name" for backward compatibility), + | "app_name", + | "developer_email", + | "implemented_by_partial_function", + | "implemented_in_version", + | "consumer_id", + | "verb" + | + |6 direction (defaults to date desc) eg: direction=desc + | + |eg: /management/metrics?from_date=$DateWithMsExampleString&to_date=$DateWithMsExampleString&limit=10000&offset=0&anon=false&app_name=TeatApp&implemented_in_version=v2.1.0&verb=POST&user_id=c7b6cb47-cb96-4441-8801-35b57456753a&username=susan.uk.29@example.com&consumer_id=78 + | + |Other filters: + | + |7 consumer_id (if null ignore) + | + |8 user_id (if null ignore) + | + |9 anon (if null ignore) only support two value : true (return where user_id is null.) or false (return where user_id is not null.) + | + |10 url (if null ignore), note: can not contain '&'. + | + |11 app_name (if null ignore) + | + |12 implemented_by_partial_function (if null ignore), + | + |13 implemented_in_version (if null ignore) + | + |14 verb (if null ignore) + | + |15 correlation_id (if null ignore) + | + |16 duration (if null ignore) non digit chars will be silently omitted + | + """.stripMargin, EmptyBody, metricsJson, List(AuthenticatedUserIsRequired, UserHasMissingRoles, UnknownError), List(apiTagMetric, apiTagApi), From b86e2f102ccfbe91fa0e3d231f508b695fad4617 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 22 May 2026 08:18:35 +0200 Subject: [PATCH 3/4] fix(v1.2.1 + v2.0.0 + v3.1.0): needsAuthentication mismatches across 21 endpoints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CI test reports (shard2(1), shard3(1), shard4(6)) flagged a recurring class of failures: unauthenticated requests get 401 / 403 where the handler's actual auth model says 200 / 201 / 401. Root cause is the same in all cases. Lift's `APIMethodsXYZ.scala` files (the ResourceDoc parity source-of-truth per CLAUDE.md) have descriptions or errorResponseBodies that disagree with the actual Lift handler's auth behaviour. In Lift's runtime this didn't matter — `OBPRestHelper` doesn't enforce ResourceDoc errors at dispatch — but http4s's `ResourceDocMiddleware` derives `needsAuthentication` from both the errors list and the description's `authenticationIsOptional` marker, so contradictory docs land the wrong status code. Fixed endpoints (intentional drifts from Lift's source-of-truth, commented at each site): v3.1.0 (2 already fixed earlier in `14abed06c`, re-broken by the restoration in `83a2f25c4`, re-applied here): - getTransactionByIdForBankAccount: description (false)→(true) - getObpConnectorLoopback: description (true)→(false) v2.0.0 (1): - createUser: removed `AuthenticatedUserIsRequired` from errors (handler is anonymous; Lift's errors list lies) v1.2.1 (18): Anonymous endpoint with auth-required error: removed it - publicAccountsAtOneBank: removed $AuthenticatedUserIsRequired View-gated endpoints with auth-required handler but description / errors said optional. Either added $AuthenticatedUserIsRequired to errors, or flipped description userAuthenticationMessage(false)→(true), or both, to make `needsAuthentication=true`: getOtherAccountByIdForBankAccount, addCounterpartyPublicAlias, updateCounterpartyPublicAlias, deleteCounterpartyPublicAlias, getOtherAccountPrivateAlias, addOtherAccountPrivateAlias, updateCounterpartyPrivateAlias, deleteCounterpartyPrivateAlias, updateCounterpartyImageUrl, deleteCounterpartyImageUrl, addCounterpartyOpenCorporatesUrl, getTransactionNarrative, addTransactionNarrative, updateTransactionNarrative, getTagsForViewOnTransaction, deleteTagForViewOnTransaction, getWhereTagForViewOnTransaction, getOtherAccountForTransaction Per the source-of-truth rule, APIMethodsXYZ.scala files are NOT modified — the drift lives on the http4s side with an explanatory comment on each pair (the v3.1.0 and v2.0.0 changes carry the comment; the 18 v1.2.1 changes are a programmatic batch and the overall intent is captured here in the commit message). --- .../scala/code/api/v1_2_1/Http4s121.scala | 57 ++++++++++++------- .../scala/code/api/v2_0_0/Http4s200.scala | 14 ++++- .../scala/code/api/v3_1_0/Http4s310.scala | 19 ++++++- 3 files changed, 67 insertions(+), 23 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala b/obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala index ea9d7fc12f..7eb721af93 100644 --- a/obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala +++ b/obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala @@ -403,7 +403,7 @@ object Http4s121 { |Authentication via OAuth is not required.""".stripMargin, EmptyBody, accountJSON, - List(AuthenticatedUserIsRequired, UnknownError, BankNotFound), + List(UnknownError, BankNotFound), apiTagAccountPublic :: apiTagAccount :: apiTagPublicData :: apiTagOldStyle :: Nil, http4sPartialFunction = Some(publicAccountsAtOneBank) ) @@ -994,11 +994,13 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID", "Get Other Account by Id", s"""Returns data about the Other Account that has shared at least one transaction with ACCOUNT_ID at BANK_ID. - |${userAuthenticationMessage(false)} + |${userAuthenticationMessage(true)} |Authentication is required if the view is not public.""", EmptyBody, otherAccountJSON, - List(BankAccountNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + BankAccountNotFound, UnknownError), List(apiTagCounterparty, apiTagAccount), http4sPartialFunction = Some(getOtherAccountByIdForBankAccount) ) @@ -1088,7 +1090,7 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/public_alias", "Add public alias to other bank account", s"""Creates the public alias for the other account OTHER_ACCOUNT_ID. | - |${userAuthenticationMessage(false)} + |${userAuthenticationMessage(true)} |Authentication is required if the view is not public. | |Note: Public aliases are automatically generated for new 'other accounts / counterparties', so this call should only be used if @@ -1097,6 +1099,7 @@ object Http4s121 { |The VIEW_ID parameter should be a view the caller is permitted to access to and that has permission to create public aliases.""", aliasJSON, successMessage, List( + $AuthenticatedUserIsRequired, BankAccountNotFound, InvalidJsonFormat, UnknownError, @@ -1131,7 +1134,7 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/public_alias", "Update public alias of other bank account", s"""Updates the public alias of the other account / counterparty OTHER_ACCOUNT_ID. | - |${userAuthenticationMessage(false)} + |${userAuthenticationMessage(true)} |Authentication is required if the view is not public.""", aliasJSON, successMessage, List(BankAccountNotFound, InvalidJsonFormat, AuthenticatedUserIsRequired, "the view does not allow metadata access", "the view does not allow updating the public alias", "Alias cannot be updated", UnknownError), @@ -1161,10 +1164,11 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/public_alias", "Delete Counterparty Public Alias", s"""Deletes the public alias of the other account OTHER_ACCOUNT_ID. | - |${userAuthenticationMessage(false)} + |${userAuthenticationMessage(true)} |Authentication is required if the view is not public.""", EmptyBody, EmptyBody, List( + $AuthenticatedUserIsRequired, BankAccountNotFound, "the view does not allow metadata access", "the view does not allow deleting the public alias", @@ -1193,7 +1197,7 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/private_alias", "Get Other Account Private Alias", s"""Returns the private alias of the other account OTHER_ACCOUNT_ID. | - |${userAuthenticationMessage(false)} + |${userAuthenticationMessage(true)} |Authentication is required if the view is not public.""", EmptyBody, aliasJSON, List(AuthenticatedUserIsRequired, BankAccountNotFound, "the view does not allow metadata access", "the view does not allow private alias access", UnknownError), @@ -1223,7 +1227,7 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/private_alias", "Create Other Account Private Alias", s"""Creates a private alias for the other account OTHER_ACCOUNT_ID. | - |${userAuthenticationMessage(false)} + |${userAuthenticationMessage(true)} |Authentication is required if the view is not public.""", aliasJSON, successMessage, List(AuthenticatedUserIsRequired, BankAccountNotFound, InvalidJsonFormat, "the view does not allow metadata access", "the view does not allow adding a private alias", "Alias cannot be added", UnknownError), @@ -1253,7 +1257,7 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/private_alias", "Update Counterparty Private Alias", s"""Updates the private alias of the counterparty (AKA other account) OTHER_ACCOUNT_ID. | - |${userAuthenticationMessage(false)} + |${userAuthenticationMessage(true)} |Authentication is required if the view is not public.""", aliasJSON, successMessage, List(AuthenticatedUserIsRequired, BankAccountNotFound, InvalidJsonFormat, "the view does not allow metadata access", "the view does not allow updating the private alias", "Alias cannot be updated", UnknownError), @@ -1283,7 +1287,7 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/private_alias", "Delete Counterparty Private Alias", s"""Deletes the private alias of the other account OTHER_ACCOUNT_ID. | - |${userAuthenticationMessage(false)} + |${userAuthenticationMessage(true)} |Authentication is required if the view is not public.""", EmptyBody, EmptyBody, List(AuthenticatedUserIsRequired, BankAccountNotFound, "the view does not allow metadata access", "the view does not allow deleting the private alias", "Alias cannot be deleted", UnknownError), @@ -1511,7 +1515,8 @@ object Http4s121 { "Update Counterparty Image Url", "Update the url that points to the logo of the counterparty", imageUrlJSON, successMessage, List( - BankAccountNotFound, + $AuthenticatedUserIsRequired, + BankAccountNotFound, InvalidJsonFormat, "the view does not allow metadata access", "the view does not allow updating an image url", @@ -1543,7 +1548,9 @@ object Http4s121 { "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/other_accounts/OTHER_ACCOUNT_ID/metadata/image_url", "Delete Counterparty Image URL", "Delete image url of other bank account", EmptyBody, EmptyBody, - List(UnknownError), + List( + $AuthenticatedUserIsRequired, + UnknownError), List(apiTagCounterpartyMetaData, apiTagCounterparty), http4sPartialFunction = Some(deleteCounterpartyImageUrl) ) @@ -1571,7 +1578,8 @@ object Http4s121 { "Add Open Corporates URL to Counterparty", "Add open corporates url to other bank account", openCorporateUrlJSON, successMessage, List( - BankAccountNotFound, + $AuthenticatedUserIsRequired, + BankAccountNotFound, InvalidJsonFormat, "the view does not allow metadata access", "the view does not allow adding an open corporate url", @@ -1893,7 +1901,8 @@ object Http4s121 { |Authentication via OAuth is required if the view is not public.""", EmptyBody, transactionNarrativeJSON, List( - BankAccountNotFound, + $AuthenticatedUserIsRequired, + BankAccountNotFound, NoViewPermission, ViewNotFound, UnknownError), @@ -1949,7 +1958,8 @@ object Http4s121 { |""", transactionNarrativeJSON, successMessage, List( - InvalidJsonFormat, + $AuthenticatedUserIsRequired, + InvalidJsonFormat, BankAccountNotFound, NoViewPermission, ViewNotFound, @@ -2000,7 +2010,9 @@ object Http4s121 { | |Authentication via OAuth is required if the view is not public.""", transactionNarrativeJSON, successMessage, - List(InvalidJsonFormat, + List( + $AuthenticatedUserIsRequired, + InvalidJsonFormat, BankAccountNotFound, NoViewPermission, ViewNotFound, @@ -2166,6 +2178,7 @@ object Http4s121 { Authentication via OAuth is required if the view is not public.""", EmptyBody, transactionTagJSON, List( + $AuthenticatedUserIsRequired, BankAccountNotFound, NoViewPermission, ViewNotFound, @@ -2250,7 +2263,9 @@ object Http4s121 { |or must be the user that posted the tag. |""".stripMargin, EmptyBody, EmptyBody, - List(NoViewPermission, + List( + $AuthenticatedUserIsRequired, + NoViewPermission, ViewNotFound, UnknownError), List(apiTagTransactionMetaData, apiTagTransaction), @@ -2405,7 +2420,9 @@ object Http4s121 { | |Authentication via OAuth is required if the view is not public.""", EmptyBody, transactionWhereJSON, - List(BankAccountNotFound, + List( + $AuthenticatedUserIsRequired, + BankAccountNotFound, NoViewPermission, ViewNotFound, UnknownError), @@ -2581,7 +2598,9 @@ object Http4s121 { |Returns details of the other party involved in the transaction, moderated by the [view](#1_2_1-getViewsForBankAccount) (VIEW_ID). Authentication via OAuth is required if the view is not public.""", EmptyBody, otherAccountJSON, - List(BankAccountNotFound, UnknownError), + List( + $AuthenticatedUserIsRequired, + BankAccountNotFound, UnknownError), List(apiTagTransaction, apiTagCounterparty), http4sPartialFunction = Some(getOtherAccountForTransaction) ) diff --git a/obp-api/src/main/scala/code/api/v2_0_0/Http4s200.scala b/obp-api/src/main/scala/code/api/v2_0_0/Http4s200.scala index 36341bf42a..c054edc48f 100644 --- a/obp-api/src/main/scala/code/api/v2_0_0/Http4s200.scala +++ b/obp-api/src/main/scala/code/api/v2_0_0/Http4s200.scala @@ -999,7 +999,19 @@ object Http4s200 { | |""", createUserJson, userJsonV200, - List(AuthenticatedUserIsRequired, InvalidJsonFormat, InvalidStrongPasswordFormat, DuplicateUsername, ExternalUserCheckFailed, "Error occurred during user creation.", UnknownError), + // Intentional drift from Lift's APIMethods200.scala source-of-truth: + // `AuthenticatedUserIsRequired` removed. Lift's createUser handler is + // anonymous (no authenticatedAccess / anonymousAccess wrapper — straight + // into JSON extraction), but Lift's ResourceDoc errors list includes + // `AuthenticatedUserIsRequired`. Lift's runtime ignored that contradiction + // because OBPRestHelper doesn't enforce ResourceDoc errors at request + // dispatch time. The http4s `ResourceDocMiddleware` does — it derives + // `needsAuthentication = errors.contains($AuthenticatedUserIsRequired) || + // roles.nonEmpty`, so leaving the error in makes middleware reject + // unauthenticated POST /users with 401 instead of letting the handler + // run and return 201 (or 409 on duplicate). Same pattern as upstream + // commit 14abed06c for v3.1.0's getObpConnectorLoopback. + List(InvalidJsonFormat, InvalidStrongPasswordFormat, DuplicateUsername, ExternalUserCheckFailed, "Error occurred during user creation.", UnknownError), List(apiTagUser, apiTagOnboarding), None, http4sPartialFunction = Some(createUser)) 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 870f59a4c0..066661aabb 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 @@ -1512,10 +1512,16 @@ object Http4s310 { "GET", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/transaction", "Get Transaction by Id", + // Intentional drift from Lift's APIMethods310.scala source-of-truth. + // Lift's description had userAuthenticationMessage(false) (auth optional), + // but Lift's handler uses authenticatedAccess(cc) (auth required). The + // ResourceDoc constructor removed $AuthenticatedUserIsRequired from errors + // when the description claimed auth was optional, making middleware return + // 403 (view-permission check) for unauthenticated requests instead of 401. + // See upstream commit 14abed06c. s"""Returns one transaction specified by TRANSACTION_ID of the account ACCOUNT_ID and [moderated](#1_2_1-getViewsForBankAccount) by the view (VIEW_ID). | - |${userAuthenticationMessage(false)} - |Authentication is required if the view is not public. + |${userAuthenticationMessage(true)} | | |""", @@ -4882,9 +4888,16 @@ object Http4s310 { "GET", "/connector/loopback", "Get Connector Status (Loopback)", + // Intentional drift from Lift's APIMethods310.scala source-of-truth. + // Lift's description had userAuthenticationMessage(true) (auth required), + // but Lift's handler uses anonymousAccess(cc) (no auth required). The + // ResourceDoc constructor added $AuthenticatedUserIsRequired to errors, + // setting needsAuthentication=true so middleware returned 401 instead of + // letting the handler return 400 NotImplemented. See upstream commit + // 14abed06c. s"""This endpoint makes a call to the Connector to check the backend transport is reachable. (Deprecated) | - |${userAuthenticationMessage(true)} + |${userAuthenticationMessage(false)} | |""", EmptyBody, From cc15bed05bc44d02e59ae2f1c340a00f052a0af4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Mili=C4=87?= Date: Fri, 22 May 2026 08:49:24 +0200 Subject: [PATCH 4/4] =?UTF-8?q?fix(v1.2.1):=20addTransactionNarrative=20ne?= =?UTF-8?q?edsAuth=20=E2=80=94=20flip=20description=20(false)=E2=86=92(tru?= =?UTF-8?q?e)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit shard3(2) CI report shows 18 of 19 v1.2.1 failures from the previous run (b86e2f102) are resolved; addTransactionNarrative still returns 403 for unauthenticated requests instead of 401. The previous fix added \$AuthenticatedUserIsRequired to the errors list, but the description still had userAuthenticationMessage(false). The ResourceDoc constructor strips \$AuthenticatedUserIsRequired from errors when the description carries `authenticationIsOptional`, so my error-list addition was silently undone. Flip the description marker to (true) so the constructor leaves \$AuthenticatedUserIsRequired in place → middleware enforces 401 for unauthenticated requests, as the handler intends. accountById has the same (false) flag in its description, but it's a genuinely conditional endpoint (anonymous OK on public views, auth required on private views) and has not surfaced as a test failure; leave it alone. Per the source-of-truth rule, APIMethods121.scala is not modified. --- obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala b/obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala index 7eb721af93..bde8257772 100644 --- a/obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala +++ b/obp-api/src/main/scala/code/api/v1_2_1/Http4s121.scala @@ -1948,13 +1948,18 @@ object Http4s121 { null, implementedInApiVersion, nameOf(addTransactionNarrative), "POST", "/banks/BANK_ID/accounts/ACCOUNT_ID/VIEW_ID/transactions/TRANSACTION_ID/metadata/narrative", "Add a Transaction Narrative", + // Intentional drift from Lift's APIMethods121.scala source-of-truth. + // Lift's description had userAuthenticationMessage(false), but the handler + // is authenticated. The ResourceDoc constructor strips $AuthenticatedUserIsRequired + // from errorResponseBodies when description carries `authenticationIsOptional`, + // making the middleware skip the 401 — view-permission check then returned + // 403 for unauthenticated requests. Flip the marker to (true). s"""Creates a description of the transaction TRANSACTION_ID. | |Note: Unlike other items of metadata, there is only one "narrative" per transaction accross all views. |If you set narrative via a view e.g. view-x it will be seen via view-y (as long as view-y has permission to see the narrative). | - |${userAuthenticationMessage(false)} - |Authentication is required if the view is not public. + |${userAuthenticationMessage(true)} |""", transactionNarrativeJSON, successMessage, List(