diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..ed8a60fda --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,19 @@ +## Description + +A few sentences describing the overall goals of the pull request's commits. + +## Checklist + + + + - [ ] I made sure, I read [CONTRIBUTING.md](CONTRIBUTING.md) to put right branch prefix as per my need. + - [ ] I made sure to update [CHANGELOG.md](CHANGELOG.md). + - [ ] I made sure to update [Stream Wiki](https://github.com/Backbase/stream-services/wiki)(only valid in case of new stream module or architecture changes). + - [ ] My changes are adequately tested. + - [ ] I made sure all the SonarCloud Quality Gate are passed. diff --git a/.github/workflows/pull-request-body-validation.yml b/.github/workflows/pull-request-body-validation.yml new file mode 100644 index 000000000..f7435c5ff --- /dev/null +++ b/.github/workflows/pull-request-body-validation.yml @@ -0,0 +1,27 @@ +name: Pull Request Body Validation + +# +# IMPORTANT TO KNOW +# +# - This workflow runs whenever new changes is Opened, reopened, edited or changes pushed to an existing Pull Request +# - It validates if all task lists in the pull request are completed +# - It validates if description in the pull request is updated by PR owner +# + +on: + pull_request: + types: [opened, edited, synchronize, reopened] +jobs: + pull-request-validation: + runs-on: ubuntu-latest + steps: + - name: Check PR description + uses: JJ/github-pr-contains-action@releases/v10 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + bodyDoesNotContain: "A few sentences describing the overall goals of the pull request's commits" + + - name: Check for incomplete task list items + uses: Shopify/task-list-checker@main + with: + github-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 986feeb6b..b297388cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,140 @@ # Changelog All notable changes to this project will be documented in this file. +## [3.60.2](https://github.com/Backbase/stream-services/compare/3.60.1...3.60.2) +### Changed +- Fix formatting of StreamTask error messages +- Fix message placeholders of `AccessGroupService` + +## [3.60.1](https://github.com/Backbase/stream-services/compare/3.60.0...3.60.1) +### Added +- Add populating the user manager cache with user profile data. + +## [3.60.0](https://github.com/Backbase/stream-services/compare/3.59.0...3.60.0) +### Added +- Adding the ability to retrieve the arrangement a payment belongs to. +### Changed +- updated application-local configurations to be aligned with the new Backbase Local env. + +## [3.59.0](https://github.com/Backbase/stream-services/compare/3.58.2...3.59.0) +### Added +- Add update identity user attributes in case it's previously created. +## [3.58.2](https://github.com/Backbase/stream-services/compare/3.58.1...3.58.2) +### Changed +- Avoiding race condition when assigning a realm to a legal entity when ingesting multiple subsidiaries at the same time. + +## [3.58.0](https://github.com/Backbase/stream-services/compare/3.57.0...3.58.0) +### Changed +- For the IMPORT_FROM_IDENTIY strategy use the user manager importIdentity API call + +## [3.57.0](https://github.com/Backbase/stream-services/compare/3.56.0...3.57.0) +### Added +- Add feature flag for limits ingestion default is true, `backbase.stream.limits.worker.enabled` +## [3.56.0](https://github.com/Backbase/stream-services/compare/3.55.0...3.56.0) +### Changed +- Fix missing portfolio aggregation model attribute `externalId` +## [3.55.0](https://github.com/Backbase/stream-services/compare/3.54.0...3.55.0) +### Changed +- Removed the no-scs tag used during the docker image build. The latest baas use service bus and these libraries will be required. +## [3.53.0](https://github.com/Backbase/stream-services/compare/3.52.0...3.53.0) +### Changed +- Fixed a bug that was causing the Product Composition integration with Transaction Composition to fail due to a missing required request parameter (`arrangementId`) + +## [3.52.0](https://github.com/Backbase/stream-services/compare/3.51.0...3.52.0) +### Changed +- Fixed two small bugs caused by introduction of UserKindSegmentationSage + add check for LE not having users before processing. (should fix error when LE is empty) + add AudiencesSegmentationConfiguration to LegalEntitySagaConfiguration + +## [3.51.0](https://github.com/Backbase/stream-services/compare/3.50.0...3.51.0) +### Changed +- Upgrading Stream from `2023.06` to `2023.06.2` +- +## [3.50.0](https://github.com/Backbase/stream-services/compare/3.49.0...3.50.0) +### Changed +- Upgrading Stream from `2023.02-LTS` to `2023.06` +- Updated Portfolio's position stream contract to include new fields + +## [3.49.0](https://github.com/Backbase/stream-services/compare/3.48.0...3.49.0) +### Added +- Added Support for ingesting LE customers into audiences user-kind segments (for both Retail and Business banking) +- The customer category is taken from the LE and if not present the default property will be used. +- UserKindSegmentationSage Needs to be enabled explicitly through these properties: + ```properties + backbase.stream.audiences.segmentation.user-kind.enabled=true|false + backbase.stream.audiences.segmentation.user-kind.default-customer-category=RETAIL|BUSINESS + ``` + +## [3.48.0](https://github.com/Backbase/stream-services/compare/3.47.0...3.48.0) +### Changed +- Updated UserService.createOrImportIdentityUser to populate user's additions to DBS with IMPORT_FROM_IDENTITY linking strategy + +## [3.47.0](https://github.com/Backbase/stream-services/compare/3.46.0...3.47.0) +### Changed +- Removed unused parameter from Payment Orders filter request. +The `from` parameter was set to Integer.MAX_VALUE and that was causing errors on newer Backbase versions. + +## [3.46.0](https://github.com/Backbase/stream-services/compare/3.45.0...3.46.0) +### Added +- Ingestion mode configuration for legal-entity and product compositions + + New properties for `legal-entity-composition`: + ```properties + backbase.stream.compositions.legal-entity.ingestion-mode.function-groups=UPSERT|REPLACE + backbase.stream.compositions.legal-entity.ingestion-mode.data-groups=UPSERT|REPLACE + backbase.stream.compositions.legal-entity.ingestion-mode.arrangements=UPSERT|REPLACE + ``` + + New properties for `product-composition`: + ```properties + backbase.stream.compositions.product.ingestion-mode.function-groups=UPSERT|REPLACE + backbase.stream.compositions.product.ingestion-mode.data-groups=UPSERT|REPLACE + backbase.stream.compositions.product.ingestion-mode.arrangements=UPSERT|REPLACE + ``` + Defaults stay the same as before (`UPSERT` for all) + +## [3.45.0](https://github.com/Backbase/stream-services/compare/3.44.0...3.45.0) +### Added +- Added support for loans ingestion into DBS. +- Included the ingestion of loans to BatchProductIngestionSage where productGroups are being processed. +- After related arrangements were created in Products capability the loans part is ingested to Loans. + +## [3.44.0](https://github.com/Backbase/stream-services/compare/3.43.0...3.44.0) +### Added +- Added Pull request body(description and checklist) validation workflow + +## [3.43.0](https://github.com/Backbase/stream-services/compare/3.42.0...3.43.0) +### Changed +- Upgraded Access-Control service-api from v2 to v3 + +## [3.42.0](https://github.com/Backbase/stream-services/compare/3.41.0...3.42.0) +### Added +- Added Pull Request template for Stream contributors. + +## [3.41.0](https://github.com/Backbase/stream-services/compare/3.40.0...3.41.0) +### Added +- Added additions to `DebitCardItem` openapi schema. + +## [3.40.0](https://github.com/Backbase/stream-services/compare/3.39.2...3.40.0) +### Added +- Add support for propagating additions from the legal entity ingestion request. + +## [3.39.2](https://github.com/Backbase/stream-services/compare/3.39.0...3.40.1) +### Changed +- Propagate additions returned by the Product and Transaction integration Response to the Product and Transaction composition Response. + +## [3.39.1](https://github.com/Backbase/stream-services/compare/3.39.0...3.40.1) +### Changed +- Bugfix for putAssignUserPermissions error `Data groups cannot be duplicated in scope of a single function group during assigning permissions` + +## [3.39.0](https://github.com/Backbase/stream-services/compare/3.38.0...3.39.0) +### Added +- Add PermissionSetApi to stream-dbs-clients for APS ingestion scenarios + +## [3.38.0](https://github.com/Backbase/stream-services/compare/3.37.0...3.38.0) +### Added +- Updated Product Mapper to map panSuffix to all product types `panSuffix` to `number` + ## [3.37.0](https://github.com/Backbase/stream-services/compare/3.36.0...3.37.0) ### Added - Adds `bankBranchCode` field to `TermDeposit` openapi schema diff --git a/README.md b/README.md index 1ba1df33d..fb8586016 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ The Services are listed with Service name and how they are packaged Currently, the following DBS services are exposed as Stream Components: * [Stream Legal Entity](stream-legal-entity) (Lib, Rest, Task) → Legal Entity Ingestion Service that orchestrate all calls to DBS from a single aggregate model. This service is exposed as a library and a REST full service. Supports retry of the aggregate and uses the Legal Entity Ingestion Model to have a single interface into DBS. Requires Access Control, Product Summary * [Stream Product Catalog](stream-product-catalog) (Lib, Rest, Task) → Enabled bootstrapping of product types into DBS. Product Types are currently hardcoded in the streamTask definition. Orchestrates calls into Product Summary +* [Stream Loans](stream-loans) (Lib, Rest, Task) → Enabled bootstrapping of loans into DBS. Loans are currently hardcoded in the streamTask definition. Orchestrate calls into Loans after the products were ingested into Product Summary * [Stream Access Control](stream-access-control) (Lib) → The Stream Access Control library provides access to Access Control services from a single library and provides an easier abstraction layer to use these services. It mixes access to persistence and service api's to enable proper service to service comms for non DBS services such as Stream. Requires Access Control, Product Summary * [Stream-Cursor](stream-cursor) (Lib, Rest, Source) → The Stream Cursor Source is listening to predefined DBS events such as On Login. For each login event, it will retrieve a list of products from entitlements and creates an Ingestion Cursor per product. Cursors can be stored in a RDBMS and published on a HTTP Web Socket or Spring Cloud Dataflow Source. Requires RDBMS, Access Control, Product Summary and Transactions. Login Event received from Authentication Starter or Identity (with Audit Events enabled) * [Stream Transactions](stream-transactions) (Lib, Rest) → Allows ingestion into DBS in a controlled way with support for retry, rate limiting and configurable batch sizes. @@ -41,7 +42,8 @@ You can find listed here the API specification containing the opinionated model | Stream [version](https://github.com/Backbase/stream-services/releases) | DBS version | Java version | |------------------------------------------------------------------------|--------------------|--------------| -| 3.34.0 to latest | 2023.02-LTS | 17 | +| 3.50.0 to latest | 2023.06 | 17 | +| 3.34.0 to 3.49.0 | 2023.02-LTS | 17 | | 3.16.0 to 3.33.0 | 2022.10 | 17 | | 3.8.0 to 3.15.0 | 2022.09 | 17 | | 3.1.0 to 3.7.0 | 2022.09 | 11 | diff --git a/api/stream-legal-entity/openapi.yaml b/api/stream-legal-entity/openapi.yaml index ade425c52..e5878bb67 100644 --- a/api/stream-legal-entity/openapi.yaml +++ b/api/stream-legal-entity/openapi.yaml @@ -2,7 +2,7 @@ openapi: 3.0.1 info: title: Ingest Legal Entity API description: Ingestion Saga for Legal Entities and their products - version: 2.3.0 + version: 2.3.1 x-logo: url: "http://www.backbase.com/wp-content/uploads/2017/04/backbase-logo-png.png" backgroundColor: "#FFFFFF" @@ -666,6 +666,97 @@ components: $ref: '#/components/schemas/BBAN' bankBranchCode: $ref: '#/components/schemas/BankBranchCode' + type: + $ref: '#/components/schemas/LoanType' + status: + $ref: '#/components/schemas/LoanStatus' + nextRepaymentDateTime: + $ref: '#/components/schemas/NextRepaymentDateTime' + nextRepaymentAmount: + $ref: '#/components/schemas/NextRepaymentAmount' + isOverdue: + $ref: '#/components/schemas/IsOverdue' + overduePaymentsCount: + $ref: '#/components/schemas/OverduePaymentsCount' + borrowers: + $ref: '#/components/schemas/Borrowers' + defaultSettlementAccountNumber: + $ref: '#/components/schemas/DefaultSettlementAccountNumber' + defaultSettlementAccountInternalId: + $ref: '#/components/schemas/DefaultSettlementAccountInternalId' + defaultSettlementAccountExternalId: + $ref: '#/components/schemas/DefaultSettlementAccountExternalId' + isFullyRepaid: + $ref: '#/components/schemas/IsFullyRepaid' + feesDueAmount: + $ref: '#/components/schemas/FeesDueAmount' + paidAmount: + $ref: '#/components/schemas/PaidAmount' + termCount: + $ref: '#/components/schemas/TermCount' + paymentFrequency: + $ref: '#/components/schemas/FrequencyUnit' + collaterals: + $ref: '#/components/schemas/Collaterals' + escrowAttributes: + $ref: '#/components/schemas/EscrowAttributes' + revolving: + $ref: '#/components/schemas/Revolving' + renewDateTime: + $ref: '#/components/schemas/RenewDateTime' + totalDirectAmortization: + $ref: '#/components/schemas/TotalDirectAmortization' + totalIndirectAmortization: + $ref: '#/components/schemas/TotalIndirectAmortization' + totalDirectAmortizationUnit: + $ref: '#/components/schemas/TermUnit' + totalIndirectAmortizationUnit: + $ref: '#/components/schemas/TermUnit' + remainingTermUnit: + $ref: '#/components/schemas/TermUnit' + remainingTermCount: + $ref: '#/components/schemas/TermCount' + approvalDateTime: + $ref: '#/components/schemas/DateTime' + numberOfPaymentsMade: + $ref: '#/components/schemas/NumberOfPaymentsMade' + totalNumberOfPayments: + $ref: '#/components/schemas/TotalNumberOfPayments' + taxesOnInterestAmount: + $ref: '#/components/schemas/TaxesOnInterestAmount' + contractLatePaymentCommissionAmount: + $ref: '#/components/schemas/ContractLatePaymentCommissionAmount' + calculationPeriodStartDateTime: + $ref: '#/components/schemas/CalculationPeriodStartDateTime' + calculationPeriodEndDateTime: + $ref: '#/components/schemas/CalculationPeriodEndDateTime' + actualLatePaymentCommissionAmount: + $ref: '#/components/schemas/ActualLatePaymentCommissionAmount' + latePaymentCommissionTaxesAmount: + $ref: '#/components/schemas/LatePaymentCommissionTaxesAmount' + overdueInterestAmount: + $ref: '#/components/schemas/OverdueInterestAmount' + overdueTaxesOnInterestAmount: + $ref: '#/components/schemas/OverdueTaxesOnInterestAmount' + overdueEscrowPaymentAmount: + $ref: '#/components/schemas/OverdueEscrowPaymentAmount' + overduePrincipalPaymentAmount: + $ref: '#/components/schemas/OverduePrincipalPaymentAmount' + overduePenaltyAmount: + $ref: '#/components/schemas/PenaltyAmount' + totalAnnualCostPercentage: + $ref: '#/components/schemas/TotalAnnualCostPercentage' + minimumDueAmount: + $ref: '#/components/schemas/MinimumDueAmount' + lastPaymentAmount: + $ref: '#/components/schemas/LastPaymentAmount' + lastPaymentDateTime: + $ref: '#/components/schemas/LastPaymentDateTime' + interestPaymentFrequency: + $ref: '#/components/schemas/FrequencyUnit' + documentMetadatas: + $ref: '#/components/schemas/DocumentMetadataArray' + TermDeposit: allOf: - $ref: '#/components/schemas/BaseProduct' @@ -883,6 +974,10 @@ components: maxLength: 32 type: string description: Status of the card ex. Active, Expired etc + additions: + type: object + additionalProperties: + type: string CardDetailsItem: required: - cardProvider @@ -975,6 +1070,8 @@ components: $ref: '#/components/schemas/ExternalIdentifier' legalEntityType: $ref: '#/components/schemas/LegalEntityType' + customerCategory: + $ref: '#/components/schemas/CustomerCategory' realmName: type: string description: Realm to which the Legal Entity should be mapped. @@ -1238,6 +1335,33 @@ components: $ref: '#/components/schemas/QuarterlyLimit' yearly: $ref: '#/components/schemas/YearlyLimit' + businessFunctionLimits: + type: array + items: + $ref: '#/components/schemas/BusinessFunctionLimit' + BusinessFunctionLimit: + title: BusinessFunctionLimit + type: object + required: + - functionId + - shadow + - privilege + properties: + functionId: + description: |- + Unique ID in the system + Guidelines: .<1xxx> + maxLength: 36 + minLength: 1 + type: string + example: "bankA.1000" + shadow: + description: shadow limit or not + type: boolean + default: false + example: true + privilege: + $ref: '#/components/schemas/Privilege' UserProfile: title: UserProfile description: | @@ -2226,6 +2350,223 @@ components: type: string description: Additional properties + ############################################### + ## Loan fields + ############################################### + LoanType: + type: string + description: Type of a loan. + LoanStatus: + type: string + description: Status of a loan. + enum: + - Active + - Inactive + - Pending + NextRepaymentDateTime: + type: string + format: date-time + description: Datetime of a next payment. + NextRepaymentAmount: + type: number + format: double + description: Amount of a next payment. + IsOverdue: + type: boolean + OverduePaymentsCount: + type: integer + Borrowers: + type: array + items: + type: string + description: Borrower(s) + DefaultSettlementAccountNumber: + type: string + description: | + Default settlement account number. If defaultSettlementAccountId is specified and the account is + from dbs then account number will be taken from it and this field will be ignored. + DefaultSettlementAccountInternalId: + type: string + DefaultSettlementAccountExternalId: + type: string + IsFullyRepaid: + type: boolean + FeesDueAmount: + type: number + format: double + PaidAmount: + type: number + format: double + TermCount: + type: integer + PaymentFrequency: + type: integer + Revolving: + type: boolean + description: Shows if a loan is revolving + RenewDateTime: + type: string + description: Shows if loan is fully repaid + format: date-time + TotalDirectAmortization: + type: number + format: double + TotalIndirectAmortization: + type: number + format: double + NumberOfPaymentsMade: + type: integer + TotalNumberOfPayments: + type: integer + DateTime: + type: string + format: date-time + TaxesOnInterestAmount: + type: number + format: double + ContractLatePaymentCommissionAmount: + type: number + description: Late payment commission amount (contract condition) + format: double + CalculationPeriodStartDateTime: + type: string + format: date-time + CalculationPeriodEndDateTime: + type: string + format: date-time + ActualLatePaymentCommissionAmount: + type: number + description: Late payment commission (actual accrual condition) + format: double + LatePaymentCommissionTaxesAmount: + type: number + format: double + OverdueInterestAmount: + type: number + format: double + OverdueTaxesOnInterestAmount: + type: number + format: double + OverdueEscrowPaymentAmount: + type: number + format: double + OverduePrincipalPaymentAmount: + type: number + format: double + PenaltyAmount: + type: number + format: double + TotalAnnualCostPercentage: + type: number + format: double + MinimumDueAmount: + type: number + format: double + LastPaymentAmount: + type: number + description: Specifies the last payment amount that was posted as a transaction + format: double + LastPaymentDateTime: + type: string + format: date-time + StartDateTime: + type: string + format: date-time + EndDateTime: + type: string + format: date-time + FrequencyUnit: + type: string + description: Frequencu units. + enum: + - "Weekly" + - "Biweekly" + - "Twicemonthly" + - "Monthly" + - "Fourweeks" + - "Bimonthly" + - "Quarterly" + - "Semiannually" + - "Annually" + - "Maturity" + DocumentMetadata: + type: object + description: A loan document metadata. + properties: + id: + maxLength: 50 + minLength: 1 + type: string + description: Identifier of the document. + name: + maxLength: 50 + minLength: 1 + type: string + description: Name of the document. + contentType: + maxLength: 50 + minLength: 1 + type: string + description: Content type of document. + DocumentMetadataArray: + type: array + items: + $ref: '#/components/schemas/DocumentMetadata' + + Collaterals: + type: array + items: + $ref: '#/components/schemas/Collateral' + Collateral: + type: object + properties: + type: + type: string + description: Type of a collateral + currentValue: + type: number + format: double + description: Current evaluetion of a collateral + currencyCode: + type: string + description: The alpha-3 code (complying with ISO 4217) of the currency that qualifies the amount + specification: + type: string + description: Specification of a collateral (e.g. Serial numbers, Address data, etc) + nextRevaluationDateTime: + type: string + format: date-time + description: Next revaluation date + EscrowAttributes: + type: object + properties: + totalAmount: + type: number + format: double + description: A total amount set aside for Escrow + insurance: + type: number + format: double + description: Insurance amount set aside for Escrow + fees: + type: number + format: double + description: Fee amount set aside for Escrow + tax: + type: number + format: double + description: Tax amount set aside for Escrow + pmi: + type: number + format: double + description: PMI amount set aside for Escrow + other: + type: number + format: double + description: Other amount set aside for Escrow + + + ############################################### ## Additional Data Group Items ############################################### @@ -2529,6 +2870,13 @@ components: enum: - "ADD" - "REMOVE" + CustomerCategory: + type: string + title: Customer Category + description: Property to differentiate between Retail and SME Service Agreement / Legal Entity. + enum: + - RETAIL + - BUSINESS examples: RootLegalEntityHierarchyExample: diff --git a/api/stream-portfolio/schemas/v1/aggregate-portfolio.yaml b/api/stream-portfolio/schemas/v1/aggregate-portfolio.yaml index 59e602a3e..c15bc0ac3 100644 --- a/api/stream-portfolio/schemas/v1/aggregate-portfolio.yaml +++ b/api/stream-portfolio/schemas/v1/aggregate-portfolio.yaml @@ -4,7 +4,10 @@ type: object properties: id: type: string - description: Unique identificator for an aggregate portfolio, used to identify aggregate portfolio at a bank or an investment firm. + description: Unique identification for an aggregate portfolio, used to identify aggregate portfolio at a bank or an investment firm. + externalId: + type: string + description: Unique identification for an aggregate portfolio externalId. name: type: string description: Name of an aggregate portfolio, characterising total wealth of the end-user. diff --git a/api/stream-portfolio/schemas/v1/position.yaml b/api/stream-portfolio/schemas/v1/position.yaml index 1f4003274..3f386fa81 100644 --- a/api/stream-portfolio/schemas/v1/position.yaml +++ b/api/stream-portfolio/schemas/v1/position.yaml @@ -31,6 +31,12 @@ properties: unrealizedPL: $ref: money.yaml description: Unrealized current profit or loss on an open position, expressed as an absolute number. + unrealizedPLLocalPct: + type: number + description: Unrealized local current profit or loss on an open position, expressed as a percentage. + unrealizedPLLocal: + $ref: money.yaml + description: Unrealized local current profit or loss on an open position, expressed as an absolute number. todayPLPct: type: number description: Today profit or loss on an open position, expressed as a percentage. diff --git a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/configuration/AccessControlConfiguration.java b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/configuration/AccessControlConfiguration.java index 455a3af32..2a89abfc3 100644 --- a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/configuration/AccessControlConfiguration.java +++ b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/configuration/AccessControlConfiguration.java @@ -1,16 +1,10 @@ package com.backbase.stream.configuration; -import com.backbase.dbs.accesscontrol.api.service.v2.DataGroupApi; -import com.backbase.dbs.accesscontrol.api.service.v2.DataGroupsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.FunctionGroupApi; -import com.backbase.dbs.accesscontrol.api.service.v2.FunctionGroupsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.LegalEntitiesApi; -import com.backbase.dbs.accesscontrol.api.service.v2.LegalEntityApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementQueryApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.UserQueryApi; -import com.backbase.dbs.accesscontrol.api.service.v2.UsersApi; +import com.backbase.dbs.accesscontrol.api.service.v3.DataGroupsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.FunctionGroupsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.LegalEntitiesApi; +import com.backbase.dbs.accesscontrol.api.service.v3.ServiceAgreementsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.UsersApi; import com.backbase.dbs.user.api.service.v2.IdentityManagementApi; import com.backbase.dbs.user.api.service.v2.UserManagementApi; import com.backbase.dbs.user.profile.api.service.v2.UserProfileManagementApi; @@ -40,7 +34,7 @@ public class AccessControlConfiguration { @Bean - public BatchResponseUtils batchResponseUtils(){ + public BatchResponseUtils batchResponseUtils() { return new BatchResponseUtils(); } @@ -55,15 +49,16 @@ public EntitlementsService entitlementsService(ArrangementService arrangementSer @Bean public LegalEntityService legalEntityService(LegalEntitiesApi legalEntitiesApi, - LegalEntityApi legalEntityApi, BatchResponseUtils batchResponseUtils) { - return new LegalEntityService(legalEntitiesApi, legalEntityApi, batchResponseUtils); + BatchResponseUtils batchResponseUtils) { + return new LegalEntityService(legalEntitiesApi, batchResponseUtils); } @Bean public UserService userService(Optional identityApi, UserManagementApi usersApi, - IdentityManagementApi identityManagementApi) { - return new UserService(usersApi, identityManagementApi, identityApi); + IdentityManagementApi identityManagementApi, + com.backbase.dbs.user.api.service.v2.UserProfileManagementApi userProfileManagementApi) { + return new UserService(usersApi, identityManagementApi, identityApi, userProfileManagementApi); } @Bean @@ -74,13 +69,12 @@ public UserProfileService userProfileService(UserProfileManagementApi userProfil @Bean public AccessGroupService accessGroupService( UserManagementApi usersApi, - DeletionProperties configurationProperties, UserQueryApi userQueryApi, - UsersApi accessControlUsersApi, DataGroupApi dataGroupApi, + DeletionProperties configurationProperties, + UsersApi accessControlUsersApi, DataGroupsApi dataGroupsApi, ServiceAgreementsApi serviceAgreementsApi, - ServiceAgreementApi serviceAgreementApi, ServiceAgreementQueryApi serviceAgreementQueryApi, - FunctionGroupsApi functionGroupsApi, FunctionGroupApi functionGroupApi, BatchResponseUtils batchResponseUtils) { - return new AccessGroupService(usersApi, userQueryApi, accessControlUsersApi, dataGroupApi, dataGroupsApi, - functionGroupApi, functionGroupsApi, serviceAgreementQueryApi, serviceAgreementApi, serviceAgreementsApi, + FunctionGroupsApi functionGroupsApi, BatchResponseUtils batchResponseUtils) { + return new AccessGroupService(usersApi, accessControlUsersApi, dataGroupsApi, + functionGroupsApi, serviceAgreementsApi, configurationProperties, batchResponseUtils); } diff --git a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/exceptions/LegalEntityException.java b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/exceptions/LegalEntityException.java index b9f29f889..9c744b350 100644 --- a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/exceptions/LegalEntityException.java +++ b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/exceptions/LegalEntityException.java @@ -1,6 +1,6 @@ package com.backbase.stream.exceptions; -import com.backbase.dbs.accesscontrol.api.service.v2.model.LegalEntityCreateItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityCreateItem; import org.springframework.web.reactive.function.client.WebClientResponseException; public class LegalEntityException extends RuntimeException { diff --git a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/AccessGroupMapper.java b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/AccessGroupMapper.java index 0e1251f74..8232b11a7 100644 --- a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/AccessGroupMapper.java +++ b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/AccessGroupMapper.java @@ -1,15 +1,14 @@ package com.backbase.stream.mapper; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ApprovalStatus; -import com.backbase.dbs.accesscontrol.api.service.v2.model.FunctionGroupItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ParticipantIngest; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationIngestFunctionGroup; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationPermission; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationPermissionFunctionGroupUpdate; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServiceAgreementItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServiceAgreementItemQuery; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServiceAgreementPut; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServicesAgreementIngest; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ParticipantIngest; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationIngestFunctionGroup; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationPermission; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationPermissionFunctionGroupUpdate; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServiceAgreementItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServiceAgreementItemQuery; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServiceAgreementPut; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServicesAgreementIngest; import com.backbase.stream.legalentity.model.BusinessFunction; import com.backbase.stream.legalentity.model.BusinessFunctionGroup; import com.backbase.stream.legalentity.model.JobRole; @@ -46,9 +45,6 @@ public interface AccessGroupMapper { PresentationIngestFunctionGroup toPresentation(JobRole referenceJobRole); - com.backbase.stream.legalentity.model.ApprovalStatus map( - ApprovalStatus approvalStatus); - /** * Map {@link BusinessFunctionGroup} with privileges to {@link PresentationPermission}. * diff --git a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/LegalEntityMapper.java b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/LegalEntityMapper.java index ac357a6f6..626dac6ee 100644 --- a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/LegalEntityMapper.java +++ b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/LegalEntityMapper.java @@ -1,6 +1,11 @@ package com.backbase.stream.mapper; -import com.backbase.dbs.accesscontrol.api.service.v2.model.*; +import com.backbase.dbs.accesscontrol.api.service.v3.model.GetServiceAgreement; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityCreateItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityItemBase; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityPut; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityType; import com.backbase.stream.legalentity.model.LegalEntity; import com.backbase.stream.legalentity.model.ServiceAgreement; import org.mapstruct.Mapper; @@ -24,17 +29,19 @@ public interface LegalEntityMapper { com.backbase.stream.legalentity.model.LegalEntityType map(LegalEntityType legalEntityType); + @Mapping(source = "type", target = "legalEntityType") LegalEntity toModel(LegalEntityCreateItem legalEntity); @Mapping(source = "id", target = "internalId") ServiceAgreement toStream(GetServiceAgreement getServiceAgreement); - @Mapping(source = "additions", target = "legalEntity.additions") - @Mapping(source = "externalId", target = "legalEntity.externalId") - @Mapping(source = "name", target = "legalEntity.name") - @Mapping(source = "legalEntityType", target = "legalEntity.type") - @Mapping(source = "parentExternalId", target = "legalEntity.parentExternalId") - @Mapping(source = "activateSingleServiceAgreement", target = "legalEntity.activateSingleServiceAgreement") - @Mapping(source = "externalId", target = "externalId") + @Mapping(source = "additions", target = "newValues.additions") + @Mapping(source = "externalId", target = "newValues.externalId") + @Mapping(source = "name", target = "newValues.name") + @Mapping(source = "legalEntityType", target = "newValues.type") + @Mapping(source = "customerCategory", target = "newValues.customerCategory") + @Mapping(source = "parentExternalId", target = "newValues.parentExternalId") + @Mapping(source = "activateSingleServiceAgreement", target = "newValues.activateSingleServiceAgreement") + @Mapping(source = "externalId", target = "currentExternalId") LegalEntityPut toLegalEntityPut(LegalEntity legalEntity); } diff --git a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/ParticipantMapper.java b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/ParticipantMapper.java index a8c5487d3..d1d65eec9 100644 --- a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/ParticipantMapper.java +++ b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/ParticipantMapper.java @@ -1,10 +1,9 @@ package com.backbase.stream.mapper; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationParticipantBatchUpdate; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationParticipantPutBody; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationParticipantBatchUpdate; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationParticipantPutBody; import com.backbase.stream.legalentity.model.LegalEntityParticipant; import com.backbase.stream.legalentity.model.ServiceAgreement; -import java.util.List; import org.mapstruct.AfterMapping; import org.mapstruct.Mapper; import org.mapstruct.Mapping; diff --git a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/UserMapper.java b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/UserMapper.java index 7059e930b..f797325f4 100644 --- a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/UserMapper.java +++ b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/mapper/UserMapper.java @@ -1,8 +1,6 @@ package com.backbase.stream.mapper; -import com.backbase.dbs.user.api.service.v2.model.GetUser; -import com.backbase.dbs.user.api.service.v2.model.User; -import com.backbase.dbs.user.api.service.v2.model.UserExternal; +import com.backbase.dbs.user.api.service.v2.model.*; import com.backbase.identity.integration.api.service.v1.model.EnhancedUserRepresentation; import com.backbase.identity.integration.api.service.v1.model.UserRequestBody; import org.mapstruct.Mapper; @@ -27,4 +25,6 @@ public interface UserMapper { @Mapping(source = "userProfile.preferredLanguage", target = "preferredLanguage") @Mapping(source = "additions", target = "additions") User toServiceUser(com.backbase.stream.legalentity.model.User user); + + UpdateIdentityRequest mapUpdateIdentity(GetIdentity user); } diff --git a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/service/AccessGroupService.java b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/service/AccessGroupService.java index 2dc8c2353..080a20c45 100644 --- a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/service/AccessGroupService.java +++ b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/service/AccessGroupService.java @@ -1,51 +1,45 @@ package com.backbase.stream.service; -import static com.backbase.dbs.accesscontrol.api.service.v2.model.BatchResponseItemExtended.StatusEnum.HTTP_STATUS_OK; +import static com.backbase.dbs.accesscontrol.api.service.v3.model.BatchResponseItemExtended.StatusEnum.HTTP_STATUS_OK; import static com.backbase.stream.legalentity.model.ServiceAgreementUserAction.ActionEnum.ADD; import static com.backbase.stream.legalentity.model.ServiceAgreementUserAction.ActionEnum.REMOVE; import static org.springframework.util.StringUtils.isEmpty; -import com.backbase.dbs.accesscontrol.api.service.v2.DataGroupApi; -import com.backbase.dbs.accesscontrol.api.service.v2.DataGroupsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.FunctionGroupApi; -import com.backbase.dbs.accesscontrol.api.service.v2.FunctionGroupsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementQueryApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.UserQueryApi; -import com.backbase.dbs.accesscontrol.api.service.v2.UsersApi; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ArrangementPrivilegesGetResponseBody; -import com.backbase.dbs.accesscontrol.api.service.v2.model.BatchResponseItemExtended; -import com.backbase.dbs.accesscontrol.api.service.v2.model.DataGroupItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.DataGroupItemSystemBase; -import com.backbase.dbs.accesscontrol.api.service.v2.model.FunctionGroupItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.Functiongroupupdate; -import com.backbase.dbs.accesscontrol.api.service.v2.model.IdItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ListOfFunctionGroupsWithDataGroups; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PersistenceApprovalPermissions; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationAction; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationApprovalStatus; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationAssignUserPermissions; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationDataGroupIdentifier; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationDataGroupItemPutRequestBody; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationDataGroupUpdate; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationFunctionDataGroup; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationFunctionGroupDataGroup; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationFunctionGroupPutRequestBody; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationGenericObjectId; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationIdentifier; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationIngestFunctionGroup; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationItemIdentifier; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationParticipantBatchUpdate; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationPermission; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationPermissionFunctionGroupUpdate; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationSearchDataGroupsRequest; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationServiceAgreementIdentifier; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationServiceAgreementUserPair; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationServiceAgreementUsersBatchUpdate; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServiceAgreementParticipantsGetResponseBody; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServiceAgreementUsersQuery; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServicesAgreementIngest; +import com.backbase.dbs.accesscontrol.api.service.v3.DataGroupsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.FunctionGroupsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.ServiceAgreementsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.UsersApi; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ArrangementPrivilegesGetResponseBody; +import com.backbase.dbs.accesscontrol.api.service.v3.model.BatchResponseItemExtended; +import com.backbase.dbs.accesscontrol.api.service.v3.model.DataGroupItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.DataGroupItemSystemBase; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupUpdate; +import com.backbase.dbs.accesscontrol.api.service.v3.model.IdItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ListOfFunctionGroupsWithDataGroups; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PersistenceApprovalPermissions; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationAction; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationAssignUserPermissions; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationDataGroupIdentifier; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationDataGroupItemPutRequestBody; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationDataGroupUpdate; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationFunctionDataGroup; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationFunctionGroupDataGroup; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationFunctionGroupPutRequestBody; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationGenericObjectId; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationIdentifier; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationIngestFunctionGroup; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationItemIdentifier; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationParticipantBatchUpdate; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationPermission; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationPermissionFunctionGroupUpdate; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationSearchDataGroupsRequest; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationServiceAgreementIdentifier; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationServiceAgreementUserPair; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationServiceAgreementUsersBatchUpdate; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServiceAgreementParticipantsGetResponseBody; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServiceAgreementUsersQuery; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServicesAgreementIngest; import com.backbase.dbs.user.api.service.v2.UserManagementApi; import com.backbase.dbs.user.api.service.v2.model.GetUser; import com.backbase.stream.configuration.DeletionProperties; @@ -124,22 +118,12 @@ public class AccessGroupService { @NonNull private final UserManagementApi usersApi; @NonNull - private final UserQueryApi userQueryApi; - @NonNull private final UsersApi accessControlUsersApi; @NonNull - private final DataGroupApi dataGroupApi; - @NonNull private final DataGroupsApi dataGroupsApi; @NonNull - private final FunctionGroupApi functionGroupApi; - @NonNull private final FunctionGroupsApi functionGroupsApi; @NonNull - private final ServiceAgreementQueryApi serviceAgreementQueryApi; - @NonNull - private final ServiceAgreementApi serviceAgreementApi; - @NonNull private final ServiceAgreementsApi serviceAgreementsApi; @NonNull private final DeletionProperties deletionProperties; @@ -159,7 +143,7 @@ public class AccessGroupService { */ public Mono createServiceAgreement(StreamTask streamTask, ServiceAgreement serviceAgreement) { ServicesAgreementIngest servicesAgreementIngest = accessGroupMapper.toPresentation(serviceAgreement); - return serviceAgreementApi.postServiceAgreementIngest(servicesAgreementIngest) + return serviceAgreementsApi.postServiceAgreementIngest(servicesAgreementIngest) .onErrorResume(WebClientResponseException.class, throwable -> { streamTask.error(SERVICE_AGREEMENT, "create", "failed", serviceAgreement.getExternalId(), "", throwable, throwable.getResponseBodyAsString(), "Failed to create Service Agreement"); @@ -176,7 +160,7 @@ public Mono createServiceAgreement(StreamTask streamTask, Serv */ public Mono getServiceAgreementByExternalId(String externalId) { log.info("setting up getting Service Agreement with external Id: {} flow", externalId); - return serviceAgreementApi.getServiceAgreementExternalId(externalId) + return serviceAgreementsApi.getServiceAgreementExternalId(externalId) .doOnNext(serviceAgreementItem -> log .info("Service Agreement: {} found", serviceAgreementItem.getExternalId())) .onErrorResume(WebClientResponseException.NotFound.class, throwable -> { @@ -260,7 +244,7 @@ public Mono updateServiceAgreementRegularUsers(StreamTask stre .flatMap(actionGroup -> { log.info("Update regular users of Service Agreement with external Id: {}", serviceAgreement.getExternalId()); - return serviceAgreementApi.putPresentationServiceAgreementUsersBatchUpdate(actionGroup) + return serviceAgreementsApi.putPresentationServiceAgreementUsersBatchUpdate(actionGroup) .onErrorResume(WebClientResponseException.class, e -> Mono.error(new StreamTaskException(streamTask, e, MessageFormat @@ -296,7 +280,7 @@ private Mono enrichUsersWithInternalUserId(T task, Lis } private Mono getServiceAgreementUsers(ServiceAgreement serviceAgreement) { - return serviceAgreementQueryApi.getServiceAgreementUsers(serviceAgreement.getInternalId()) + return serviceAgreementsApi.getServiceAgreementUsers(serviceAgreement.getInternalId()) .onErrorResume(WebClientResponseException.NotFound.class, e -> { log.info("users not found"); return Mono.just(new ServiceAgreementUsersQuery()); @@ -403,7 +387,7 @@ private Mono putServiceAgreementParticipants(StreamTask stream log.debug("updating participants: " + request.toString()); - return serviceAgreementApi.putPresentationIngestServiceAgreementParticipants(request) + return serviceAgreementsApi.putPresentationIngestServiceAgreementParticipants(request) .onErrorResume(WebClientResponseException.class, e -> { streamTask.error("participant", "update-participants", "failed", serviceAgreement.getExternalId(), serviceAgreement.getInternalId(), e, e.getResponseBodyAsString(), @@ -418,7 +402,7 @@ private Mono putServiceAgreementParticipants(StreamTask stream resultList.stream().forEach(r -> { if (r.getStatus() != HTTP_STATUS_OK) { streamTask.error("participant", "update-participant", "failed", r.getResourceId(), - null, "Error updating Participant {} for Service Agreement: {}", r.getResourceId(), + null, "Error updating Participant %s for Service Agreement: %s", r.getResourceId(), serviceAgreement.getExternalId()); log.error("Error updating Participant {} for Service Agreement: {}", r.getResourceId(), serviceAgreement.getExternalId()); @@ -465,7 +449,7 @@ public Mono setAdministrators(LegalEntity legalEntity) { presentationServiceAgreementUsersBatchUpdate.users(userPairs); presentationServiceAgreementUsersBatchUpdate.setAction(PresentationAction.ADD); - return serviceAgreementApi.putPresentationServiceAgreementAdminsBatchUpdate(presentationServiceAgreementUsersBatchUpdate) + return serviceAgreementsApi.putPresentationServiceAgreementAdminsBatchUpdate(presentationServiceAgreementUsersBatchUpdate) .doOnError(WebClientResponseException.BadRequest.class, this::handleError) .collectList() .map(batchResponseItemExtendeds -> { @@ -518,9 +502,7 @@ public Mono assignPermissions(StreamTask streamTask, ServiceAgre return Mono.error(new StreamTaskException(streamTask, e, "Failed to Assign permissions: " + e.getResponseBodyAsString())); }) - .flatMap(this::processApprovalStatus) - .map(jobProfileUser::approvalStatus); - + .then(Mono.just(jobProfileUser)); } public Mono assignPermissionsBatch(BatchProductGroupTask task, Map>> usersPermissions) { @@ -572,7 +554,7 @@ public Mono assignPermissionsBatch(BatchProductGroupTask * Retrieves function groups by service agreement id, filter any non-system and convert resulting list into a set of ids. */ private Mono> getAssociatedSystemFunctionsIds(BatchProductGroupTask task) { - return functionGroupApi.getFunctionGroups(task.getData().getServiceAgreement().getInternalId()) + return functionGroupsApi.getFunctionGroups(task.getData().getServiceAgreement().getInternalId()) .onErrorResume(WebClientResponseException.class, e -> { task.error(ACCESS_GROUP, "assign-permissions", "failed", task.getData().getServiceAgreement().getExternalId(), task.getData().getServiceAgreement().getInternalId(), e, e.getResponseBodyAsString(), "Failed to fetch function groups"); return Mono.error(new StreamTaskException(task, e, "Failed to fetch function groups: " + e.getResponseBodyAsString())); @@ -599,7 +581,7 @@ private Mono> mergeUserPermissions(Batch List request, Set systemFunctionGroupIds) { return Flux.fromIterable(users) - .flatMap(user -> userQueryApi.getPersistenceApprovalPermissions(user.getInternalId(), task.getData().getServiceAgreement().getInternalId()) + .flatMap(user -> accessControlUsersApi.getPersistenceApprovalPermissions(user.getInternalId(), task.getData().getServiceAgreement().getInternalId()) .onErrorResume(WebClientResponseException.NotFound.class, e -> Mono.empty()) .map(PersistenceApprovalPermissions::getItems) .map(items -> items.stream() @@ -643,17 +625,17 @@ private Mono> mergeUserPermissions(Batch .filter(mergedFunctionDataGroup -> hasTheSameFunctionGroupId(mergedFunctionDataGroup, requestFunctionDataGroup)) .findFirst(); - // If requested function group is already ingested, merge the request and existing function group - if (mergedFunctionGroupOptional.isPresent()) { - PresentationFunctionGroupDataGroup mergedFunctionGroup = mergedFunctionGroupOptional.get(); - - if (mergedFunctionGroup.getDataGroupIdentifiers() != null) { - mergedFunctionGroup.getDataGroupIdentifiers().addAll(requestFunctionDataGroup.getDataGroupIdentifiers()); - } - // otherwise we should copy the function group from the request completely - } else { - mergedUserPermissions.addFunctionGroupDataGroupsItem(requestFunctionDataGroup); - } + mergedFunctionGroupOptional.ifPresentOrElse(mergedFunctionGroup -> + // If requested function group is already ingested, merge the request and existing function group + mergedFunctionGroup.setDataGroupIdentifiers( + Stream.of(mergedFunctionGroup.getDataGroupIdentifiers(), + requestFunctionDataGroup.getDataGroupIdentifiers()) + .filter(Objects::nonNull) + .flatMap(List::stream) + .distinct() + .toList()), + // otherwise we should copy the function group from the request completely + () -> mergedUserPermissions.addFunctionGroupDataGroupsItem(requestFunctionDataGroup)); }); } return mergedUserPermissions; @@ -726,15 +708,6 @@ private ListOfFunctionGroupsWithDataGroups functionGroupsWithDataGroup(JobProfil return functionGroups; } - private Mono processApprovalStatus( - PresentationApprovalStatus presentationApprovalStatus) { - if (presentationApprovalStatus.getApprovalStatus() != null) { - return Mono.just(accessGroupMapper.map(presentationApprovalStatus.getApprovalStatus())); - } else { - return Mono.empty(); - } - } - private PresentationFunctionDataGroup getPresentationFunctionDataGroup(ProductGroup productGroup, BusinessFunctionGroup businessFunctionGroup) { return new PresentationFunctionDataGroup() @@ -810,7 +783,7 @@ public Mono updateExistingDataGroupsBatch(BatchProductGro .dataGroupIdentifier(mapDataGroupId(dbsDataGroup.getId())) .type(dbsDataGroup.getType()) .action(PresentationAction.ADD) - .dataItems(arrangementsToAdd.stream().map(id -> new PresentationItemIdentifier().internalIdIdentifier(id)).collect(Collectors.toList())) + .dataItems(arrangementsToAdd.stream().map(id -> new PresentationItemIdentifier().id(id)).collect(Collectors.toList())) ); } if (!CollectionUtils.isEmpty(arrangementsToRemove)) { @@ -818,7 +791,7 @@ public Mono updateExistingDataGroupsBatch(BatchProductGro .dataGroupIdentifier(mapDataGroupId(dbsDataGroup.getId())) .type(dbsDataGroup.getType()) .action(PresentationAction.REMOVE) - .dataItems(arrangementsToRemove.stream().map(id -> new PresentationItemIdentifier().internalIdIdentifier(id)).collect(Collectors.toList())) + .dataItems(arrangementsToRemove.stream().map(id -> new PresentationItemIdentifier().id(id)).collect(Collectors.toList())) ); } }); @@ -852,7 +825,7 @@ public Flux updateDataGroupItems(List getExistingDataGroups(String serviceAgreementInternalId, String type) { - return dataGroupApi.getDataGroups(serviceAgreementInternalId, type, true); + return dataGroupsApi.getDataGroups(serviceAgreementInternalId, type, true); } /** @@ -940,7 +913,7 @@ private Mono updateAccessGroupWithArrangementIds(DataGroupItem dat log.info("Updating Data Access Group: {}", dataGroupsDataGroupItem.getId()); List dataItems = Stream.concat(StreamUtils.getInternalProductIds(productGroup).stream(), StreamUtils.getCustomDataGroupItems(productGroup).stream()) - .map(id -> new PresentationItemIdentifier().internalIdIdentifier(id)).collect(Collectors.toList()); + .map(id -> new PresentationItemIdentifier().id(id)).collect(Collectors.toList()); PresentationDataGroupUpdate presentationDataGroupUpdate = new PresentationDataGroupUpdate(); presentationDataGroupUpdate.setDataGroupIdentifier(mapDataGroupId(dataGroupsDataGroupItem.getId())); @@ -978,7 +951,6 @@ public Mono createArrangementDataAccessGroup(ServiceAgreement dataGroupItemSystemBase.setName(productGroup.getName()); dataGroupItemSystemBase.setDescription(productGroup.getDescription()); dataGroupItemSystemBase.setServiceAgreementId(serviceAgreement.getInternalId()); - dataGroupItemSystemBase.setAreItemsInternalIds(true); dataGroupItemSystemBase.setItems(dataItems); dataGroupItemSystemBase.setType(productGroup.getProductGroupType().name()); if (dataGroupItemSystemBase.getItems().stream().anyMatch(Objects::isNull)) { @@ -987,9 +959,11 @@ public Mono createArrangementDataAccessGroup(ServiceAgreement } return dataGroupsApi.postDataGroups(dataGroupItemSystemBase) - .onErrorResume(WebClientResponseException.class, badRequest -> { - streamTask.error(ACCESS_GROUP, CREATE_ACCESS_GROUP, REJECTED, productGroup.getName(), null, "Data group items cannot have null items"); - return Mono.error(new StreamTaskException(streamTask, badRequest, "Data Group Items cannot have null items")); + .onErrorResume(WebClientResponseException.class, webClientResponseEx -> { + String message = webClientResponseEx.getResponseBodyAsString(); + streamTask.error(ACCESS_GROUP, CREATE_ACCESS_GROUP, REJECTED, productGroup.getName(), null, + webClientResponseEx, message, "Unable to add data access group items"); + return Mono.error(new StreamTaskException(streamTask, webClientResponseEx, message)); }) .map(idItem -> { streamTask.info(ACCESS_GROUP, CREATE_ACCESS_GROUP, CREATED, productGroup.getName(), idItem.getId(), "Create new Data Group"); @@ -1029,7 +1003,7 @@ public Mono deleteFunctionGroupsForServiceAgreement(String serviceAgreemen return Mono.empty(); } - return functionGroupApi.getFunctionGroups(serviceAgreementInternalId) + return functionGroupsApi.getFunctionGroups(serviceAgreementInternalId) .collectList() .flatMap(functionGroups -> functionGroupsApi.postFunctionGroupsDelete( @@ -1050,7 +1024,7 @@ public Mono deleteFunctionGroupsForServiceAgreement(String serviceAgreemen */ public Mono> getFunctionGroupsForServiceAgreement(String serviceAgreementInternalId) { log.debug("Retrieving Function Groups for Service Agreement {}", serviceAgreementInternalId); - return functionGroupApi.getFunctionGroups(serviceAgreementInternalId) + return functionGroupsApi.getFunctionGroups(serviceAgreementInternalId) .collectList(); } @@ -1063,7 +1037,7 @@ public Mono> getFunctionGroupsForServiceAgreement(String */ public Mono deleteAdmins(ServiceAgreement serviceAgreement) { log.debug("Removing admins for Service Agreement {}", serviceAgreement.getName()); - return serviceAgreementQueryApi.getServiceAgreementAdmins(serviceAgreement.getInternalId()) + return serviceAgreementsApi.getServiceAgreementAdmins(serviceAgreement.getInternalId()) .flatMapMany(admins -> Flux.fromIterable(admins.getAdmins())) // get External ID for each admin. // We need to get the user by using the internal id to facilitate the delete for issue #46 @@ -1079,7 +1053,7 @@ public Mono deleteAdmins(ServiceAgreement serviceAgreement) { if (CollectionUtils.isEmpty(admins)) { return Mono.empty(); } else { - return serviceAgreementApi.putPresentationServiceAgreementAdminsBatchUpdate( + return serviceAgreementsApi.putPresentationServiceAgreementAdminsBatchUpdate( new PresentationServiceAgreementUsersBatchUpdate() .action(PresentationAction.REMOVE) .users(admins)) @@ -1100,7 +1074,7 @@ public Mono deleteAdmins(ServiceAgreement serviceAgreement) { * @return flux of arrangements internal ids. */ public Flux getArrangementInternalIdsForServiceAgreement(String serviceAgreementInternalId) { - return dataGroupApi.getDataGroups(serviceAgreementInternalId, null, true) + return dataGroupsApi.getDataGroups(serviceAgreementInternalId, null, true) .collectList() .map(dataGroupItems -> { // get all internal arrangement IDs present in data groups. @@ -1200,7 +1174,7 @@ private Mono createJobRole(StreamTask streamTask, ServiceAgreement serv // Removing constant from mapper and adding default APS here to avoid issues with apsName. if(jobRole.getApsId() == null && isEmpty(jobRole.getApsName())){ log.warn("Adding default APS '1 - User APS' to job role since it wasn't previously set."); - presentationIngestFunctionGroup.setApsId(BigDecimal.ONE); + presentationIngestFunctionGroup.setApsId(1L); } return functionGroupsApi.postPresentationIngestFunctionGroup(presentationIngestFunctionGroup) @@ -1339,7 +1313,7 @@ private Mono updateJobRole(StreamTask streamTask, ServiceAgre } PresentationFunctionGroupPutRequestBody putRequestBody = new PresentationFunctionGroupPutRequestBody(); - putRequestBody.functionGroup(new Functiongroupupdate() + putRequestBody.functionGroup(new FunctionGroupUpdate() .name(jobRole.getName()) .description(jobRole.getDescription()) .validFromDate(jobRole.getValidFromDate()) @@ -1373,7 +1347,7 @@ private Mono updateJobRole(StreamTask streamTask, ServiceAgre if (!item.getStatus().equals(HTTP_STATUS_OK)) { streamTask.error(JOB_ROLE, "ingest-reference-job-role", FAILED, item.getExternalServiceAgreementId(), item.getResourceId(), - "Failed up setup Job Role - status: {}, errors: {}", item.getStatus(), item.getErrors()); + "Failed setting up Job Role - status: %s, errors: %s", item.getStatus(), item.getErrors()); return Mono.error(new StreamTaskException(streamTask, "Failed to setup Job Role - status: " + item.getStatus() + ", errors: " + item.getErrors())); } jobRole.setId(idItems.get(0).getResourceId()); @@ -1400,7 +1374,7 @@ private Mono> updateBatchBusinessFunctionGroup(S PresentationFunctionGroupPutRequestBody putRequestBody = new PresentationFunctionGroupPutRequestBody(); - putRequestBody.setFunctionGroup(new Functiongroupupdate() + putRequestBody.setFunctionGroup(new FunctionGroupUpdate() .description(Optional.ofNullable(bfg.getDescription()).orElse(bfg.getName())) .permissions(getUpdatePermissions(bfg)) .name(bfg.getName()) @@ -1433,7 +1407,7 @@ private boolean isEmptyFunctionName(List getUpdatePermissions(Bus @NotNull private Flux getFunctionGroups(StreamTask streamTask, ServiceAgreement serviceAgreement) { - return functionGroupApi.getFunctionGroups(serviceAgreement.getInternalId()) + return functionGroupsApi.getFunctionGroups(serviceAgreement.getInternalId()) .onErrorResume(WebClientResponseException.class, e -> { log.error("Failed to get Function Groups for Service Agreement: {} Response: {}", serviceAgreement.getExternalId(), e.getResponseBodyAsString()); diff --git a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/service/LegalEntityService.java b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/service/LegalEntityService.java index 447c1ef38..2f5d5afd5 100644 --- a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/service/LegalEntityService.java +++ b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/service/LegalEntityService.java @@ -1,33 +1,22 @@ package com.backbase.stream.service; -import com.backbase.dbs.accesscontrol.api.service.v2.LegalEntitiesApi; -import com.backbase.dbs.accesscontrol.api.service.v2.LegalEntityApi; -import com.backbase.dbs.accesscontrol.api.service.v2.model.LegalEntitiesBatchDelete; -import com.backbase.dbs.accesscontrol.api.service.v2.model.LegalEntityCreateItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.LegalEntityItemId; -import com.backbase.dbs.accesscontrol.api.service.v2.model.LegalEntityPut; +import com.backbase.dbs.accesscontrol.api.service.v3.LegalEntitiesApi; +import com.backbase.dbs.accesscontrol.api.service.v3.model.IdItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntitiesBatchDelete; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityCreateItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityPut; import com.backbase.stream.exceptions.LegalEntityException; import com.backbase.stream.legalentity.model.LegalEntity; import com.backbase.stream.legalentity.model.ServiceAgreement; import com.backbase.stream.mapper.AccessGroupMapper; import com.backbase.stream.mapper.LegalEntityMapper; import com.backbase.stream.utils.BatchResponseUtils; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; -import java.time.ZoneId; import java.util.Collections; -import java.util.Date; import java.util.Objects; -import java.util.UUID; -import javax.crypto.Cipher; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.mapstruct.factory.Mappers; -import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Pageable; import org.springframework.web.client.RestClientException; import org.springframework.web.reactive.function.client.WebClientResponseException; @@ -45,20 +34,11 @@ public class LegalEntityService { @NonNull private final LegalEntitiesApi legalEntitiesApi; @NonNull - private final LegalEntityApi legalEntityApi; - @NonNull private final BatchResponseUtils batchResponseUtils; private final LegalEntityMapper mapper = Mappers.getMapper(LegalEntityMapper.class); private final AccessGroupMapper serviceAgreementMapper = Mappers.getMapper(AccessGroupMapper.class); - // Configured at access-control service - @Value("${backbase.accesscontrol.token.key:Bar12345Bar12345}") - private final String accessControlApiTokenKey = "Bar12345Bar12345"; - - @Value("${backbase.accesscontrol.token.initPhrase:RandomInitVecto2}") - private final String accessControlApiTokenInitPhrase = "RandomInitVecto2"; - /** * Create Legal Entity in Access Control. * @@ -75,7 +55,7 @@ public Mono createLegalEntity(LegalEntity legalEntity) { }); } - private Mono createLegalEntity(LegalEntityCreateItem legalEntity) { + private Mono createLegalEntity(LegalEntityCreateItem legalEntity) { // Create Legal Entity without master service agreement return legalEntitiesApi.postCreateLegalEntities(legalEntity) .doOnError(WebClientResponseException.class, this::handleWebClientResponseException) @@ -95,7 +75,7 @@ public Flux getSubEntities(String legalEntityExternalId, Pageable p return getLegalEntityByExternalId(legalEntityExternalId) .flux() .flatMap(legalEntity -> - legalEntitiesApi.getSubEntities(legalEntity.getInternalId(), null, + legalEntitiesApi.getSubEntities(legalEntity.getInternalId(), Math.toIntExact(pageable.getOffset()), pageable.getPageSize(), null) .map(mapper::toStream)); } @@ -124,7 +104,7 @@ public Mono getMasterServiceAgreementForExternalLegalEntityId( */ public Mono getMasterServiceAgreementForInternalLegalEntityId(String legalEntityInternalId) { log.info("Getting Service Agreement for: {}", legalEntityInternalId); - return legalEntityApi.getMasterServiceAgreement(legalEntityInternalId) + return legalEntitiesApi.getMasterServiceAgreement(legalEntityInternalId) .doOnNext(serviceAgreementItem -> log.info("Service Agreement: {} found for legal entity: {}", serviceAgreementItem.getExternalId(), legalEntityInternalId)) .onErrorResume(WebClientResponseException.NotFound.class, throwable -> { log.info("Master Service Agreement not found for: {}. Request:[{}] {} Response: {}", legalEntityInternalId, throwable.getRequest().getMethod(), throwable.getRequest().getURI() , throwable.getResponseBodyAsString()); @@ -166,7 +146,6 @@ public Mono getLegalEntityByInternalId(String internalId) { public Mono deleteLegalEntity(String legalEntityExternalId) { return legalEntitiesApi.postLegalEntitiesBatchDelete( new LegalEntitiesBatchDelete() - .accessToken(getAccessControlAccessToken()) .externalIds(Collections.singletonList(legalEntityExternalId))) .map(r -> batchResponseUtils.checkBatchResponseItem(r, "Remove Legal Entity", r.getStatus().getValue(), r.getResourceId(), r.getErrors())) .collectList() @@ -183,34 +162,6 @@ private void handleWebClientResponseException(WebClientResponseException webclie webclientResponseException.getResponseBodyAsString()); } - /** - * Generate access token which is usually verified by Access Control APIs during delete action. - * - * This implementation is brought here to avoid calling the - * - * @return access token. - */ - private String getAccessControlAccessToken() { - try { - SecretKeySpec secretKeySpec = new SecretKeySpec(accessControlApiTokenKey.getBytes(StandardCharsets.UTF_8), "AES"); - IvParameterSpec iv = new IvParameterSpec(accessControlApiTokenInitPhrase.getBytes(StandardCharsets.UTF_8)); - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING"); - cipher.init(1, secretKeySpec, iv); - - byte[] encrypted = cipher.doFinal(Long.toString(Date.from( - LocalDateTime.now() - .atZone(ZoneId.systemDefault()).toInstant()).getTime()).getBytes()); - - ByteBuffer bb = ByteBuffer.wrap(encrypted); - long firstLong = bb.getLong(); - long secondLong = bb.getLong(); - return new UUID(firstLong, secondLong).toString(); - } catch (Exception e) { - log.error("Error obtaining access token: ", e); - return null; - } - } - /** * Update Legal Entity in Access Control. * @@ -224,7 +175,7 @@ public Mono putLegalEntity(LegalEntity legalEntity) { .doOnError(WebClientResponseException.class, this::handleWebClientResponseException) .onErrorResume(WebClientResponseException.class, exception -> Mono.error(new RuntimeException("Failed to update Legal Entity", exception))) .onErrorStop() - .then(getLegalEntityByExternalId(legalEntityPut.getExternalId())); + .then(getLegalEntityByExternalId(legalEntityPut.getNewValues().getExternalId())); } } diff --git a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/service/UserService.java b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/service/UserService.java index 73f55081d..125437edd 100644 --- a/stream-access-control/access-control-core/src/main/java/com/backbase/stream/service/UserService.java +++ b/stream-access-control/access-control-core/src/main/java/com/backbase/stream/service/UserService.java @@ -1,40 +1,21 @@ package com.backbase.stream.service; -import static java.util.Optional.ofNullable; - import com.backbase.dbs.user.api.service.v2.IdentityManagementApi; import com.backbase.dbs.user.api.service.v2.UserManagementApi; -import com.backbase.dbs.user.api.service.v2.model.AddRealmRequest; -import com.backbase.dbs.user.api.service.v2.model.AssignRealm; -import com.backbase.dbs.user.api.service.v2.model.BatchResponseItem; -import com.backbase.dbs.user.api.service.v2.model.BatchUser; -import com.backbase.dbs.user.api.service.v2.model.CreateIdentityRequest; -import com.backbase.dbs.user.api.service.v2.model.GetUser; -import com.backbase.dbs.user.api.service.v2.model.GetUsersByLegalEntityIdsRequest; -import com.backbase.dbs.user.api.service.v2.model.GetUsersList; -import com.backbase.dbs.user.api.service.v2.model.Realm; -import com.backbase.dbs.user.api.service.v2.model.UpdateIdentityRequest; -import com.backbase.dbs.user.api.service.v2.model.UserCreated; -import com.backbase.dbs.user.api.service.v2.model.UserExternal; +import com.backbase.dbs.user.api.service.v2.UserProfileManagementApi; +import com.backbase.dbs.user.api.service.v2.model.*; +import com.backbase.dbs.user.api.service.v2.model.UserProfile; import com.backbase.identity.integration.api.service.v1.IdentityIntegrationServiceApi; import com.backbase.identity.integration.api.service.v1.model.EnhancedUserRepresentation; import com.backbase.identity.integration.api.service.v1.model.UserRequestBody; import com.backbase.stream.exceptions.UserUpsertException; -import com.backbase.stream.legalentity.model.IdentityUserLinkStrategy; import com.backbase.stream.legalentity.model.LegalEntity; import com.backbase.stream.legalentity.model.User; +import com.backbase.stream.legalentity.model.*; import com.backbase.stream.mapper.RealmMapper; import com.backbase.stream.mapper.UserMapper; import com.backbase.stream.worker.exception.StreamTaskException; import com.backbase.stream.worker.model.StreamTask; -import java.text.MessageFormat; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.UUID; -import java.util.stream.Collectors; -import javax.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.mapstruct.factory.Mappers; @@ -45,6 +26,14 @@ import org.springframework.web.reactive.function.client.WebClientResponseException; import reactor.core.publisher.Mono; +import javax.validation.constraints.NotNull; +import java.text.MessageFormat; +import java.util.*; +import java.util.stream.Collectors; + +import static java.util.Objects.requireNonNullElse; +import static java.util.Optional.ofNullable; + /** * Stream User Management. Still needs to be adapted to use Identity correctly */ @@ -62,6 +51,7 @@ public class UserService { private final UserManagementApi usersApi; private final IdentityManagementApi identityManagementApi; private final Optional identityIntegrationApi; + private final UserProfileManagementApi userManagerProfileApi; /** * Get User by external ID. @@ -218,9 +208,8 @@ private Mono existingRealm(final String realmName) { log.error("Error getting realm: {} Response: {}", realmName, e.getResponseBodyAsString()); return Mono.error(e); }) - .collectList() - .map(realms -> realms.stream().filter(realm -> realmName.equals(realm.getRealmName())).findFirst()) - .flatMap(Mono::justOrEmpty); + .filter(realm -> realmName.equals(realm.getRealmName())) + .next(); } /** @@ -233,11 +222,8 @@ public Mono setupRealm(LegalEntity legalEntity) { if (StringUtils.isEmpty(legalEntity.getRealmName())) { return Mono.empty(); } - Mono existingRealm = existingRealm(legalEntity.getRealmName()); - Mono createNewRealm = createRealm(legalEntity.getRealmName()); - return existingRealm.switchIfEmpty(createNewRealm) - .map(actual -> actual); - + return existingRealm(legalEntity.getRealmName()) + .switchIfEmpty(createRealm(legalEntity.getRealmName())); } /** @@ -273,48 +259,74 @@ public Mono linkLegalEntityToRealm(LegalEntity legalEntity) { * @return the same User with updated internal and external id on success */ public Mono createOrImportIdentityUser(User user, String legalEntityInternalId, StreamTask streamTask) { - CreateIdentityRequest createIdentityRequest = new CreateIdentityRequest(); - createIdentityRequest.setLegalEntityInternalId(legalEntityInternalId); - createIdentityRequest.setExternalId(user.getExternalId()); - if (IdentityUserLinkStrategy.CREATE_IN_IDENTITY.equals(user.getIdentityLinkStrategy())) { - Objects.requireNonNull(user.getFullName(), "User Full Name is required for user: " + user.getExternalId() + " in legal entity: " + legalEntityInternalId); - Objects.requireNonNull(user.getEmailAddress(), "User Email Address is required for user: " + user.getExternalId() + " in legal entity: " + legalEntityInternalId); - Objects.requireNonNull(user.getMobileNumber(), "User Mobile Number is required for user: " + user.getExternalId() + " in legal entity: " + legalEntityInternalId); + Mono upsertCall; + if (IdentityUserLinkStrategy.CREATE_IN_IDENTITY.equals(user.getIdentityLinkStrategy())) { + Objects.requireNonNull(user.getFullName(), + "User Full Name is required for user: " + user.getExternalId() + " in legal entity: " + + legalEntityInternalId); + Objects.requireNonNull( + Optional.ofNullable(user.getEmailAddress()).map(EmailAddress::getAddress).orElse(null), + "User Email Address is required for user: " + user.getExternalId() + " in legal entity: " + + legalEntityInternalId); + Objects.requireNonNull(Optional.ofNullable(user.getMobileNumber()).map(PhoneNumber::getNumber).orElse(null), + "User Mobile Number is required for user: " + user.getExternalId() + " in legal entity: " + + legalEntityInternalId); + + CreateIdentityRequest createIdentityRequest = new CreateIdentityRequest(); + createIdentityRequest.setLegalEntityInternalId(legalEntityInternalId); + createIdentityRequest.setExternalId(user.getExternalId()); + createIdentityRequest.setAdditions(user.getAdditions()); createIdentityRequest.setFullName(user.getFullName()); createIdentityRequest.setEmailAddress(user.getEmailAddress().getAddress()); createIdentityRequest.setMobileNumber(user.getMobileNumber().getNumber()); ofNullable(user.getAttributes()).ifPresent(createIdentityRequest::setAttributes); - ofNullable(user.getAdditions()).ifPresent(createIdentityRequest::setAdditions); + + upsertCall = identityManagementApi.createIdentity(createIdentityRequest) + .onErrorResume(WebClientResponseException.class, e -> { + log.error("Error creating identity: {} Response: {}", createIdentityRequest, + e.getResponseBodyAsString()); + String message = "Failed to create user: " + user.getExternalId(); + streamTask.error("user", "create-identity", "failed", + user.getExternalId(), legalEntityInternalId, e, e.getMessage(), message); + return Mono.error(new StreamTaskException(streamTask, message)); + }); + } else { + ImportIdentity importIdentity = new ImportIdentity(); + importIdentity.setLegalEntityInternalId(legalEntityInternalId); + importIdentity.setExternalId(user.getExternalId()); + importIdentity.additions(user.getAdditions()); + upsertCall = identityManagementApi.importIdentity(importIdentity) + .onErrorResume(WebClientResponseException.class, e -> { + log.error("Error importing identity: {} Response: {}", importIdentity, e.getResponseBodyAsString()); + String message = "Failed to import user: " + user.getExternalId(); + streamTask.error("user", "import-identity", "failed", + user.getExternalId(), legalEntityInternalId, e, e.getMessage(), message); + return Mono.error(new StreamTaskException(streamTask, message)); + }); } - return identityManagementApi.createIdentity(createIdentityRequest) - .onErrorResume(WebClientResponseException.class, e -> { - log.error("Error creating identity: {} Response: {}", createIdentityRequest, e.getResponseBodyAsString()); - String message = "Failed to create user: " + user.getExternalId(); - streamTask.error("user", "create-identity", "failed", - user.getExternalId(), legalEntityInternalId, e, e.getMessage(), message); - return Mono.error(new StreamTaskException(streamTask, message)); - }) + return upsertCall .map(identityCreatedItem -> { user.setInternalId(identityCreatedItem.getInternalId()); user.setExternalId(identityCreatedItem.getExternalId()); return user; }) - .flatMap(newUser -> this.updateIdentityUserAttributes(user, streamTask)); + .flatMap(newUser -> this.updateIdentityUser(user, streamTask)); } - private Mono updateIdentityUserAttributes(User user, StreamTask streamTask) { + private Mono updateIdentityUser(User user, StreamTask streamTask) { if (IdentityUserLinkStrategy.IMPORT_FROM_IDENTIY.equals(user.getIdentityLinkStrategy()) - && user.getAttributes() != null) { + && (user.getAttributes() != null || user.getAdditions() != null)) { UpdateIdentityRequest replaceIdentity = new UpdateIdentityRequest(); replaceIdentity.attributes(user.getAttributes()); + replaceIdentity.additions(user.getAdditions()); return identityManagementApi.updateIdentity(user.getInternalId(), replaceIdentity) .onErrorResume(WebClientResponseException.class, e -> { log.error("Error updating identity: {} Response: {}", user.getExternalId(), e.getResponseBodyAsString()); String message = "Failed to update identity: " + user.getExternalId(); - streamTask.error("user", "update-identity-attributes", "failed", + streamTask.error("user", "update-identity-attributes-and-additions", "failed", user.getExternalId(), user.getInternalId(), e, e.getMessage(), message); return Mono.error(new StreamTaskException(streamTask, message)); @@ -416,6 +428,39 @@ private Optional updateRequired(EnhancedUserRepresentation curr return updateRequired ? Optional.of(userRequestBody) : Optional.empty(); } + + /** + * Update Identity User, ex: emailAddress, mobileNumber, attributes, and additions + * + * @param user + * @return Mono + */ + public Mono updateIdentity(User user) { + + Objects.requireNonNull(user.getInternalId(), "user internalId is required"); + + return identityManagementApi.getIdentity(user.getInternalId()) + .map(mapper::mapUpdateIdentity) + .flatMap(updateIdentityRequest -> { + log.debug("Trying to update identity attributes and additions, externalId [{}]", user.getExternalId()); + if (updateIdentityRequest.getAttributes() == null) { + updateIdentityRequest.attributes(new HashMap<>()); + } + if (updateIdentityRequest.getAdditions() == null) { + updateIdentityRequest.additions(new HashMap<>()); + } + updateIdentityRequest.getAttributes().putAll(requireNonNullElse(user.getAttributes(), Map.of())); + updateIdentityRequest.getAdditions().putAll(requireNonNullElse(user.getAdditions(), Map.of())); + + return identityManagementApi.updateIdentity(user.getInternalId(), updateIdentityRequest) + .onErrorResume(WebClientResponseException.class, e -> { + log.error("Failed to update identity: {}", e.getResponseBodyAsString(), e); + return Mono.error(e); + }); + } + ).thenReturn(user); + } + /** * Update user * @@ -445,4 +490,22 @@ public Mono updateUser(User user) { .then(getUserByExternalId(user.getExternalId())); } + /** + * Return user profile. + * + * @param userId user internal id. + * @return User profile if exists. Empty if not. + */ + public Mono getUserProfile(String userId) { + if (userId == null) { + return Mono.empty(); + } + + return userManagerProfileApi.getUserProfile(userId) + .doOnNext(userProfile -> log.info("Found user profile for internalId {}", userId)) + .onErrorResume(WebClientResponseException.NotFound.class, notFound -> { + log.info("User profile with id: {} does not exist: {}", userId, notFound.getResponseBodyAsString()); + return Mono.empty(); + }); + } } diff --git a/stream-access-control/access-control-core/src/test/java/com/backbase/stream/mapper/AccessGroupMapperTest.java b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/mapper/AccessGroupMapperTest.java index e898d3bbc..addaa00f1 100644 --- a/stream-access-control/access-control-core/src/test/java/com/backbase/stream/mapper/AccessGroupMapperTest.java +++ b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/mapper/AccessGroupMapperTest.java @@ -2,19 +2,18 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import com.backbase.dbs.accesscontrol.api.service.v2.model.CreateStatus; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ParticipantIngest; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationUserApsIdentifiers; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServiceAgreementItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServiceAgreementPut; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServicesAgreementIngest; -import com.backbase.dbs.accesscontrol.api.service.v2.model.Status; +import com.backbase.dbs.accesscontrol.api.service.v3.model.CreateStatus; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ParticipantIngest; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationUserApsIdentifiers; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServiceAgreementItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServiceAgreementPut; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServicesAgreementIngest; +import com.backbase.dbs.accesscontrol.api.service.v3.model.Status; import com.backbase.stream.legalentity.model.ApsIdentifiers; import com.backbase.stream.legalentity.model.LegalEntityParticipant; import com.backbase.stream.legalentity.model.LegalEntityStatus; import com.backbase.stream.legalentity.model.ServiceAgreement; import java.time.LocalDate; -import java.util.Collections; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; @@ -148,4 +147,4 @@ void toPresentationPutMapsServiceAgreementToServiceAgreementPut() { assertEquals(expected, actual); } -} \ No newline at end of file +} diff --git a/stream-access-control/access-control-core/src/test/java/com/backbase/stream/mapper/LegalEntityMapperTest.java b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/mapper/LegalEntityMapperTest.java new file mode 100644 index 000000000..b244abecc --- /dev/null +++ b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/mapper/LegalEntityMapperTest.java @@ -0,0 +1,201 @@ +package com.backbase.stream.mapper; + +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +import com.backbase.dbs.accesscontrol.api.service.v3.model.GetServiceAgreement; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityCreateItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityItemBase; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityPut; +import com.backbase.dbs.accesscontrol.api.service.v3.model.Status; +import com.backbase.stream.legalentity.model.CustomerCategory; +import com.backbase.stream.legalentity.model.LegalEntity; +import com.backbase.stream.legalentity.model.LegalEntityStatus; +import com.backbase.stream.legalentity.model.LegalEntityType; +import com.backbase.stream.legalentity.model.ServiceAgreement; +import java.time.LocalDate; +import java.time.Month; +import java.util.Map; +import org.junit.jupiter.api.Test; +import org.mapstruct.factory.Mappers; + +class LegalEntityMapperTest { + + public static final LegalEntity LEGAL_ENTITY_MODEL = new LegalEntity() + .name("Test Legal Entity") + .externalId("externalId") + .internalId("internalId") + .legalEntityType(com.backbase.stream.legalentity.model.LegalEntityType.BANK) + .customerCategory(com.backbase.stream.legalentity.model.CustomerCategory.RETAIL) + .parentExternalId("parentExternalId") + .activateSingleServiceAgreement(true) + .additions(Map.of("k1", "v1", "k2", "v2")); + + private final LegalEntityMapper mapper = Mappers.getMapper(LegalEntityMapper.class); + + @Test + void toPresentationWithNullArg() { + assertNull(mapper.toPresentation(null)); + } + + @Test + void toPresentationShouldCopyAllAttributes() { + LegalEntityCreateItem presentation = mapper.toPresentation(LEGAL_ENTITY_MODEL); + + assertAll( + () -> assertNotNull(presentation), + () -> assertEquals("externalId", presentation.getExternalId()), + () -> assertEquals("Test Legal Entity", presentation.getName()), + () -> assertEquals( + com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityType.BANK, + presentation.getType() + ), + () -> assertEquals( + com.backbase.dbs.accesscontrol.api.service.v3.model.CustomerCategory.RETAIL, + presentation.getCustomerCategory() + ), + () -> assertEquals("parentExternalId", presentation.getParentExternalId()), + () -> assertEquals(Boolean.TRUE, presentation.getActivateSingleServiceAgreement()) + ); + } + + @Test + void toStreamFromLegalEntityItemBaseWithNullArg() { + assertNull(mapper.toStream((LegalEntityItemBase) null)); + } + + @Test + void toStreamFromLegalEntityItemBaseShouldCopyAllAttributes() { + LegalEntityItemBase legalEntityItemBase = new LegalEntityItemBase() + .name("Test Legal Entity") + .id("internalId") + .externalId("externalId") + .type(com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityType.CUSTOMER) + .customerCategory(com.backbase.dbs.accesscontrol.api.service.v3.model.CustomerCategory.BUSINESS) + .additions(Map.of("k1", "v1", "k2", "v2")); + LegalEntity model = mapper.toStream(legalEntityItemBase); + assertAll( + () -> assertNotNull(model), + () -> assertEquals("externalId", model.getExternalId()), + () -> assertEquals("internalId", model.getInternalId()), + () -> assertEquals("Test Legal Entity", model.getName()), + () -> assertEquals(LegalEntityType.CUSTOMER, model.getLegalEntityType()), + () -> assertEquals(CustomerCategory.BUSINESS, model.getCustomerCategory()) + ); + + } + + @Test + void toStreamFromLegalEntityItemWithNullArg() { + assertNull(mapper.toStream((LegalEntityItem) null)); + } + + @Test + void toStreamFromLegalEntityItemShouldCopyAllAttributes() { + LegalEntityItem legalEntityItem = new LegalEntityItem() + .id("internalId") + .externalId("externalId") + .parentId("parentId") + .name("Test Legal Entity") + .type(com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityType.CUSTOMER) + ; + LegalEntity model = mapper.toStream(legalEntityItem); + assertAll( + () -> assertNotNull(model), + () -> assertEquals("externalId", model.getExternalId()), + () -> assertEquals("internalId", model.getInternalId()), + () -> assertEquals("parentId", model.getParentInternalId()), + () -> assertEquals("Test Legal Entity", model.getName()), + () -> assertEquals(LegalEntityType.CUSTOMER, model.getLegalEntityType()) + ); + } + + @Test + void toModelFromLegalEntityCreateItemWithNullArg() { + assertNull(mapper.toModel(null)); + } + + @Test + void toModelFromLegalEntityCreateItemShouldCopyAllAttributes() { + LegalEntityCreateItem legalEntity = new LegalEntityCreateItem() + .externalId("externalId") + .parentExternalId("parentExternalId") + .name("Test Legal Entity") + .type(com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityType.CUSTOMER) + .activateSingleServiceAgreement(true); + LegalEntity model = mapper.toModel(legalEntity); + assertAll( + () -> assertNotNull(model), + () -> assertEquals("externalId", model.getExternalId()), + () -> assertEquals("parentExternalId", model.getParentExternalId()), + () -> assertEquals("Test Legal Entity", model.getName()), + () -> assertEquals(LegalEntityType.CUSTOMER, model.getLegalEntityType()), + () -> assertEquals(Boolean.TRUE, model.getActivateSingleServiceAgreement()) + ); + } + + @Test + void toStreamFromGetServiceAgreementWithNullArg() { + assertNull(mapper.toStream((GetServiceAgreement) null)); + } + + @Test + void toStreamFromGetServiceAgreementShouldCopyAllAttributes() { + GetServiceAgreement getServiceAgreement = new GetServiceAgreement() + .id("internalId") + .externalId("externalId") + .name("Test Service Agreement") + .description("description") + .validFromDate("2018-08-31") + .validFromTime("07:48:23") + .validUntilDate("2018-09-30") + .validUntilTime("07:49:24") + .status(Status.ENABLED) + .isMaster(true) + .creatorLegalEntity("creatorLegalEntity"); + ServiceAgreement model = mapper.toStream(getServiceAgreement); + assertAll( + () -> assertNotNull(model), + () -> assertEquals("externalId", model.getExternalId()), + () -> assertEquals("internalId", model.getInternalId()), + () -> assertEquals("Test Service Agreement", model.getName()), + () -> assertEquals("description", model.getDescription()), + () -> assertEquals(LocalDate.of(2018, Month.AUGUST, 31), model.getValidFromDate()), + () -> assertEquals("07:48:23", model.getValidFromTime()), + () -> assertEquals(LocalDate.of(2018, Month.SEPTEMBER, 30), model.getValidUntilDate()), + () -> assertEquals("07:49:24", model.getValidUntilTime()), + () -> assertEquals(Boolean.TRUE, model.getIsMaster()), + () -> assertEquals(LegalEntityStatus.ENABLED, model.getStatus()) + ); + } + + @Test + void testToLegalEntityPutWithNullArg() { + assertNull(mapper.toLegalEntityPut(null)); + } + + @Test + void testToLegalEntityPutShouldCopyAllAttributes() { + LegalEntityPut presentation = mapper.toLegalEntityPut(LEGAL_ENTITY_MODEL); + + assertAll( + () -> assertNotNull(presentation), + () -> assertEquals("externalId", presentation.getCurrentExternalId()), + () -> assertEquals("externalId", presentation.getNewValues().getExternalId()), + () -> assertEquals("Test Legal Entity", presentation.getNewValues().getName()), + () -> assertEquals( + com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityType.BANK, + presentation.getNewValues().getType() + ), + () -> assertEquals( + com.backbase.dbs.accesscontrol.api.service.v3.model.CustomerCategory.RETAIL, + presentation.getNewValues().getCustomerCategory() + ), + () -> assertEquals("parentExternalId", presentation.getNewValues().getParentExternalId()), + () -> assertEquals(Boolean.TRUE, presentation.getNewValues().getActivateSingleServiceAgreement()) + ); + } +} diff --git a/stream-access-control/access-control-core/src/test/java/com/backbase/stream/mapper/ParticipantMapperTest.java b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/mapper/ParticipantMapperTest.java index bd1904a69..ae0183e19 100644 --- a/stream-access-control/access-control-core/src/test/java/com/backbase/stream/mapper/ParticipantMapperTest.java +++ b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/mapper/ParticipantMapperTest.java @@ -2,9 +2,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationAction; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationParticipantBatchUpdate; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationParticipantPutBody; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationAction; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationParticipantBatchUpdate; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationParticipantPutBody; import com.backbase.stream.legalentity.model.LegalEntityParticipant; import com.backbase.stream.legalentity.model.ServiceAgreement; import org.junit.jupiter.api.Test; @@ -28,10 +28,8 @@ void mapToPresentationBatchPut() { new LegalEntityParticipant().externalId("p2").sharingAccounts(false).sharingUsers(false).action( LegalEntityParticipant.ActionEnum.REMOVE)); - PresentationParticipantBatchUpdate actual = subject.toPresentation(serviceAgreement); - PresentationParticipantBatchUpdate expected = new PresentationParticipantBatchUpdate(); expected.addParticipantsItem(new PresentationParticipantPutBody().sharingAccounts(true).sharingUsers(true) .action(PresentationAction.ADD).externalParticipantId("p1").externalServiceAgreementId(saExternalId)); @@ -39,4 +37,4 @@ void mapToPresentationBatchPut() { .action(PresentationAction.REMOVE).externalParticipantId("p2").externalServiceAgreementId(saExternalId)); assertEquals(expected, actual); } -} \ No newline at end of file +} diff --git a/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/AccessGroupServiceTest.java b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/AccessGroupServiceTest.java index 413aff603..813381100 100644 --- a/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/AccessGroupServiceTest.java +++ b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/AccessGroupServiceTest.java @@ -1,9 +1,9 @@ package com.backbase.stream.service; -import static com.backbase.dbs.accesscontrol.api.service.v2.model.BatchResponseItemExtended.StatusEnum.HTTP_STATUS_INTERNAL_SERVER_ERROR; -import static com.backbase.dbs.accesscontrol.api.service.v2.model.BatchResponseItemExtended.StatusEnum.HTTP_STATUS_OK; -import static com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationAction.ADD; -import static com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationAction.REMOVE; +import static com.backbase.dbs.accesscontrol.api.service.v3.model.BatchResponseItemExtended.StatusEnum.HTTP_STATUS_INTERNAL_SERVER_ERROR; +import static com.backbase.dbs.accesscontrol.api.service.v3.model.BatchResponseItemExtended.StatusEnum.HTTP_STATUS_OK; +import static com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationAction.ADD; +import static com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationAction.REMOVE; import static com.backbase.stream.LambdaAssertions.assertEqualsTo; import static com.backbase.stream.WebClientTestUtils.buildWebResponseExceptionMono; import static com.backbase.stream.legalentity.model.LegalEntityStatus.ENABLED; @@ -17,32 +17,27 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.backbase.dbs.accesscontrol.api.service.v2.DataGroupApi; -import com.backbase.dbs.accesscontrol.api.service.v2.DataGroupsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.FunctionGroupApi; -import com.backbase.dbs.accesscontrol.api.service.v2.FunctionGroupsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementQueryApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.UserQueryApi; -import com.backbase.dbs.accesscontrol.api.service.v2.UsersApi; -import com.backbase.dbs.accesscontrol.api.service.v2.model.BatchResponseItemExtended; -import com.backbase.dbs.accesscontrol.api.service.v2.model.FunctionGroupItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.FunctionGroupItem.TypeEnum; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PersistenceApprovalPermissions; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PersistenceApprovalPermissionsGetResponseBody; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationAction; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationAssignUserPermissions; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationDataGroupIdentifier; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationFunctionGroupDataGroup; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationIdentifier; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationParticipantBatchUpdate; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationParticipantPutBody; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationServiceAgreementUserPair; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationServiceAgreementUsersBatchUpdate; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServiceAgreementItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServiceAgreementParticipantsGetResponseBody; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServiceAgreementUsersQuery; +import com.backbase.dbs.accesscontrol.api.service.v3.DataGroupsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.FunctionGroupsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.ServiceAgreementsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.UsersApi; +import com.backbase.dbs.accesscontrol.api.service.v3.model.BatchResponseItemExtended; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupItem.TypeEnum; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PersistenceApprovalPermissions; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PersistenceApprovalPermissionsGetResponseBody; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationAction; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationAssignUserPermissions; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationDataGroupIdentifier; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationFunctionGroupDataGroup; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationIdentifier; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationParticipantBatchUpdate; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationParticipantPutBody; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationServiceAgreementUserPair; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationServiceAgreementUsersBatchUpdate; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServiceAgreementItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServiceAgreementParticipantsGetResponseBody; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServiceAgreementUsersQuery; import com.backbase.dbs.user.api.service.v2.UserManagementApi; import com.backbase.stream.configuration.DeletionProperties; import com.backbase.stream.legalentity.model.BaseProductGroup; @@ -92,30 +87,15 @@ class AccessGroupServiceTest { @Mock private UserManagementApi usersApi; - @Mock - private UserQueryApi userQueryApi; - @Mock private UsersApi accessControlUsersApi; - @Mock - private DataGroupApi dataGroupApi; - @Mock private DataGroupsApi dataGroupsApi; - @Mock - private FunctionGroupApi functionGroupApi; - @Mock private FunctionGroupsApi functionGroupsApi; - @Mock - private ServiceAgreementQueryApi serviceAgreementQueryApi; - - @Mock - private ServiceAgreementApi serviceAgreementApi; - @Mock private ServiceAgreementsApi serviceAgreementsApi; @@ -130,7 +110,7 @@ void getServiceAgreementByExternalIdRetrievesServiceAgreementByExternalId() { final String externalId = "someExternalId"; final Mono dbsSa = Mono.just(new ServiceAgreementItem().externalId(externalId)); - when(serviceAgreementApi.getServiceAgreementExternalId(eq(externalId))).thenReturn(dbsSa); + when(serviceAgreementsApi.getServiceAgreementExternalId(eq(externalId))).thenReturn(dbsSa); Mono result = subject.getServiceAgreementByExternalId(externalId); @@ -148,7 +128,7 @@ void getServiceAgreementByExternalIdReturnsEmptyOnServiceAgreementNotFound() { Mono response = buildWebResponseExceptionMono(WebClientResponseException.NotFound.class, HttpMethod.GET); - when(serviceAgreementApi.getServiceAgreementExternalId(eq(externalId))) + when(serviceAgreementsApi.getServiceAgreementExternalId(eq(externalId))) .thenReturn(response); @@ -185,7 +165,7 @@ void updateServiceAgreement() { .addParticipantsItem(new LegalEntityParticipant().externalId("p2").sharingAccounts(false) .sharingUsers(false).action(LegalEntityParticipant.ActionEnum.ADD)); - when(serviceAgreementApi.putPresentationIngestServiceAgreementParticipants(any())) + when(serviceAgreementsApi.putPresentationIngestServiceAgreementParticipants(any())) .thenReturn(Flux.concat( Mono.just(new BatchResponseItemExtended().action(ADD).resourceId("p1").status(HTTP_STATUS_OK)), Mono.just(new BatchResponseItemExtended().action(ADD).resourceId("p2").status(HTTP_STATUS_OK)) @@ -195,20 +175,20 @@ void updateServiceAgreement() { .map(u -> new BatchResponseItemExtended().status(HTTP_STATUS_OK) .resourceId(u.getUserProfile().getUser().getExternalId())) .collect(Collectors.toList())); - when(serviceAgreementApi.putPresentationServiceAgreementUsersBatchUpdate(any())).thenReturn(usersResponse); + when(serviceAgreementsApi.putPresentationServiceAgreementUsersBatchUpdate(any())).thenReturn(usersResponse); when(serviceAgreementsApi.getServiceAgreementParticipants(eq(saInternalId))) .thenReturn(Flux.fromIterable(Collections.emptyList())); Mono emptyExistingUsersList = Mono.just(new ServiceAgreementUsersQuery()); - when(serviceAgreementQueryApi.getServiceAgreementUsers(eq(saInternalId))).thenReturn(emptyExistingUsersList); + when(serviceAgreementsApi.getServiceAgreementUsers(eq(saInternalId))).thenReturn(emptyExistingUsersList); Mono result = subject.updateServiceAgreementAssociations(streamTask, serviceAgreement, regularUsers); result.block(); - InOrder inOrderValidator = inOrder(serviceAgreementApi); + InOrder inOrderValidator = inOrder(serviceAgreementsApi); thenUpdateParticipantsCall(inOrderValidator, saExternalId, ADD, new ExpectedParticipantUpdate("p1", true, true), new ExpectedParticipantUpdate("p2", false, false)); @@ -253,7 +233,7 @@ void updateServiceAgreementWithExistingParticipants() { .addParticipantsItem(new LegalEntityParticipant().externalId("p3").sharingAccounts(false) .sharingUsers(false).action(LegalEntityParticipant.ActionEnum.ADD)); - when(serviceAgreementApi.putPresentationIngestServiceAgreementParticipants(any())) + when(serviceAgreementsApi.putPresentationIngestServiceAgreementParticipants(any())) .thenReturn(Flux.concat(Mono.just(new BatchResponseItemExtended().status(HTTP_STATUS_OK)))); ServiceAgreementParticipantsGetResponseBody existingPar1 = @@ -268,18 +248,18 @@ void updateServiceAgreementWithExistingParticipants() { .map(u -> new BatchResponseItemExtended().status(HTTP_STATUS_OK) .resourceId(u.getUserProfile().getUser().getExternalId())) .collect(Collectors.toList())); - when(serviceAgreementApi.putPresentationServiceAgreementUsersBatchUpdate(any())).thenReturn(usersResponse); + when(serviceAgreementsApi.putPresentationServiceAgreementUsersBatchUpdate(any())).thenReturn(usersResponse); Mono existingUsersList = Mono.just(new ServiceAgreementUsersQuery().addUserIdsItem("in_userId1").addUserIdsItem("in_userId3")); - when(serviceAgreementQueryApi.getServiceAgreementUsers(eq(saInternalId))).thenReturn(existingUsersList); + when(serviceAgreementsApi.getServiceAgreementUsers(eq(saInternalId))).thenReturn(existingUsersList); Mono result = subject.updateServiceAgreementAssociations(streamTask, serviceAgreement, regularUsers); result.block(); - InOrder inOrderValidator = inOrder(serviceAgreementApi); + InOrder inOrderValidator = inOrder(serviceAgreementsApi); thenUpdateParticipantsCall(inOrderValidator, saExternalId, ADD, new ExpectedParticipantUpdate("p3", false, false)); thenUpdateParticipantsCall(inOrderValidator, saExternalId, REMOVE, @@ -309,7 +289,7 @@ void updateParticipantsLogsAllErrors() { .addParticipantsItem(new LegalEntityParticipant().externalId("p4").sharingAccounts(false) .sharingUsers(false).action(LegalEntityParticipant.ActionEnum.ADD)); - when(serviceAgreementApi.putPresentationIngestServiceAgreementParticipants(any())) + when(serviceAgreementsApi.putPresentationIngestServiceAgreementParticipants(any())) .thenReturn(Flux.concat( Mono.just(new BatchResponseItemExtended().action(ADD).resourceId("p1") .status(HTTP_STATUS_OK)), @@ -351,7 +331,8 @@ void assignPermissionsBatch() { batchProductGroupTask.setIngestionMode(BatchProductIngestionMode.UPSERT); Map> baseProductGroupMap = new HashMap<>(); - baseProductGroupMap.put(new BusinessFunctionGroup().id("business-function-group-id-1"), Collections.emptyList()); + baseProductGroupMap.put(new BusinessFunctionGroup().id("business-function-group-id-1"), + List.of(new BaseProductGroup().internalId("data-group-id"))); Map>> usersPermissions = new HashMap<>(); usersPermissions.put( @@ -372,21 +353,22 @@ void assignPermissionsBatch() { ).dataGroupIdentifiers(Collections.emptyList()), new PresentationFunctionGroupDataGroup().functionGroupIdentifier( new PresentationIdentifier().idIdentifier("business-function-group-id-1") - ).dataGroupIdentifiers(Collections.emptyList()) + ).dataGroupIdentifiers(List.of(new PresentationDataGroupIdentifier().idIdentifier("data-group-id"))) )) ); - when(functionGroupApi.getFunctionGroups("sa-internal-id")) + when(functionGroupsApi.getFunctionGroups("sa-internal-id")) .thenReturn(Flux.just( new FunctionGroupItem().id("system-group-id-1").name("SYSTEM_FUNCTION_GROUP").type(FunctionGroupItem.TypeEnum.SYSTEM), new FunctionGroupItem().id("system-group-id-2").name("Full access").type(FunctionGroupItem.TypeEnum.TEMPLATE) )); - when(userQueryApi.getPersistenceApprovalPermissions("user-internal-id", "sa-internal-id")) + when(accessControlUsersApi.getPersistenceApprovalPermissions("user-internal-id", "sa-internal-id")) .thenReturn(Mono.just(new PersistenceApprovalPermissions().items(Arrays.asList( new PersistenceApprovalPermissionsGetResponseBody().functionGroupId("system-group-id-1").dataGroupIds(Collections.emptyList()), new PersistenceApprovalPermissionsGetResponseBody().functionGroupId("system-group-id-2").dataGroupIds(Collections.emptyList()), - new PersistenceApprovalPermissionsGetResponseBody().functionGroupId("system-group-id-3").dataGroupIds(Collections.emptyList()) + new PersistenceApprovalPermissionsGetResponseBody().functionGroupId("system-group-id-3").dataGroupIds(Collections.emptyList()), + new PersistenceApprovalPermissionsGetResponseBody().functionGroupId("business-function-group-id-1").dataGroupIds(List.of("data-group-id")) )))); when(accessControlUsersApi.putAssignUserPermissions(expectedPermissions)) @@ -454,13 +436,13 @@ void assignPermissionsBatchNoExistingFunctionGroups() { )) )); - when(functionGroupApi.getFunctionGroups("sa-internal-id")) + when(functionGroupsApi.getFunctionGroups("sa-internal-id")) .thenReturn(Flux.just( new FunctionGroupItem().id("system-group-id-1").name("SFG").type(FunctionGroupItem.TypeEnum.SYSTEM), new FunctionGroupItem().id("function-group-id-1").name("Full access").type(FunctionGroupItem.TypeEnum.TEMPLATE) )); - when(userQueryApi.getPersistenceApprovalPermissions("user-internal-id", "sa-internal-id")) + when(accessControlUsersApi.getPersistenceApprovalPermissions("user-internal-id", "sa-internal-id")) .thenReturn(Mono.just(new PersistenceApprovalPermissions().items(Arrays.asList( new PersistenceApprovalPermissionsGetResponseBody().functionGroupId("function-group-id-1").dataGroupIds(Collections.emptyList()), new PersistenceApprovalPermissionsGetResponseBody().functionGroupId("business-function-group-id-1").dataGroupIds(Collections.singletonList("data-group-1")) @@ -557,13 +539,13 @@ void assignPermissionsBatchEmptyExistingPermissions() { )) ); - when(functionGroupApi.getFunctionGroups("sa-internal-id")) + when(functionGroupsApi.getFunctionGroups("sa-internal-id")) .thenReturn(Flux.just( new FunctionGroupItem().id("system-group-id-1").name("SYSTEM_FUNCTION_GROUP").type(FunctionGroupItem.TypeEnum.SYSTEM), new FunctionGroupItem().id("system-group-id-2").name("Full access").type(FunctionGroupItem.TypeEnum.TEMPLATE) )); - when(userQueryApi.getPersistenceApprovalPermissions("user-internal-id", "sa-internal-id")) + when(accessControlUsersApi.getPersistenceApprovalPermissions("user-internal-id", "sa-internal-id")) .thenReturn(Mono.just(new PersistenceApprovalPermissions().items(Collections.emptyList()))); when(accessControlUsersApi.putAssignUserPermissions(expectedPermissions)) @@ -617,12 +599,12 @@ void assignPermissionsBatchOnlySystemFunctionGroupExists() { )) ); - when(functionGroupApi.getFunctionGroups("sa-internal-id")) + when(functionGroupsApi.getFunctionGroups("sa-internal-id")) .thenReturn(Flux.just( new FunctionGroupItem().id("system-group-id-1").name("SYSTEM_FUNCTION_GROUP").type(FunctionGroupItem.TypeEnum.SYSTEM) )); - when(userQueryApi.getPersistenceApprovalPermissions("user-internal-id", "sa-internal-id")) + when(accessControlUsersApi.getPersistenceApprovalPermissions("user-internal-id", "sa-internal-id")) .thenReturn(Mono.just(new PersistenceApprovalPermissions().items(Collections.singletonList( new PersistenceApprovalPermissionsGetResponseBody() .functionGroupId("system-group-id-1") @@ -666,7 +648,7 @@ void deleteFunctionGroupsForServiceAgreement_templateTypeConfigured_deletesOnlyT FunctionGroupItem templateFunctionGroup = new FunctionGroupItem().id("template-group-id-2").name("Full access") .type(TypeEnum.TEMPLATE); - when(functionGroupApi.getFunctionGroups(internalSaId)) + when(functionGroupsApi.getFunctionGroups(internalSaId)) .thenReturn(Flux.just( systemFunctionGroup, templateFunctionGroup @@ -692,7 +674,7 @@ private void thenRegularUsersUpdateCall(String expectedSaExId, PresentationActio new PresentationServiceAgreementUsersBatchUpdate().action(expectedAction) .users(Stream.of(expectedUserIds).map(userId -> new PresentationServiceAgreementUserPair() .externalUserId(userId).externalServiceAgreementId(expectedSaExId)).collect(Collectors.toList())); - verify(serviceAgreementApi, times(1)) + verify(serviceAgreementsApi, times(1)) .putPresentationServiceAgreementUsersBatchUpdate(eq(expectedRegularUserAddUpdate)); } @@ -703,7 +685,7 @@ private void thenUpdateParticipantsCall(InOrder validator, String expectedSaExId .externalServiceAgreementId(expectedSaExId).externalParticipantId(ep.exId) .sharingAccounts(ep.sharingAccounts).sharingUsers(ep.sharingAccounts).action(expectedAction)) .collect(Collectors.toList())); - validator.verify(serviceAgreementApi).putPresentationIngestServiceAgreementParticipants(eq(expectedRequest)); + validator.verify(serviceAgreementsApi).putPresentationIngestServiceAgreementParticipants(eq(expectedRequest)); } private ServiceAgreement buildInputServiceAgreement(String saInternalId, String saExternalId, String description, diff --git a/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/AccessGroupServiceUpdateFunctionGroupsTest.java b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/AccessGroupServiceUpdateFunctionGroupsTest.java index 61133627c..c9a0323f9 100644 --- a/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/AccessGroupServiceUpdateFunctionGroupsTest.java +++ b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/AccessGroupServiceUpdateFunctionGroupsTest.java @@ -3,26 +3,21 @@ import static com.backbase.stream.legalentity.model.LegalEntityStatus.ENABLED; import static org.mockito.ArgumentMatchers.any; -import com.backbase.dbs.accesscontrol.api.service.v2.DataGroupApi; -import com.backbase.dbs.accesscontrol.api.service.v2.DataGroupsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.FunctionGroupApi; -import com.backbase.dbs.accesscontrol.api.service.v2.FunctionGroupsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementQueryApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.UserQueryApi; -import com.backbase.dbs.accesscontrol.api.service.v2.UsersApi; -import com.backbase.dbs.accesscontrol.api.service.v2.model.BatchResponseItemExtended; -import com.backbase.dbs.accesscontrol.api.service.v2.model.FunctionGroupItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.Functiongroupupdate; -import com.backbase.dbs.accesscontrol.api.service.v2.model.IdItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.Permission; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationFunctionGroupPutRequestBody; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationIdentifier; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationIngestFunctionGroup; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationPermission; -import com.backbase.dbs.accesscontrol.api.service.v2.model.PresentationPermissionFunctionGroupUpdate; -import com.backbase.dbs.accesscontrol.api.service.v2.model.Privilege; +import com.backbase.dbs.accesscontrol.api.service.v3.DataGroupsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.FunctionGroupsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.ServiceAgreementsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.UsersApi; +import com.backbase.dbs.accesscontrol.api.service.v3.model.BatchResponseItemExtended; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupUpdate; +import com.backbase.dbs.accesscontrol.api.service.v3.model.IdItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.Permission; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationFunctionGroupPutRequestBody; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationIdentifier; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationIngestFunctionGroup; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationPermission; +import com.backbase.dbs.accesscontrol.api.service.v3.model.PresentationPermissionFunctionGroupUpdate; +import com.backbase.dbs.accesscontrol.api.service.v3.model.Privilege; import com.backbase.dbs.user.api.service.v2.UserManagementApi; import com.backbase.stream.configuration.DeletionProperties; import com.backbase.stream.legalentity.model.BusinessFunction; @@ -33,7 +28,6 @@ import com.backbase.stream.utils.BatchResponseUtils; import com.backbase.stream.worker.exception.StreamTaskException; import com.backbase.stream.worker.model.StreamTask; -import java.math.BigDecimal; import java.time.LocalDate; import java.util.Arrays; import java.util.Collections; @@ -59,30 +53,15 @@ class AccessGroupServiceUpdateFunctionGroupsTest { @Mock private UserManagementApi usersApi; - @Mock - private UserQueryApi userQueryApi; - @Mock private UsersApi accessControlUsersApi; - @Mock - private DataGroupApi dataGroupApi; - @Mock private DataGroupsApi dataGroupsApi; - @Mock - private FunctionGroupApi functionGroupApi; - @Mock private FunctionGroupsApi functionGroupsApi; - @Mock - private ServiceAgreementQueryApi serviceAgreementQueryApi; - - @Mock - private ServiceAgreementApi serviceAgreementApi; - @Mock private ServiceAgreementsApi serviceAgreementsApi; @@ -117,7 +96,7 @@ void setupJobRole() { .addParticipantsItem(new LegalEntityParticipant().externalId("p3").sharingAccounts(false) .sharingUsers(false).action(LegalEntityParticipant.ActionEnum.ADD)); - Mockito.when(functionGroupApi.getFunctionGroups(saInternalId)) + Mockito.when(functionGroupsApi.getFunctionGroups(saInternalId)) .thenReturn(Flux.fromIterable(Collections.singletonList(new FunctionGroupItem() .name("jobRoleOld").id("2") .addPermissionsItem(new Permission().functionId("102") @@ -155,7 +134,7 @@ void setupJobRole() { Mockito.verify(functionGroupsApi) .postPresentationIngestFunctionGroup(new PresentationIngestFunctionGroup() .externalServiceAgreementId(saExternalId) - .apsId(BigDecimal.ONE) + .apsId(1L) .name("jobRoleNew") .description("jobRoleNew") .type(PresentationIngestFunctionGroup.TypeEnum.REGULAR) @@ -197,7 +176,7 @@ void updateJobRole() { .addParticipantsItem(new LegalEntityParticipant().externalId("p3").sharingAccounts(false) .sharingUsers(false).action(LegalEntityParticipant.ActionEnum.ADD)); - Mockito.when(functionGroupApi.getFunctionGroups(saInternalId)) + Mockito.when(functionGroupsApi.getFunctionGroups(saInternalId)) .thenReturn(Flux.fromIterable(Collections.singletonList(new FunctionGroupItem() .name("jobRole").id("1") .addPermissionsItem(new Permission().functionId("101") @@ -235,7 +214,7 @@ void updateJobRole() { Mockito.verify(functionGroupsApi) .putFunctionGroupsUpdate(Collections.singletonList(new PresentationFunctionGroupPutRequestBody() .identifier(new PresentationIdentifier().idIdentifier("1")) - .functionGroup(new Functiongroupupdate() + .functionGroup(new FunctionGroupUpdate() .name("jobRole") .description("jobRole") .metadata(Map.of("key1","value1")) @@ -274,7 +253,7 @@ void updateJobRoleItemStatusIs400() { .addParticipantsItem(new LegalEntityParticipant().externalId("p3").sharingAccounts(false) .sharingUsers(false).action(LegalEntityParticipant.ActionEnum.ADD)); - Mockito.when(functionGroupApi.getFunctionGroups(saInternalId)) + Mockito.when(functionGroupsApi.getFunctionGroups(saInternalId)) .thenReturn(Flux.fromIterable(Collections.singletonList(new FunctionGroupItem() .name("jobRole").id("1") .addPermissionsItem(new Permission().functionId("101") @@ -314,7 +293,7 @@ void updateJobRoleItemStatusIs400() { Mockito.verify(functionGroupsApi) .putFunctionGroupsUpdate(Collections.singletonList(new PresentationFunctionGroupPutRequestBody() .identifier(new PresentationIdentifier().idIdentifier("1")) - .functionGroup(new Functiongroupupdate() + .functionGroup(new FunctionGroupUpdate() .name("jobRole") .description("jobRole") .addPermissionsItem(new PresentationPermissionFunctionGroupUpdate() @@ -352,7 +331,7 @@ void setupFunctionGroups() { .addParticipantsItem(new LegalEntityParticipant().externalId("p3").sharingAccounts(false) .sharingUsers(false).action(LegalEntityParticipant.ActionEnum.ADD)); - Mockito.when(functionGroupApi.getFunctionGroups(saInternalId)) + Mockito.when(functionGroupsApi.getFunctionGroups(saInternalId)) .thenReturn(Flux.fromIterable(Collections.singletonList(new FunctionGroupItem() .name("fg1").id("1") .addPermissionsItem(new Permission().functionId("101") @@ -392,7 +371,7 @@ void setupFunctionGroups() { Mockito.verify(functionGroupsApi) .putFunctionGroupsUpdate(Collections.singletonList(new PresentationFunctionGroupPutRequestBody() .identifier(new PresentationIdentifier().idIdentifier("1")) - .functionGroup(new Functiongroupupdate() + .functionGroup(new FunctionGroupUpdate() .name("fg1") .description("fg1") .addPermissionsItem(new PresentationPermissionFunctionGroupUpdate() diff --git a/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/LegalEntityServiceTest.java b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/LegalEntityServiceTest.java index 1ab83c25e..a65a21e9a 100644 --- a/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/LegalEntityServiceTest.java +++ b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/LegalEntityServiceTest.java @@ -4,10 +4,9 @@ import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.when; -import com.backbase.dbs.accesscontrol.api.service.v2.LegalEntitiesApi; -import com.backbase.dbs.accesscontrol.api.service.v2.LegalEntityApi; -import com.backbase.dbs.accesscontrol.api.service.v2.model.BatchResponseItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.LegalEntityItemBase; +import com.backbase.dbs.accesscontrol.api.service.v3.LegalEntitiesApi; +import com.backbase.dbs.accesscontrol.api.service.v3.model.BatchResponseItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.LegalEntityItemBase; import com.backbase.stream.legalentity.model.LegalEntity; import com.backbase.stream.mapper.LegalEntityMapper; import com.backbase.stream.utils.BatchResponseUtils; @@ -28,14 +27,12 @@ class LegalEntityServiceTest { @Mock private LegalEntitiesApi legalEntitiesApi; - @Mock - private LegalEntityApi legalEntityApi; private LegalEntityMapper mapper = Mappers.getMapper(LegalEntityMapper.class); @BeforeEach void setup() { - subject = new LegalEntityService(legalEntitiesApi, legalEntityApi, new BatchResponseUtils()); + subject = new LegalEntityService(legalEntitiesApi, new BatchResponseUtils()); } @Test diff --git a/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/UserServiceTest.java b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/UserServiceTest.java index afae4e4c3..b304299c5 100644 --- a/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/UserServiceTest.java +++ b/stream-access-control/access-control-core/src/test/java/com/backbase/stream/service/UserServiceTest.java @@ -3,6 +3,7 @@ import static com.backbase.stream.legalentity.model.IdentityUserLinkStrategy.CREATE_IN_IDENTITY; import static com.backbase.stream.legalentity.model.IdentityUserLinkStrategy.IMPORT_FROM_IDENTIY; import static com.backbase.stream.LambdaAssertions.assertEqualsTo; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; @@ -11,6 +12,7 @@ import com.backbase.dbs.user.api.service.v2.IdentityManagementApi; import com.backbase.dbs.user.api.service.v2.UserManagementApi; +import com.backbase.dbs.user.api.service.v2.UserProfileManagementApi; import com.backbase.dbs.user.api.service.v2.model.*; import com.backbase.identity.integration.api.service.v1.IdentityIntegrationServiceApi; import com.backbase.identity.integration.api.service.v1.model.EnhancedUserRepresentation; @@ -27,7 +29,9 @@ import java.util.Optional; import com.backbase.stream.product.task.ProductGroupTask; +import com.backbase.stream.worker.exception.StreamTaskException; import com.backbase.stream.worker.model.StreamTask; +import java.util.UUID; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -54,9 +58,13 @@ class UserServiceTest { @Mock private IdentityIntegrationServiceApi identityIntegrationApi; + @Mock + private UserProfileManagementApi userManagerProfileApi; + @BeforeEach void setup() { - subject = new UserService(usersApi, identityManagementApi, Optional.of(identityIntegrationApi)); + subject = new UserService(usersApi, identityManagementApi, Optional.of(identityIntegrationApi), + userManagerProfileApi); } @Test @@ -181,7 +189,7 @@ void createOrImportIdentityUserUpdateAttributesWhenIFIStrategy() { final IdentityUserLinkStrategy strategy = IMPORT_FROM_IDENTIY; CreateIdentityResponse response = new CreateIdentityResponse().externalId(externalId).internalId(internalId); - when(identityManagementApi.createIdentity(any())).thenReturn(Mono.just(response)); + when(identityManagementApi.importIdentity(any())).thenReturn(Mono.just(response)); when(identityManagementApi.updateIdentity(eq(internalId), any())).thenReturn(Mono.empty().then()); @@ -193,9 +201,9 @@ void createOrImportIdentityUserUpdateAttributesWhenIFIStrategy() { result.subscribe(assertEqualsTo(user)); - CreateIdentityRequest expectedCreateIdentityRequest = new CreateIdentityRequest().externalId(externalId) - .legalEntityInternalId(legalEntityId); - verify(identityManagementApi).createIdentity(expectedCreateIdentityRequest); + ImportIdentity expectedImportIdentityRequest = new ImportIdentity().externalId(externalId) + .legalEntityInternalId(legalEntityId); + verify(identityManagementApi).importIdentity(expectedImportIdentityRequest); UpdateIdentityRequest expectedUpdateIdentityRequest = new UpdateIdentityRequest().attributes(attributesMap); verify(identityManagementApi).updateIdentity(internalId, expectedUpdateIdentityRequest); } @@ -208,7 +216,7 @@ void createOrImportIdentityUserDontUpdateWhenNoAttributesPresent() { final IdentityUserLinkStrategy strategy = IMPORT_FROM_IDENTIY; CreateIdentityResponse response = new CreateIdentityResponse().externalId(externalId).internalId(internalId); - when(identityManagementApi.createIdentity(any())).thenReturn(Mono.just(response)); + when(identityManagementApi.importIdentity(any())).thenReturn(Mono.just(response)); User user = new User().externalId(externalId).identityLinkStrategy(strategy); @@ -217,12 +225,53 @@ void createOrImportIdentityUserDontUpdateWhenNoAttributesPresent() { result.subscribe(assertEqualsTo(user)); - CreateIdentityRequest expectedCreateIdentityRequest = new CreateIdentityRequest().externalId(externalId) - .legalEntityInternalId(legalEntityId); - verify(identityManagementApi).createIdentity(expectedCreateIdentityRequest); + ImportIdentity expectedImportIdentityRequest = new ImportIdentity().externalId(externalId) + .legalEntityInternalId(legalEntityId); + verify(identityManagementApi).importIdentity(expectedImportIdentityRequest); verifyNoMoreInteractions(identityManagementApi); } + @Test + void createOrImportIdentityUserUpdateAdditionsWhenIFIStrategy() { + final String internalId = "someInternalId"; + final String externalId = "someExternalId"; + final String legalEntityId = "someLegalEntityId"; + final Map additionsMap = Collections.singletonMap("someKey", "someValue"); + + CreateIdentityResponse response = new CreateIdentityResponse().externalId(externalId).internalId(internalId); + when(identityManagementApi.importIdentity(any())).thenReturn(Mono.just(response)); + when(identityManagementApi.updateIdentity(eq(internalId), any())).thenReturn(Mono.empty().then()); + + User user = new User().externalId(externalId).additions(additionsMap).identityLinkStrategy(IMPORT_FROM_IDENTIY); + Mono result = subject.createOrImportIdentityUser(user, legalEntityId, new ProductGroupTask()); + + result.subscribe(assertEqualsTo(user)); + ImportIdentity expectedImportIdentityRequest = + new ImportIdentity() + .externalId(externalId) + .additions(additionsMap) + .legalEntityInternalId(legalEntityId); + + verify(identityManagementApi).importIdentity(expectedImportIdentityRequest); + verify(identityManagementApi).updateIdentity(internalId, new UpdateIdentityRequest().additions(additionsMap)); + } + + @Test + void createOrImportIdentityUserUpdateAdditionsWithError() { + final String internalId = "someInternalId"; + final String externalId = "someExternalId"; + final String legalEntityId = "someLegalEntityId"; + final Map additionsMap = Collections.singletonMap("someKey", "someValue"); + + CreateIdentityResponse response = new CreateIdentityResponse().externalId(externalId).internalId(internalId); + when(identityManagementApi.importIdentity(any())).thenReturn(Mono.just(response)); + + User user = new User().externalId(externalId).additions(additionsMap).identityLinkStrategy(IMPORT_FROM_IDENTIY); + + StepVerifier.create(subject.createOrImportIdentityUser(user, legalEntityId, new ProductGroupTask())) + .expectError(StreamTaskException.class); + } + @Test void createOrImportIdentityUserDoesNotUpdateUserWhenNoIFIStrategy() { final String internalId = "someInternalId"; @@ -280,6 +329,48 @@ void createOrImportIdentityUserFailure() { } + @Test + void createOrImportIdentityErrorOnImport() { + final String externalId = "someExternalId"; + final String legalEntityId = "someLegalEntityId"; + final Map attributesMap = Collections.singletonMap("someKey", "someValue"); + final IdentityUserLinkStrategy strategy = IMPORT_FROM_IDENTIY; + final String emailAddress = "some@email.com"; + final String mobileNumber = "123456"; + final String fullName = "someName"; + + when(identityManagementApi.importIdentity(any())).thenReturn(Mono.error(WebClientResponseException.create(500,"", new HttpHeaders(), "Error response".getBytes(StandardCharsets.UTF_8), null))); + + User user = new User().externalId(externalId).attributes(attributesMap) + .identityLinkStrategy(strategy).fullName(fullName) + .emailAddress(new EmailAddress().address(emailAddress)) + .mobileNumber(new PhoneNumber().number(mobileNumber)); + + + Mono result = subject.createOrImportIdentityUser(user, legalEntityId, new ProductGroupTask()); + + StepVerifier.create(result) + .expectError().verify(); + } + + @Test + void createOrImportIdentityErrorOnCreateWhenMissingEmail() { + final String externalId = "someExternalId"; + final String legalEntityId = "someLegalEntityId"; + final Map attributesMap = Collections.singletonMap("someKey", "someValue"); + final IdentityUserLinkStrategy strategy = CREATE_IN_IDENTITY; + final String emailAddress = null; + final String mobileNumber = "123456"; + final String fullName = "someName"; + + User user = new User().externalId(externalId).attributes(attributesMap) + .identityLinkStrategy(strategy).fullName(fullName) + .emailAddress(new EmailAddress().address(emailAddress)) + .mobileNumber(new PhoneNumber().number(mobileNumber)); + + assertThrows(NullPointerException.class, () -> subject.createOrImportIdentityUser(user, legalEntityId, new ProductGroupTask())); + } + @Test void getUserByExternalId() { final String externalId = "someExternalId"; @@ -399,4 +490,30 @@ void updateUser_batchResponseItem_fail() { StepVerifier.create(result) .expectError().verify(); } + + @Test + void getUserProfile() { + final String userId = UUID.randomUUID().toString(); + + when(userManagerProfileApi.getUserProfile(userId)).thenReturn(Mono.just(new UserProfile())); + + Mono result = subject.getUserProfile(userId); + + StepVerifier.create(result) + .assertNext(Assertions::assertNotNull) + .verifyComplete(); + } + + @Test + void getUserProfile_notFound() { + final String userId = UUID.randomUUID().toString(); + + when(userManagerProfileApi.getUserProfile(userId)).thenReturn(Mono.error(WebClientResponseException.NotFound.create(404, "not found", new HttpHeaders(), new byte[0], null))); + + Mono result = subject.getUserProfile(userId); + + StepVerifier.create(result) + .expectNextCount(0) + .verifyComplete(); + } } diff --git a/stream-approvals/approvals-core/src/main/java/com/backbase/stream/ApprovalSaga.java b/stream-approvals/approvals-core/src/main/java/com/backbase/stream/ApprovalSaga.java index dc60ef8f7..b29d7879b 100644 --- a/stream-approvals/approvals-core/src/main/java/com/backbase/stream/ApprovalSaga.java +++ b/stream-approvals/approvals-core/src/main/java/com/backbase/stream/ApprovalSaga.java @@ -2,7 +2,7 @@ import static com.backbase.stream.product.utils.StreamUtils.nullableCollectionToStream; -import com.backbase.dbs.accesscontrol.api.service.v2.model.FunctionGroupItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupItem; import com.backbase.stream.approval.model.Approval; import com.backbase.stream.approval.model.ApprovalType; import com.backbase.stream.approval.model.Policy; diff --git a/stream-audiences/README.md b/stream-audiences/README.md new file mode 100644 index 000000000..c87c39c9e --- /dev/null +++ b/stream-audiences/README.md @@ -0,0 +1,6 @@ +# Stream Audiences Integration +The goal of this module is to enable ingestion of Customers into Retail Customers and Business Customers segments. + +The ingestion is done through an HTTP call towards `User Segments Collector` service. + +`UserKindSegmentationSaga` (responsible for triggering the ingestion towards the collector) is triggered from `LegalEntitySaga` \ No newline at end of file diff --git a/stream-audiences/audiences-core/pom.xml b/stream-audiences/audiences-core/pom.xml new file mode 100644 index 000000000..439616893 --- /dev/null +++ b/stream-audiences/audiences-core/pom.xml @@ -0,0 +1,42 @@ + + + 4.0.0 + + + com.backbase.stream + stream-audiences + 3.61.0 + + + audiences-core + jar + Stream :: Audiences Core + + + true + + + + + com.backbase.stream + stream-dbs-clients + ${project.version} + + + com.backbase.stream + stream-worker + ${project.version} + + + io.projectreactor + reactor-test + test + + + com.backbase.buildingblocks + service-sdk-starter-test + test + + + + diff --git a/stream-audiences/audiences-core/src/main/java/com/backbase/stream/audiences/UserKindSegmentationSaga.java b/stream-audiences/audiences-core/src/main/java/com/backbase/stream/audiences/UserKindSegmentationSaga.java new file mode 100644 index 000000000..90e69af6b --- /dev/null +++ b/stream-audiences/audiences-core/src/main/java/com/backbase/stream/audiences/UserKindSegmentationSaga.java @@ -0,0 +1,67 @@ +package com.backbase.stream.audiences; + +import com.backbase.audiences.collector.api.service.v1.HandlersServiceApi; +import com.backbase.stream.configuration.UserKindSegmentationProperties; +import com.backbase.stream.worker.StreamTaskExecutor; +import com.backbase.stream.worker.exception.StreamTaskException; +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; + +@Slf4j +public class UserKindSegmentationSaga implements StreamTaskExecutor { + + public static final String ENTITY = "CustomerOnboarded"; + public static final String INGEST = "ingest"; + public static final String SUCCESS = "success"; + public static final String ERROR = "error"; + public static final String INGESTED_SUCCESSFULLY = "Customer ingested successfully"; + public static final String FAILED_TO_INGEST = "Failed to ingest Customer"; + + private final HandlersServiceApi handlersServiceApi; + private final UserKindSegmentationProperties userKindSegmentationProperties; + + public UserKindSegmentationSaga( + HandlersServiceApi handlersServiceApi, + UserKindSegmentationProperties userKindSegmentationProperties + ) { + this.handlersServiceApi = handlersServiceApi; + this.userKindSegmentationProperties = userKindSegmentationProperties; + } + + @Override + public Mono executeTask(UserKindSegmentationTask streamTask) { + var request = streamTask.getCustomerOnboardedRequest(); + + return handlersServiceApi.customerOnboarded(request) + .then(Mono.fromCallable(() -> { + streamTask.info(ENTITY, INGEST, SUCCESS, null, request.getInternalUserId(), INGESTED_SUCCESSFULLY); + return streamTask; + })) + .onErrorResume(throwable -> { + streamTask.error(ENTITY, INGEST, ERROR, null, request.getInternalUserId(), FAILED_TO_INGEST); + return Mono.error(new StreamTaskException(streamTask, throwable, FAILED_TO_INGEST)); + }); + } + + @Override + public Mono rollBack(UserKindSegmentationTask streamTask) { + return null; + } + + public boolean isEnabled() { + if (userKindSegmentationProperties == null) { + return false; + } + + return userKindSegmentationProperties.enabled(); + } + + public String getDefaultCustomerCategory() { + if (!isEnabled()) { + return null; + } + + return userKindSegmentationProperties.defaultCustomerCategory(); + } + +} diff --git a/stream-audiences/audiences-core/src/main/java/com/backbase/stream/audiences/UserKindSegmentationTask.java b/stream-audiences/audiences-core/src/main/java/com/backbase/stream/audiences/UserKindSegmentationTask.java new file mode 100644 index 000000000..f78674f74 --- /dev/null +++ b/stream-audiences/audiences-core/src/main/java/com/backbase/stream/audiences/UserKindSegmentationTask.java @@ -0,0 +1,16 @@ +package com.backbase.stream.audiences; + +import com.backbase.audiences.collector.api.service.v1.model.CustomerOnboardedRequest; +import com.backbase.stream.worker.model.StreamTask; +import lombok.Data; + +@Data +public class UserKindSegmentationTask extends StreamTask { + + private CustomerOnboardedRequest customerOnboardedRequest; + + @Override + public String getName() { + return "customersSegment"; + } +} diff --git a/stream-audiences/audiences-core/src/main/java/com/backbase/stream/configuration/AudiencesSegmentationConfiguration.java b/stream-audiences/audiences-core/src/main/java/com/backbase/stream/configuration/AudiencesSegmentationConfiguration.java new file mode 100644 index 000000000..ee01d1479 --- /dev/null +++ b/stream-audiences/audiences-core/src/main/java/com/backbase/stream/configuration/AudiencesSegmentationConfiguration.java @@ -0,0 +1,21 @@ +package com.backbase.stream.configuration; + +import com.backbase.audiences.collector.api.service.v1.HandlersServiceApi; +import com.backbase.stream.audiences.UserKindSegmentationSaga; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@EnableConfigurationProperties({UserKindSegmentationProperties.class}) +@Configuration +public class AudiencesSegmentationConfiguration { + + @Bean + public UserKindSegmentationSaga userKindSegmentationSaga( + HandlersServiceApi handlersServiceApi, + UserKindSegmentationProperties userKindSegmentationProperties + ) { + return new UserKindSegmentationSaga(handlersServiceApi, userKindSegmentationProperties); + } + +} diff --git a/stream-audiences/audiences-core/src/main/java/com/backbase/stream/configuration/UserKindSegmentationProperties.java b/stream-audiences/audiences-core/src/main/java/com/backbase/stream/configuration/UserKindSegmentationProperties.java new file mode 100644 index 000000000..5c63208d5 --- /dev/null +++ b/stream-audiences/audiences-core/src/main/java/com/backbase/stream/configuration/UserKindSegmentationProperties.java @@ -0,0 +1,11 @@ +package com.backbase.stream.configuration; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "backbase.stream.audiences.segmentation.user-kind") +public record UserKindSegmentationProperties( + boolean enabled, + String defaultCustomerCategory +) { + +} diff --git a/stream-audiences/audiences-core/src/main/java/com/backbase/stream/package-info.java b/stream-audiences/audiences-core/src/main/java/com/backbase/stream/package-info.java new file mode 100644 index 000000000..831c97c86 --- /dev/null +++ b/stream-audiences/audiences-core/src/main/java/com/backbase/stream/package-info.java @@ -0,0 +1 @@ +package com.backbase.stream; \ No newline at end of file diff --git a/stream-audiences/audiences-core/src/test/java/com/backbase/stream/audiences/UserKindSegmentationSagaTest.java b/stream-audiences/audiences-core/src/test/java/com/backbase/stream/audiences/UserKindSegmentationSagaTest.java new file mode 100644 index 000000000..5bd53c429 --- /dev/null +++ b/stream-audiences/audiences-core/src/test/java/com/backbase/stream/audiences/UserKindSegmentationSagaTest.java @@ -0,0 +1,82 @@ +package com.backbase.stream.audiences; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.backbase.audiences.collector.api.service.v1.HandlersServiceApi; +import com.backbase.audiences.collector.api.service.v1.model.CustomerOnboardedRequest; +import com.backbase.audiences.collector.api.service.v1.model.CustomerOnboardedRequest.UserKindEnum; +import com.backbase.stream.configuration.UserKindSegmentationProperties; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Mono; + +@ExtendWith(MockitoExtension.class) +class UserKindSegmentationSagaTest { + + @Mock + private HandlersServiceApi handlersServiceApi; + + @Mock + private UserKindSegmentationProperties userKindSegmentationProperties; + + @InjectMocks + private UserKindSegmentationSaga userKindSegmentationSaga; + + @Test + void testExecuteTask() { + var task = createTask(); + when(handlersServiceApi.customerOnboarded(any())).thenReturn(Mono.empty()); + + userKindSegmentationSaga.executeTask(task).block(); + + verify(handlersServiceApi).customerOnboarded(any()); + } + + @Test + void isDisabledByDefault() { + var saga = new UserKindSegmentationSaga( + handlersServiceApi, + null + ); + + assertFalse(saga.isEnabled()); + } + + @Test + void defaultCustomerCategoryIsNullWhenSagaIsDisabled() { + when(userKindSegmentationProperties.enabled()).thenReturn(false); + + assertNull(userKindSegmentationSaga.getDefaultCustomerCategory()); + } + + @Test + void rollbackReturnsNull() { + assertNull(userKindSegmentationSaga.rollBack(createTask())); + } + + @Test + void returnsDefaultCustomerCategoryFromProperties() { + when(userKindSegmentationProperties.enabled()).thenReturn(true); + when(userKindSegmentationProperties.defaultCustomerCategory()).thenReturn("RETAIL"); + + assertEquals("RETAIL", userKindSegmentationSaga.getDefaultCustomerCategory()); + } + + private UserKindSegmentationTask createTask() { + var task = new UserKindSegmentationTask(); + task.setCustomerOnboardedRequest( + new CustomerOnboardedRequest() + .internalUserId("internal-id") + .userKind(UserKindEnum.RETAILCUSTOMER) + ); + return task; + } +} \ No newline at end of file diff --git a/stream-audiences/audiences-core/src/test/java/com/backbase/stream/configuration/UserKindSegmentationPropertiesTest.java b/stream-audiences/audiences-core/src/test/java/com/backbase/stream/configuration/UserKindSegmentationPropertiesTest.java new file mode 100644 index 000000000..37adbe8c5 --- /dev/null +++ b/stream-audiences/audiences-core/src/test/java/com/backbase/stream/configuration/UserKindSegmentationPropertiesTest.java @@ -0,0 +1,17 @@ +package com.backbase.stream.configuration; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class UserKindSegmentationPropertiesTest { + + @Test + void testProperties() { + UserKindSegmentationProperties properties = new UserKindSegmentationProperties(true, "testCategory"); + + assertTrue(properties.enabled()); + assertEquals("testCategory", properties.defaultCustomerCategory()); + } +} \ No newline at end of file diff --git a/stream-audiences/pom.xml b/stream-audiences/pom.xml new file mode 100644 index 000000000..94e567651 --- /dev/null +++ b/stream-audiences/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + + com.backbase.stream + stream-services + 3.61.0 + + + stream-audiences + + pom + Stream :: Audiences + + + audiences-core + + + diff --git a/stream-compositions/api/integrations-api/payment-order-integration-api/src/main/resources/service-api-v2.yaml b/stream-compositions/api/integrations-api/payment-order-integration-api/src/main/resources/service-api-v2.yaml index ae67efd1e..9ba4f7b52 100644 --- a/stream-compositions/api/integrations-api/payment-order-integration-api/src/main/resources/service-api-v2.yaml +++ b/stream-compositions/api/integrations-api/payment-order-integration-api/src/main/resources/service-api-v2.yaml @@ -89,4 +89,4 @@ components: paymentOrder: type: array items: - $ref: '../../../target/yaml/payment/payment-order-service-api-v2.12.3.yaml#/components/schemas/PaymentOrderPostRequest' + $ref: '../../../target/yaml/payment/payment-order-service-api-v2.12.4.yaml#/components/schemas/PaymentOrderPostRequest' diff --git a/stream-compositions/api/integrations-api/product-integration-api/src/main/resources/service-api-v2.yaml b/stream-compositions/api/integrations-api/product-integration-api/src/main/resources/service-api-v2.yaml index 874c298ab..b84c3223b 100644 --- a/stream-compositions/api/integrations-api/product-integration-api/src/main/resources/service-api-v2.yaml +++ b/stream-compositions/api/integrations-api/product-integration-api/src/main/resources/service-api-v2.yaml @@ -132,6 +132,10 @@ components: type: array items: $ref: '../../../../../../../api/stream-legal-entity/openapi.yaml#/components/schemas/ProductGroup' + additions: + type: object + additionalProperties: + type: string PullArrangementResponse: type: object diff --git a/stream-compositions/api/integrations-api/transaction-integration-api/src/main/resources/service-api-v2.yaml b/stream-compositions/api/integrations-api/transaction-integration-api/src/main/resources/service-api-v2.yaml index 4178a3344..eedf29ad8 100644 --- a/stream-compositions/api/integrations-api/transaction-integration-api/src/main/resources/service-api-v2.yaml +++ b/stream-compositions/api/integrations-api/transaction-integration-api/src/main/resources/service-api-v2.yaml @@ -80,6 +80,10 @@ components: type: array items: $ref: '../../../target/yaml/transaction-manager/transaction-manager-service-api-v2.10.0.yaml#/components/schemas/TransactionsPostRequestBody' + additions: + type: object + additionalProperties: + type: string InternalServerError: required: diff --git a/stream-compositions/api/service-api/payment-order-composition-api/src/main/resources/service-api-v2.yaml b/stream-compositions/api/service-api/payment-order-composition-api/src/main/resources/service-api-v2.yaml index aa439e66a..e36271388 100644 --- a/stream-compositions/api/service-api/payment-order-composition-api/src/main/resources/service-api-v2.yaml +++ b/stream-compositions/api/service-api/payment-order-composition-api/src/main/resources/service-api-v2.yaml @@ -125,7 +125,7 @@ components: paymentOrders: type: array items: - $ref: '../../../target/yaml/payment/payment-order-service-api-v2.12.3.yaml#/components/schemas/PaymentOrderPostRequest' + $ref: '../../../target/yaml/payment/payment-order-service-api-v2.12.4.yaml#/components/schemas/PaymentOrderPostRequest' PaymentOrderIngestionResponse: type: object @@ -133,11 +133,11 @@ components: newPaymentOrder: type: array items: - $ref: '../../../target/yaml/payment/payment-order-service-api-v2.12.3.yaml#/components/schemas/PaymentOrderPostResponse' + $ref: '../../../target/yaml/payment/payment-order-service-api-v2.12.4.yaml#/components/schemas/PaymentOrderPostResponse' updatedPaymentOrder: type: array items: - $ref: '../../../target/yaml/payment/payment-order-service-api-v2.12.3.yaml#/components/schemas/PaymentOrderPostResponse' + $ref: '../../../target/yaml/payment/payment-order-service-api-v2.12.4.yaml#/components/schemas/PaymentOrderPostResponse' deletedPaymentOrder: type: array items: diff --git a/stream-compositions/api/service-api/product-composition-api/src/main/resources/service-api-v2.yaml b/stream-compositions/api/service-api/product-composition-api/src/main/resources/service-api-v2.yaml index bb4af5106..80c9a0d5c 100644 --- a/stream-compositions/api/service-api/product-composition-api/src/main/resources/service-api-v2.yaml +++ b/stream-compositions/api/service-api/product-composition-api/src/main/resources/service-api-v2.yaml @@ -216,6 +216,10 @@ components: type: array items: $ref: '../../../../../../../api/stream-legal-entity/openapi.yaml#/components/schemas/ProductGroup' + additions: + type: object + additionalProperties: + type: string ArrangementPullIngestionRequest: type: object diff --git a/stream-compositions/api/service-api/transaction-composition-api/src/main/resources/service-api-v2.yaml b/stream-compositions/api/service-api/transaction-composition-api/src/main/resources/service-api-v2.yaml index ed0893c19..3eebd77fd 100644 --- a/stream-compositions/api/service-api/transaction-composition-api/src/main/resources/service-api-v2.yaml +++ b/stream-compositions/api/service-api/transaction-composition-api/src/main/resources/service-api-v2.yaml @@ -120,6 +120,10 @@ components: type: array items: $ref: '../../../target/yaml/transaction-manager/transaction-manager-service-api-v2.10.0.yaml#/components/schemas/TransactionsPostResponseBody' + additions: + type: object + additionalProperties: + type: string InternalServerError: required: diff --git a/stream-compositions/cursors/transaction-cursor/src/main/resources/application-local.yml b/stream-compositions/cursors/transaction-cursor/src/main/resources/application-local.yml index a6e6a9325..81f733293 100644 --- a/stream-compositions/cursors/transaction-cursor/src/main/resources/application-local.yml +++ b/stream-compositions/cursors/transaction-cursor/src/main/resources/application-local.yml @@ -8,7 +8,7 @@ spring: username: ${DB_USER:root} password: ${DB_PASSWORD:root} url: jdbc:mysql://localhost:3306/transaction_cursor?createDatabaseIfNotExist=true&useSSL=false - driver-class-name: com.mysql.jdbc.Driver + driver-class-name: com.mysql.cj.jdbc.Driver zipkin: enabled: false main: diff --git a/stream-compositions/cursors/transaction-cursor/src/main/resources/application.yml b/stream-compositions/cursors/transaction-cursor/src/main/resources/application.yml index 62d61dd7f..298543040 100644 --- a/stream-compositions/cursors/transaction-cursor/src/main/resources/application.yml +++ b/stream-compositions/cursors/transaction-cursor/src/main/resources/application.yml @@ -5,7 +5,7 @@ spring: username: ${DB_USER:root} password: ${DB_PASSWORD:root} url: jdbc:mysql://localhost:3306/transaction_cursor?createDatabaseIfNotExist=true&useSSL=false - driver-class-name: com.mysql.jdbc.Driver + driver-class-name: com.mysql.cj.jdbc.Driver zipkin: enabled: false main: diff --git a/stream-compositions/services/legal-entity-composition-service/src/main/java/com/backbase/stream/compositions/legalentity/core/config/LegalEntityConfigurationProperties.java b/stream-compositions/services/legal-entity-composition-service/src/main/java/com/backbase/stream/compositions/legalentity/core/config/LegalEntityConfigurationProperties.java index 95e1194bc..f6398b9d0 100644 --- a/stream-compositions/services/legal-entity-composition-service/src/main/java/com/backbase/stream/compositions/legalentity/core/config/LegalEntityConfigurationProperties.java +++ b/stream-compositions/services/legal-entity-composition-service/src/main/java/com/backbase/stream/compositions/legalentity/core/config/LegalEntityConfigurationProperties.java @@ -1,5 +1,6 @@ package com.backbase.stream.compositions.legalentity.core.config; +import com.backbase.stream.product.task.BatchProductIngestionMode; import lombok.Data; import lombok.Getter; import lombok.NoArgsConstructor; @@ -16,11 +17,11 @@ public class LegalEntityConfigurationProperties { private Chains chains = new Chains(); private Events events = new Events(); private Cursor cursor = new Cursor(); + private IngestionMode ingestionMode = new IngestionMode(); @Data @NoArgsConstructor public static class Events { - private Boolean enableCompleted = Boolean.FALSE; private Boolean enableFailed = Boolean.FALSE; } @@ -28,7 +29,6 @@ public static class Events { @Data @NoArgsConstructor public static class Cursor { - private Boolean enabled = Boolean.FALSE; private String baseUrl = "http://legal-entity-cursor:9000"; } @@ -36,15 +36,12 @@ public static class Cursor { @Data @NoArgsConstructor public static class Chains { - private Boolean includeSubsidiaries = Boolean.FALSE; - private ProductComposition productComposition = new ProductComposition(); } @Data public static abstract class BaseComposition { - private Boolean enabled = Boolean.FALSE; private String baseUrl = "http://localhost:9002/"; private Boolean async = Boolean.FALSE; @@ -55,6 +52,14 @@ public static class ProductComposition extends BaseComposition { } + @Data + @NoArgsConstructor + public static class IngestionMode { + private BatchProductIngestionMode.FunctionGroupsMode functionGroups = BatchProductIngestionMode.FunctionGroupsMode.UPSERT; + private BatchProductIngestionMode.DataGroupsMode dataGroups = BatchProductIngestionMode.DataGroupsMode.UPSERT; + private BatchProductIngestionMode.ArrangementsMode arrangements = BatchProductIngestionMode.ArrangementsMode.UPSERT; + } + public Boolean isCompletedEventEnabled() { return Boolean.TRUE.equals(events.getEnableCompleted()); } @@ -70,4 +75,12 @@ public boolean isProductChainEnabled() { public boolean isProductChainAsync() { return Boolean.TRUE.equals(chains.getProductComposition().getAsync()); } + + public BatchProductIngestionMode ingestionMode() { + return BatchProductIngestionMode.builder() + .functionGroupsMode(ingestionMode.getFunctionGroups()) + .dataGroupIngestionMode(ingestionMode.getDataGroups()) + .arrangementsMode(ingestionMode.getArrangements()) + .build(); + } } diff --git a/stream-compositions/services/legal-entity-composition-service/src/main/java/com/backbase/stream/compositions/legalentity/core/service/impl/LegalEntityIngestionServiceImpl.java b/stream-compositions/services/legal-entity-composition-service/src/main/java/com/backbase/stream/compositions/legalentity/core/service/impl/LegalEntityIngestionServiceImpl.java index f0c9cf28c..2cc2c8f93 100644 --- a/stream-compositions/services/legal-entity-composition-service/src/main/java/com/backbase/stream/compositions/legalentity/core/service/impl/LegalEntityIngestionServiceImpl.java +++ b/stream-compositions/services/legal-entity-composition-service/src/main/java/com/backbase/stream/compositions/legalentity/core/service/impl/LegalEntityIngestionServiceImpl.java @@ -4,6 +4,7 @@ import com.backbase.buildingblocks.presentation.errors.Error; import com.backbase.stream.LegalEntitySaga; import com.backbase.stream.LegalEntityTask; +import com.backbase.stream.compositions.legalentity.core.config.LegalEntityConfigurationProperties; import com.backbase.stream.compositions.legalentity.core.model.LegalEntityPullRequest; import com.backbase.stream.compositions.legalentity.core.model.LegalEntityPushRequest; import com.backbase.stream.compositions.legalentity.core.model.LegalEntityResponse; @@ -11,19 +12,18 @@ import com.backbase.stream.compositions.legalentity.core.service.LegalEntityIntegrationService; import com.backbase.stream.compositions.legalentity.core.service.LegalEntityPostIngestionService; import com.backbase.stream.legalentity.model.LegalEntity; - -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import javax.validation.ConstraintViolation; -import javax.validation.Validator; - import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import reactor.core.publisher.Mono; +import javax.validation.ConstraintViolation; +import javax.validation.Validator; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + @Slf4j @Service @AllArgsConstructor @@ -33,6 +33,7 @@ public class LegalEntityIngestionServiceImpl implements LegalEntityIngestionServ private final LegalEntityIntegrationService legalEntityIntegrationService; private final Validator validator; private final LegalEntityPostIngestionService legalEntityPostIngestionService; + private final LegalEntityConfigurationProperties legalEntityConfigurationProperties; /** * {@inheritDoc} @@ -73,10 +74,15 @@ private Mono pullLegalEntity(LegalEntityPullRequest request * @return LegalEntity */ private Mono sendToDbs(LegalEntityResponse res) { - return legalEntitySaga.executeTask(new LegalEntityTask(res.getLegalEntity())) + return legalEntitySaga + .executeTask( + new LegalEntityTask( + res.getLegalEntity(), + legalEntityConfigurationProperties.ingestionMode())) .map(LegalEntityTask::getData) .map(le -> LegalEntityResponse.builder() .legalEntity(le) + .additions(res.getAdditions()) .membershipAccounts(res.getMembershipAccounts()) .productChainEnabledFromRequest(res.getProductChainEnabledFromRequest()) .build()); diff --git a/stream-compositions/services/legal-entity-composition-service/src/main/resources/application-local.yml b/stream-compositions/services/legal-entity-composition-service/src/main/resources/application-local.yml index b2afafd4a..44874841c 100644 --- a/stream-compositions/services/legal-entity-composition-service/src/main/resources/application-local.yml +++ b/stream-compositions/services/legal-entity-composition-service/src/main/resources/application-local.yml @@ -11,28 +11,28 @@ spring: eureka: client: enabled: true - serviceUrl.defaultZone: http://localhost:8080/registry/eureka + serviceUrl.defaultZone: http://localhost:8761/eureka/ backbase: communication: services: usermanager: - direct-uri: http://localhost:8086/user-manager + direct-uri: http://localhost:8060 access-control: - direct-uri: http://localhost:8086/access-control + direct-uri: http://localhost:8040 arrangement: manager: - direct-uri: http://localhost:8082/arrangement-manager + direct-uri: http://localhost:8050 user: profile: - direct-uri: http://localhost:8086/user-profile-manager + direct-uri: http://localhost:8061 identity: integration: - direct-uri: http://localhost:8181/identity-integration-service + direct-uri: http://localhost:8070 http: client-secret: bb-secret client-id: bb-client - access-token-uri: "http://localhost:8181/oidc-token-converter-service/oauth/token" + access-token-uri: "http://localhost:7779/oauth/token" stream: legalentity: sink: @@ -43,7 +43,7 @@ backbase: integration-base-url: http://localhost:7001 chains: product-composition: - enabled: true + enabled: false base-url: http://localhost:9003 async: false events: diff --git a/stream-compositions/services/legal-entity-composition-service/src/test/java/com/backbase/stream/compositions/legalentity/core/service/impl/LegalEntityIngestionServiceImplTest.java b/stream-compositions/services/legal-entity-composition-service/src/test/java/com/backbase/stream/compositions/legalentity/core/service/impl/LegalEntityIngestionServiceImplTest.java index dc216161c..54c588bbc 100644 --- a/stream-compositions/services/legal-entity-composition-service/src/test/java/com/backbase/stream/compositions/legalentity/core/service/impl/LegalEntityIngestionServiceImplTest.java +++ b/stream-compositions/services/legal-entity-composition-service/src/test/java/com/backbase/stream/compositions/legalentity/core/service/impl/LegalEntityIngestionServiceImplTest.java @@ -67,6 +67,9 @@ class LegalEntityIngestionServiceImplTest { @Mock ProductCompositionApi productCompositionApi; + @Mock + LegalEntityConfigurationProperties legalEntityConfigurationProperties; + @BeforeEach void setUp() { legalEntityPostIngestionService = new LegalEntityPostIngestionServiceImpl(eventBus, config, @@ -76,7 +79,8 @@ void setUp() { legalEntitySaga, legalEntityIntegrationService, validator, - legalEntityPostIngestionService); + legalEntityPostIngestionService, + legalEntityConfigurationProperties); } diff --git a/stream-compositions/services/payment-order-composition-service/src/main/resources/application-local.yml b/stream-compositions/services/payment-order-composition-service/src/main/resources/application-local.yml index cf1879d1a..ded95238c 100644 --- a/stream-compositions/services/payment-order-composition-service/src/main/resources/application-local.yml +++ b/stream-compositions/services/payment-order-composition-service/src/main/resources/application-local.yml @@ -17,22 +17,22 @@ backbase: communication: services: usermanager: - direct-uri: http://localhost:8086/user-manager + direct-uri: http://localhost:8060 access-control: - direct-uri: http://localhost:8086/access-control + direct-uri: http://localhost:8040 arrangement: manager: - direct-uri: http://localhost:8082/arrangement-manager + direct-uri: http://localhost:8050 payment: order: - direct-uri: http://localhost:8090/payment-order-service + direct-uri: http://localhost:8051 transaction: manager: - direct-uri: http://localhost:8083/transaction-manager + direct-uri: http://localhost:8083 http: client-secret: bb-secret client-id: bb-client - access-token-uri: http://localhost:8181/oidc-token-converter-service/oauth/token + access-token-uri: "http://localhost:7779/oauth/token" activemq: enabled: true stream: @@ -50,7 +50,7 @@ eureka: role: live client: serviceUrl: - defaultZone: http://localhost:8080/registry/eureka/ + defaultZone: http://localhost:8761/eureka/ logging: level: diff --git a/stream-compositions/services/product-catalog-composition-service/src/main/resources/application-local.yml b/stream-compositions/services/product-catalog-composition-service/src/main/resources/application-local.yml index a862bc723..19c4e4bae 100644 --- a/stream-compositions/services/product-catalog-composition-service/src/main/resources/application-local.yml +++ b/stream-compositions/services/product-catalog-composition-service/src/main/resources/application-local.yml @@ -10,16 +10,16 @@ backbase: communication: services: usermanager: - direct-uri: http://localhost:8086/user-manager + direct-uri: http://localhost:8060 access-control: - direct-uri: http://localhost:8086/access-control + direct-uri: http://localhost:8040 arrangement: manager: - direct-uri: http://localhost:8082/arrangement-manager + direct-uri: http://localhost:8050 http: client-secret: bb-secret client-id: bb-client - access-token-uri: http://localhost:8181/oidc-token-converter-service/oauth/token + access-token-uri: "http://localhost:7779/oauth/token" stream: compositions: product-catalog: @@ -37,7 +37,7 @@ eureka: role: live client: serviceUrl: - defaultZone: http://localhost:8080/registry/eureka/ + defaultZone: http://localhost:8761/eureka/ bootstrap: enabled: true diff --git a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/config/ProductConfigurationProperties.java b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/config/ProductConfigurationProperties.java index 78e044bdc..30bf30cfb 100644 --- a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/config/ProductConfigurationProperties.java +++ b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/config/ProductConfigurationProperties.java @@ -112,4 +112,12 @@ public static class TransactionComposition extends BaseComposition { public static class PaymentOrderComposition extends BaseComposition { private List excludeProductTypeExternalIds = new ArrayList<>(); } + + public BatchProductIngestionMode ingestionMode() { + return BatchProductIngestionMode.builder() + .functionGroupsMode(ingestionMode.getFunctionGroups()) + .dataGroupIngestionMode(ingestionMode.getDataGroups()) + .arrangementsMode(ingestionMode.getArrangements()) + .build(); + } } diff --git a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/mapper/ProductRestMapper.java b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/mapper/ProductRestMapper.java index 21f96b14f..f44cd839d 100644 --- a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/mapper/ProductRestMapper.java +++ b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/mapper/ProductRestMapper.java @@ -66,7 +66,8 @@ public ResponseEntity mapResponse(ProductIngestRespons .withProductGroups( response.getProductGroups().stream() .map(productMapper::mapStreamToComposition) - .collect(Collectors.toList())), + .collect(Collectors.toList())) + .withAdditions(response.getAdditions()), HttpStatus.CREATED); } } diff --git a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/model/ProductIngestPullRequest.java b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/model/ProductIngestPullRequest.java index fca619f8e..c370f7cf8 100644 --- a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/model/ProductIngestPullRequest.java +++ b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/model/ProductIngestPullRequest.java @@ -5,8 +5,10 @@ import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; +import lombok.Setter; @Getter +@Setter @Builder @AllArgsConstructor public class ProductIngestPullRequest { diff --git a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ArrangementIngestionServiceImpl.java b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ArrangementIngestionServiceImpl.java index fb51f436d..214739054 100644 --- a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ArrangementIngestionServiceImpl.java +++ b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ArrangementIngestionServiceImpl.java @@ -85,6 +85,7 @@ private Mono sendToDbs(ArrangementIngestResponse res) return arrangementService.updateArrangement(res.getArrangement()) .map(item -> ArrangementIngestResponse.builder() .arrangement(res.getArrangement()) + .arrangementInternalId(res.getArrangementInternalId()) .config(res.getConfig()) .build()); } diff --git a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ArrangementIntegrationServiceImpl.java b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ArrangementIntegrationServiceImpl.java index 07741c555..9d20a1821 100644 --- a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ArrangementIntegrationServiceImpl.java +++ b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ArrangementIntegrationServiceImpl.java @@ -28,6 +28,7 @@ public Mono pullArrangement( .arrangementExternalId(ingestionRequest.getExternalArrangementId())) .map(item -> arrangementMapper.mapIntegrationToStream(item.getArrangement())) .map(item -> ArrangementIngestResponse.builder() + .arrangementInternalId(ingestionRequest.getArrangementId()) .arrangement(item) .build()) .onErrorResume(this::handleIntegrationError) diff --git a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ArrangementPostIngestionServiceImpl.java b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ArrangementPostIngestionServiceImpl.java index 99ef1c650..987f66b40 100644 --- a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ArrangementPostIngestionServiceImpl.java +++ b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ArrangementPostIngestionServiceImpl.java @@ -104,6 +104,7 @@ private Mono handleTransactionError(Throwable t) { private TransactionPullIngestionRequest buildTransactionPullRequest(ArrangementIngestResponse res) { return new TransactionPullIngestionRequest() + .withArrangementId(res.getArrangementInternalId()) .withExternalArrangementId(res.getArrangement().getExternalArrangementId()); } } diff --git a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ProductIngestionServiceImpl.java b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ProductIngestionServiceImpl.java index 9ac37c145..bb3713286 100644 --- a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ProductIngestionServiceImpl.java +++ b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ProductIngestionServiceImpl.java @@ -115,12 +115,7 @@ private BatchProductGroupTask buildBatchTask(ProductIngestResponse res) { .internalId(res.getServiceAgreementInternalId()) .externalId(res.getServiceAgreementExternalId())); - return new BatchProductGroupTask(res.getServiceAgreementInternalId(), bpg, - BatchProductIngestionMode.builder() - .functionGroupsMode(config.getIngestionMode().getFunctionGroups()) - .dataGroupIngestionMode(config.getIngestionMode().getDataGroups()) - .arrangementsMode(config.getIngestionMode().getArrangements()) - .build()); + return new BatchProductGroupTask(res.getServiceAgreementInternalId(), bpg, config.ingestionMode()); } private Mono validate(ProductIngestResponse res) { diff --git a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ProductIntegrationServiceImpl.java b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ProductIntegrationServiceImpl.java index 6ea16c1c9..5f364b380 100644 --- a/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ProductIntegrationServiceImpl.java +++ b/stream-compositions/services/product-composition-service/src/main/java/com/backbase/stream/compositions/product/core/service/impl/ProductIntegrationServiceImpl.java @@ -9,6 +9,8 @@ import com.backbase.stream.legalentity.model.ProductGroup; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; +import java.util.HashMap; +import java.util.Map; import org.springframework.stereotype.Service; import reactor.core.publisher.Mono; @@ -48,9 +50,9 @@ private ProductIngestResponse setRequestParameters( response.setUserExternalId(request.getUserExternalId()); response.setUserInternalId(request.getUserInternalId()); response.setSource(request.getSource()); - response.setAdditions(request.getAdditions()); response.setTransactionChainEnabledFromRequest(request.getTransactionChainEnabled()); response.setPaymentOrderChainEnabledFromRequest(request.getPaymentOrderChainEnabled()); + putAllAbsent(response, request); return response; } @@ -74,4 +76,21 @@ private Mono handleIntegrationError(Throwable e) { log.error("Error while pulling products: {}", e.getMessage()); return Mono.error(new InternalServerErrorException().withMessage(e.getMessage())); } + + private void putAllAbsent(ProductIngestResponse productIngestResponse, + ProductIngestPullRequest productIngestPullRequest) { + Map sourceAdditions = productIngestPullRequest.getAdditions(); + Map additions = productIngestResponse.getAdditions(); + if (additions == null) { + additions = new HashMap<>(); + } + if (sourceAdditions != null) { + for (String key : sourceAdditions.keySet()) { + if (!additions.containsKey(key)) { + additions.put(key, sourceAdditions.get(key)); + } + } + productIngestResponse.setAdditions(additions); + } + } } diff --git a/stream-compositions/services/product-composition-service/src/main/resources/application-local.yml b/stream-compositions/services/product-composition-service/src/main/resources/application-local.yml index dfdc3a959..6c981ec0b 100644 --- a/stream-compositions/services/product-composition-service/src/main/resources/application-local.yml +++ b/stream-compositions/services/product-composition-service/src/main/resources/application-local.yml @@ -20,29 +20,29 @@ sso: eureka: client: enabled: true - serviceUrl.defaultZone: http://localhost:8080/registry/eureka + serviceUrl.defaultZone: http://localhost:8761/eureka/ backbase: communication: services: usermanager: - direct-uri: http://localhost:8086/user-manager + direct-uri: http://localhost:8060 access-control: - direct-uri: http://localhost:8086/access-control + direct-uri: http://localhost:8040 arrangement: manager: - direct-uri: http://localhost:8082/arrangement-manager + direct-uri: http://localhost:8050 http: client-secret: bb-secret client-id: bb-client - access-token-uri: http://localhost:8181/oidc-token-converter-service/oauth/token + access-token-uri: "http://localhost:7779/oauth/token" stream: compositions: product: integration-base-url: http://localhost:7003 chains: transaction-composition: - enabled: true + enabled: false base-url: http://localhost:9004 async: false excludeProductTypeExternalIds: diff --git a/stream-compositions/services/transaction-composition-service/src/main/java/com/backbase/stream/compositions/transaction/core/model/TransactionIngestResponse.java b/stream-compositions/services/transaction-composition-service/src/main/java/com/backbase/stream/compositions/transaction/core/model/TransactionIngestResponse.java index e5e949a56..fecac21d6 100644 --- a/stream-compositions/services/transaction-composition-service/src/main/java/com/backbase/stream/compositions/transaction/core/model/TransactionIngestResponse.java +++ b/stream-compositions/services/transaction-composition-service/src/main/java/com/backbase/stream/compositions/transaction/core/model/TransactionIngestResponse.java @@ -6,6 +6,7 @@ import lombok.Getter; import java.util.List; +import java.util.Map; @Getter @Builder @@ -13,4 +14,5 @@ public class TransactionIngestResponse { private final String arrangementId; private final List transactions; + private final Map additions; } diff --git a/stream-compositions/services/transaction-composition-service/src/main/java/com/backbase/stream/compositions/transaction/core/service/impl/TransactionIngestionServiceImpl.java b/stream-compositions/services/transaction-composition-service/src/main/java/com/backbase/stream/compositions/transaction/core/service/impl/TransactionIngestionServiceImpl.java index 3eb9372e4..a6cd1ccc6 100644 --- a/stream-compositions/services/transaction-composition-service/src/main/java/com/backbase/stream/compositions/transaction/core/service/impl/TransactionIngestionServiceImpl.java +++ b/stream-compositions/services/transaction-composition-service/src/main/java/com/backbase/stream/compositions/transaction/core/service/impl/TransactionIngestionServiceImpl.java @@ -217,6 +217,7 @@ private TransactionIngestResponse buildResponse(List pullTransactions(TransactionIngestPullR return transactionIntegrationApi .pullTransactions( transactionMapper.mapStreamToIntegration(ingestPullRequest)) + .doOnNext(response -> processSuccess(response, ingestPullRequest)) .flatMapIterable(PullTransactionsResponse::getTransactions); } + + private void processSuccess(PullTransactionsResponse pullTransactionsResponse, TransactionIngestPullRequest ingestPullRequest) { + propagateAdditions(pullTransactionsResponse, ingestPullRequest); + } + + private void propagateAdditions(PullTransactionsResponse pullTransactionsResponse, + TransactionIngestPullRequest ingestPullRequest) { + if (pullTransactionsResponse.getAdditions() != null) { + Map additions = ingestPullRequest.getAdditions(); + if (additions == null) { + additions = new HashMap<>(); + } + additions.putAll(pullTransactionsResponse.getAdditions()); + ingestPullRequest.setAdditions(additions); + } + } } diff --git a/stream-compositions/services/transaction-composition-service/src/main/java/com/backbase/stream/compositions/transaction/http/TransactionController.java b/stream-compositions/services/transaction-composition-service/src/main/java/com/backbase/stream/compositions/transaction/http/TransactionController.java index 3ddb65a1f..3ac4d66cd 100644 --- a/stream-compositions/services/transaction-composition-service/src/main/java/com/backbase/stream/compositions/transaction/http/TransactionController.java +++ b/stream-compositions/services/transaction-composition-service/src/main/java/com/backbase/stream/compositions/transaction/http/TransactionController.java @@ -89,7 +89,8 @@ private ResponseEntity mapIngestionToResponse(Tran return new ResponseEntity<>( new TransactionIngestionResponse() .withTransactions( - response.getTransactions().stream().map(mapper::mapStreamToComposition).collect(Collectors.toList())), + response.getTransactions().stream().map(mapper::mapStreamToComposition).collect(Collectors.toList())) + .withAdditions(response.getAdditions()), HttpStatus.CREATED); } } diff --git a/stream-compositions/services/transaction-composition-service/src/main/resources/application-local.yml b/stream-compositions/services/transaction-composition-service/src/main/resources/application-local.yml index 5a2af6714..51333fdd5 100644 --- a/stream-compositions/services/transaction-composition-service/src/main/resources/application-local.yml +++ b/stream-compositions/services/transaction-composition-service/src/main/resources/application-local.yml @@ -23,19 +23,19 @@ backbase: communication: services: usermanager: - direct-uri: http://localhost:8086/user-manager + direct-uri: http://localhost:8060 access-control: - direct-uri: http://localhost:8086/access-control + direct-uri: http://localhost:8040 arrangement: manager: - direct-uri: http://localhost:8082/arrangement-manager + direct-uri: http://localhost:8050 transaction: manager: - direct-uri: http://localhost:8083/transaction-manager + direct-uri: http://localhost:8083 http: client-secret: bb-secret client-id: bb-client - access-token-uri: http://localhost:8181/oidc-token-converter-service/oauth/token + access-token-uri: "http://localhost:7779/oauth/token" activemq: enabled: true stream: @@ -58,7 +58,7 @@ eureka: role: live client: serviceUrl: - defaultZone: http://localhost:8080/registry/eureka/ + defaultZone: http://localhost:8761/eureka/ logging: level: diff --git a/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/AccessControlClientConfig.java b/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/AccessControlClientConfig.java index 1ed9ed7ff..80bfb1644 100644 --- a/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/AccessControlClientConfig.java +++ b/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/AccessControlClientConfig.java @@ -1,18 +1,13 @@ package com.backbase.stream.clients.config; import com.backbase.dbs.accesscontrol.api.service.ApiClient; -import com.backbase.dbs.accesscontrol.api.service.v2.DataGroupApi; -import com.backbase.dbs.accesscontrol.api.service.v2.DataGroupsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.FunctionGroupApi; -import com.backbase.dbs.accesscontrol.api.service.v2.FunctionGroupsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.LegalEntitiesApi; -import com.backbase.dbs.accesscontrol.api.service.v2.LegalEntityApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementQueryApi; -import com.backbase.dbs.accesscontrol.api.service.v2.ServiceAgreementsApi; -import com.backbase.dbs.accesscontrol.api.service.v2.UserContextApi; -import com.backbase.dbs.accesscontrol.api.service.v2.UserQueryApi; -import com.backbase.dbs.accesscontrol.api.service.v2.UsersApi; +import com.backbase.dbs.accesscontrol.api.service.v3.DataGroupsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.FunctionGroupsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.LegalEntitiesApi; +import com.backbase.dbs.accesscontrol.api.service.v3.ServiceAgreementsApi; +import com.backbase.dbs.accesscontrol.api.service.v3.UserContextApi; +import com.backbase.dbs.accesscontrol.api.service.v3.UsersApi; +import com.backbase.dbs.accesscontrol.api.service.v3.PermissionSetApi; import com.fasterxml.jackson.databind.ObjectMapper; import java.text.DateFormat; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -37,54 +32,24 @@ public ApiClient accessControlApiClient(ObjectMapper objectMapper, DateFormat da .setBasePath(createBasePath()); } - @Bean - @ConditionalOnMissingBean - public UserQueryApi userQueryApi(ApiClient accessControlApiClient) { - return new UserQueryApi(accessControlApiClient); - } - @Bean @ConditionalOnMissingBean public UsersApi accessControlUsersApi(ApiClient accessControlApiClient) { return new UsersApi(accessControlApiClient); } - @Bean - @ConditionalOnMissingBean - public DataGroupApi dataGroupApi(ApiClient accessControlApiClient) { - return new DataGroupApi(accessControlApiClient); - } - @Bean @ConditionalOnMissingBean public DataGroupsApi dataGroupsApi(ApiClient accessControlApiClient) { return new DataGroupsApi(accessControlApiClient); } - @Bean - @ConditionalOnMissingBean - public FunctionGroupApi functionGroupApi(ApiClient accessControlApiClient) { - return new FunctionGroupApi(accessControlApiClient); - } - @Bean @ConditionalOnMissingBean public FunctionGroupsApi functionGroupsApi(ApiClient accessControlApiClient) { return new FunctionGroupsApi(accessControlApiClient); } - @Bean - @ConditionalOnMissingBean - public ServiceAgreementQueryApi serviceAgreementQueryApi(ApiClient accessControlApiClient) { - return new ServiceAgreementQueryApi(accessControlApiClient); - } - - @Bean - @ConditionalOnMissingBean - public ServiceAgreementApi serviceAgreementApi(ApiClient accessControlApiClient) { - return new ServiceAgreementApi(accessControlApiClient); - } - @Bean @ConditionalOnMissingBean public ServiceAgreementsApi serviceAgreementsApi(ApiClient accessControlApiClient) { @@ -99,14 +64,14 @@ public LegalEntitiesApi legalEntitiesApi(ApiClient accessControlApiClient) { @Bean @ConditionalOnMissingBean - public LegalEntityApi legalEntityApi(ApiClient accessControlApiClient) { - return new LegalEntityApi(accessControlApiClient); + public UserContextApi userContextApi(ApiClient accessControlApiClient) { + return new UserContextApi(accessControlApiClient); } @Bean @ConditionalOnMissingBean - public UserContextApi userContextApi(ApiClient accessControlApiClient) { - return new UserContextApi(accessControlApiClient); + public PermissionSetApi permissionSetApi(ApiClient accessControlApiClient) { + return new PermissionSetApi(accessControlApiClient); } } diff --git a/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/AudiencesCollectorClientConfig.java b/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/AudiencesCollectorClientConfig.java new file mode 100644 index 000000000..713c1e7fb --- /dev/null +++ b/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/AudiencesCollectorClientConfig.java @@ -0,0 +1,35 @@ +package com.backbase.stream.clients.config; + +import com.backbase.audiences.collector.api.service.ApiClient; +import com.backbase.audiences.collector.api.service.v1.HandlersServiceApi; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.text.DateFormat; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties("backbase.communication.services.user-segments-collector") +public class AudiencesCollectorClientConfig extends CompositeApiClientConfig { + + public static final String USER_SEGMENT_COLLECTOR_SERVICE_ID = "user-segments-collector"; + + public AudiencesCollectorClientConfig() { + super(USER_SEGMENT_COLLECTOR_SERVICE_ID); + } + + @Bean + @ConditionalOnMissingBean + public ApiClient userSegmentCollectorApiClient(ObjectMapper objectMapper, DateFormat dateFormat) { + return new ApiClient(getWebClient(), objectMapper, dateFormat) + .setBasePath(createBasePath()); + } + + @Bean + @ConditionalOnMissingBean + public HandlersServiceApi userSegmentCollectorServiceApi(ApiClient apiClient) { + return new HandlersServiceApi(apiClient); + } + +} diff --git a/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/LoansApiClientConfig.java b/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/LoansApiClientConfig.java new file mode 100644 index 000000000..6d73e080f --- /dev/null +++ b/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/LoansApiClientConfig.java @@ -0,0 +1,35 @@ +package com.backbase.stream.clients.config; + +import com.backbase.loan.inbound.api.service.ApiClient; +import com.backbase.loan.inbound.api.service.v1.LoansApi; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.text.DateFormat; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConfigurationProperties("backbase.communication.services.loan") +public class LoansApiClientConfig extends CompositeApiClientConfig { + + public static final String LOAN_SERVICE_ID = "loan"; + + public LoansApiClientConfig() { + super(LOAN_SERVICE_ID); + } + + @Bean + @ConditionalOnMissingBean + public ApiClient loanApiClient(ObjectMapper objectMapper, DateFormat dateFormat) { + return new ApiClient(getWebClient(), objectMapper, dateFormat) + .setBasePath(createBasePath()); + } + + @Bean + @ConditionalOnMissingBean + public LoansApi loansApi(ApiClient loanApiClient) { + return new LoansApi(loanApiClient); + } + +} diff --git a/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/UserManagerClientConfig.java b/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/UserManagerClientConfig.java index c0d567d18..6fbf1f1aa 100644 --- a/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/UserManagerClientConfig.java +++ b/stream-dbs-clients/src/main/java/com/backbase/stream/clients/config/UserManagerClientConfig.java @@ -3,6 +3,7 @@ import com.backbase.dbs.user.api.service.ApiClient; import com.backbase.dbs.user.api.service.v2.IdentityManagementApi; import com.backbase.dbs.user.api.service.v2.UserManagementApi; +import com.backbase.dbs.user.api.service.v2.UserProfileManagementApi; import com.fasterxml.jackson.databind.ObjectMapper; import java.text.DateFormat; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -39,4 +40,10 @@ public IdentityManagementApi identityManagementApi(ApiClient userManagerClient) return new IdentityManagementApi(userManagerClient); } + @Bean + @ConditionalOnMissingBean + public UserProfileManagementApi userManagerProfileApi(ApiClient userManagerClient) { + return new UserProfileManagementApi(userManagerClient); + } + } diff --git a/stream-dbs-clients/src/main/openapi/user-segments-collector-service-api-v1.0.0.yaml b/stream-dbs-clients/src/main/openapi/user-segments-collector-service-api-v1.0.0.yaml new file mode 100644 index 000000000..93e6ac553 --- /dev/null +++ b/stream-dbs-clients/src/main/openapi/user-segments-collector-service-api-v1.0.0.yaml @@ -0,0 +1,188 @@ +openapi: 3.0.3 +info: + title: User Segments Collector service + description: Perform user segmentation. + version: 1.0.0 + x-icon: category + x-api-dashboard-tags: + - name: Engage +servers: +- url: http://localhost:4010 + description: Prism mock server +tags: +- name: handlers-service +paths: + /service-api/v1/handlers/customer-onboarded: + post: + tags: + - handlers-service + description: Handles customer onboarded request by adding user to a corresponding + Segment. + operationId: customerOnboarded + requestBody: + content: + application/json: + schema: + $ref: '#/components/schemas/customer-onboarded-request' + examples: + customer-onboarded-request-business: + $ref: '#/components/examples/customer-onboarded-request-business' + customer-onboarded-request-retail: + $ref: '#/components/examples/customer-onboarded-request-retail' + required: true + responses: + "202": + description: The customer onboarding request was processed successfully. + "400": + description: If one or more request parameters don't comply with the specification + content: + application/json: + schema: + $ref: '#/components/schemas/error' + example: + $ref: '#/components/examples/lib-bad-request-validation-error' + "401": + description: If the authentication credentials provided are invalid + content: + application/json: + schema: + $ref: '#/components/schemas/error' + example: + $ref: '#/components/examples/lib-unauthorized-error' + "403": + description: If the current user doesn't have sufficient permissions + content: + application/json: + schema: + $ref: '#/components/schemas/error' + example: + $ref: '#/components/examples/lib-forbidden-error' + "500": + description: If a runtime error occurs while processing the request + content: + application/json: + schema: + $ref: '#/components/schemas/simple-error' + example: + $ref: '#/components/examples/lib-internal-server-error' + x-BbAccessControl: false +components: + schemas: + customer-onboarded-request: + title: CustomerOnboardedRequest + required: + - internalUserId + - userKind + type: object + properties: + internalUserId: + type: string + description: internal user id + userKind: + type: string + description: user kind + example: RetailCustomer + enum: + - RetailCustomer + - SME + error: + title: Error + required: + - key + - message + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information + key: + minLength: 1 + type: string + description: Error summary + errors: + type: array + description: Detailed error information + items: + $ref: '#/components/schemas/error-item' + simple-error: + title: SimpleError + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information + key: + minLength: 1 + type: string + description: Error summary + error-item: + title: ErrorItem + required: + - key + - message + type: object + properties: + message: + minLength: 1 + type: string + description: Any further information. + key: + minLength: 1 + type: string + description: "{capability-name}.api.{api-key-name}. For generated validation\ + \ errors this is the path in the document the error resolves to. e.g.\ + \ object name + '.' + field" + context: + title: Context + type: object + additionalProperties: + type: string + description: Context can be anything used to construct localised messages. + examples: + customer-onboarded-request-business: + summary: customer-onboarded-request-business + value: + internalUserId: 00000000-0000-0000-a000-000000000001 + userKind: SME + customer-onboarded-request-retail: + summary: customer-onboarded-request-retail + value: + internalUserId: 00000000-0000-0000-a000-000000000002 + userKind: RetailCustomer + lib-bad-request-validation-error: + summary: lib-bad-request-validation-error + value: + message: Bad Request + key: GENERAL_ERROR + errors: + - message: "Value Exceeded. Must be between {min} and {max}." + key: common.api.shoesize + context: + max: "50" + min: "1" + lib-unauthorized-error: + summary: lib-unauthorized-error + value: + message: Access to requested resource denied. + key: GENERAL_ERROR + errors: + - message: Resource access denied due to invalid credentials. + key: common.api.token + context: + accessToken: expired + lib-forbidden-error: + summary: lib-forbidden-error + value: + message: Access to requested resource denied. + key: GENERAL_ERROR + errors: + - message: "Resource access denied due to an insufficient user quota of {quota}." + key: common.api.quota + context: + quota: someQuota + lib-internal-server-error: + summary: lib-internal-server-error + value: + message: Description of error diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/java/com/backbase/stream/SetupLegalEntityHierarchyTaskApplicationIT.java b/stream-legal-entity/legal-entity-bootstrap-task/src/test/java/com/backbase/stream/SetupLegalEntityHierarchyTaskApplicationIT.java index 92185682a..19aaef067 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/java/com/backbase/stream/SetupLegalEntityHierarchyTaskApplicationIT.java +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/java/com/backbase/stream/SetupLegalEntityHierarchyTaskApplicationIT.java @@ -4,6 +4,7 @@ import com.backbase.stream.config.BootstrapConfigurationProperties; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.beans.factory.annotation.Autowired; @@ -15,7 +16,7 @@ @SpringBootTest @ActiveProfiles({"it", "moustache-bank", "moustache-bank-subsidiaries"}) -public class SetupLegalEntityHierarchyTaskApplicationIT { +class SetupLegalEntityHierarchyTaskApplicationIT { @Autowired BootstrapConfigurationProperties configuration; @@ -39,6 +40,9 @@ static void registerDynamicProperties(DynamicPropertyRegistry registry) { registry.add("spring.cloud.discovery.client.simple.instances.arrangement-manager[0].uri", () -> wiremockUrl); registry.add("spring.cloud.discovery.client.simple.instances.arrangement-manager[0].metadata.contextPath", () -> "/arrangement-manager"); + registry.add("spring.cloud.discovery.client.simple.instances.loan[0].uri", () -> wiremockUrl); + registry.add("spring.cloud.discovery.client.simple.instances.loan[0].metadata.contextPath", + () -> "/loan"); } @Test diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-data-groups.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-data-groups.json index 04d4bc8c0..dba6051b7 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-data-groups.json +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-data-groups.json @@ -1,7 +1,7 @@ { "request": { "method": "POST", - "url": "/access-control/service-api/v2/accessgroups/data-groups", + "url": "/access-control/service-api/v3/accesscontrol/data-groups", "headers": { "X-B3-TraceId": { "matches": "[\\w-]+" diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-function-groups.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-function-groups.json index 6a1b20d31..52cf86982 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-function-groups.json +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-function-groups.json @@ -1,7 +1,7 @@ { "request": { "method": "POST", - "url": "/access-control/service-api/v2/accessgroups/function-groups/ingest", + "url": "/access-control/service-api/v3/accesscontrol/function-groups/ingest", "headers": { "X-B3-TraceId": { "matches": "[\\w-]+" diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-legal-entity.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-legal-entity.json index e0ac42ea8..4c8d8ea81 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-legal-entity.json +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-legal-entity.json @@ -1,7 +1,7 @@ { "request": { "method": "POST", - "url": "/access-control/service-api/v2/legalentities/create", + "url": "/access-control/service-api/v3/accesscontrol/legal-entities/create", "headers": { "X-B3-TraceId": { "matches": "[\\w-]+" diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-service-agreement.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-service-agreement.json index 2d252c6d5..c92fd144e 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-service-agreement.json +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/create-service-agreement.json @@ -1,7 +1,7 @@ { "request": { "method": "POST", - "url": "/access-control/service-api/v2/accessgroups/serviceagreements/ingest/serviceagreements", + "url": "/access-control/service-api/v3/accessgroups/serviceagreements/ingest/serviceagreements", "headers": { "X-B3-TraceId": { "matches": "[\\w-]+" diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-data-groups.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-data-groups.json index 3fd24a0b8..be549ca3d 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-data-groups.json +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-data-groups.json @@ -1,7 +1,7 @@ { "request": { "method": "GET", - "urlPath": "/access-control/service-api/v2/accesscontrol/accessgroups/data-groups", + "urlPath": "/access-control/service-api/v3/accesscontrol/data-groups", "headers": { "X-B3-TraceId": { "matches": "[\\w-]+" diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-function-groups.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-function-groups.json index 14695b9fa..faba224c4 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-function-groups.json +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-function-groups.json @@ -1,7 +1,7 @@ { "request": { "method": "GET", - "urlPath": "/access-control/service-api/v2/accesscontrol/accessgroups/function-groups", + "urlPath": "/access-control/service-api/v3/accesscontrol/function-groups", "headers": { "X-B3-TraceId": { "matches": "[\\w-]+" diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-identity.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-identity.json new file mode 100644 index 000000000..8503f1085 --- /dev/null +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-identity.json @@ -0,0 +1,27 @@ +{ + "request": { + "method": "GET", + "urlPathPattern": "/user-manager/service-api/v2/users/identities/([\\w-]+)", + "headers": { + "X-B3-TraceId": { + "matches": "[\\w-]+" + }, + "X-B3-SpanId": { + "matches": "[\\w-]+" + }, + "X-TID": { + "contains": "tenant1" + } + } + }, + "response": { + "status": 200, + "jsonBody": { + "internalId": "internal-id", + "externalId": "external-id" + }, + "headers": { + "Content-Type": "application/json" + } + } +} diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-legal-entity-external.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-legal-entity-external.json index 88a6bb155..da3db7de7 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-legal-entity-external.json +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-legal-entity-external.json @@ -1,7 +1,7 @@ { "request": { "method": "GET", - "urlPathPattern": "/access-control/service-api/v2/legalentities/external/([\\w-]+)", + "urlPathPattern": "/access-control/service-api/v3/accesscontrol/legal-entities/external/([\\w-]+)", "headers": { "X-B3-TraceId": { "matches": "[\\w-]+" diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-legal-entity.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-legal-entity.json index fd6141a36..7b1103269 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-legal-entity.json +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-legal-entity.json @@ -1,7 +1,7 @@ { "request": { "method": "GET", - "urlPathPattern": "/access-control/service-api/v2/legalentities/([\\w-]+)", + "urlPathPattern": "/access-control/service-api/v3/accesscontrol/legal-entities/([\\w-]+)", "headers": { "X-B3-TraceId": { "matches": "[\\w-]+" diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-service-agreement-permissions.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-service-agreement-permissions.json index a4081c3af..35e89d8c5 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-service-agreement-permissions.json +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-service-agreement-permissions.json @@ -1,7 +1,7 @@ { "request": { "method": "GET", - "urlPathPattern": "/access-control/service-api/v2/accesscontrol/accessgroups/users/([\\w-]+)/service-agreements/([\\w-]+)/permissions", + "urlPathPattern": "/access-control/service-api/v3/accessgroups/users/([\\w-]+)/service-agreements/([\\w-]+)/permissions", "headers": { "X-B3-TraceId": { "matches": "[\\w-]+" diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-service-agreement.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-service-agreement.json index e519ff5a4..5c211b530 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-service-agreement.json +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/get-service-agreement.json @@ -1,7 +1,7 @@ { "request": { "method": "GET", - "urlPathPattern": "/access-control/service-api/v2/accesscontrol/legalentities/([\\w-]+)/serviceagreements/master", + "urlPathPattern": "/access-control/service-api/v3/accesscontrol/legal-entities/([\\w-]+)/service-agreements/master", "headers": { "X-B3-TraceId": { "matches": "[\\w-]+" diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/put-identity.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/put-identity.json new file mode 100644 index 000000000..b5c301dfc --- /dev/null +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/put-identity.json @@ -0,0 +1,26 @@ +{ + "request": { + "method": "PUT", + "urlPathPattern": "/user-manager/service-api/v2/users/identities/([\\w-]+)", + "headers": { + "X-B3-TraceId": { + "matches": "[\\w-]+" + }, + "X-B3-SpanId": { + "matches": "[\\w-]+" + }, + "X-TID": { + "contains": "tenant1" + } + } + }, + "response": { + "status": 200, + "jsonBody": { + + }, + "headers": { + "Content-Type": "application/json" + } + } +} diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/update-function-groups.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/update-function-groups.json index cf2edcdaa..4ab0c11f3 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/update-function-groups.json +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/update-function-groups.json @@ -1,7 +1,7 @@ { "request": { "method": "PUT", - "url": "/access-control/service-api/v2/accessgroups/function-groups/batch/update", + "url": "/access-control/service-api/v3/accesscontrol/function-groups/batch/update", "headers": { "X-B3-TraceId": { "matches": "[\\w-]+" diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/update-legal-entity.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/update-legal-entity.json index 944f0fa2f..40c46f215 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/update-legal-entity.json +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/update-legal-entity.json @@ -1,7 +1,7 @@ { "request": { "method": "PUT", - "url": "/access-control/service-api/v2/legalentities", + "url": "/access-control/service-api/v3/accesscontrol/legal-entities", "headers": { "X-B3-TraceId": { "matches": "[\\w-]+" diff --git a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/update-user-permissions.json b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/update-user-permissions.json index 4ec780b9b..d4f7f0a6a 100644 --- a/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/update-user-permissions.json +++ b/stream-legal-entity/legal-entity-bootstrap-task/src/test/resources/mappings/update-user-permissions.json @@ -1,7 +1,7 @@ { "request": { "method": "PUT", - "url": "/access-control/service-api/v2/accessgroups/users/permissions/user-permissions", + "url": "/access-control/service-api/v3/accessgroups/users/permissions/user-permissions", "headers": { "X-B3-TraceId": { "matches": "[\\w-]+" diff --git a/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/LegalEntitySaga.java b/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/LegalEntitySaga.java index 0fd623032..7154f6448 100644 --- a/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/LegalEntitySaga.java +++ b/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/LegalEntitySaga.java @@ -1,15 +1,9 @@ package com.backbase.stream; -import static com.backbase.stream.product.utils.StreamUtils.nullableCollectionToStream; -import static com.backbase.stream.service.UserService.REMOVED_PREFIX; -import static java.util.Objects.isNull; -import static java.util.Objects.nonNull; -import static java.util.Objects.requireNonNull; -import static java.util.Optional.ofNullable; -import static org.springframework.util.CollectionUtils.isEmpty; - -import com.backbase.dbs.accesscontrol.api.service.v2.model.FunctionGroupItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServiceAgreementParticipantsGetResponseBody; +import com.backbase.audiences.collector.api.service.v1.model.CustomerOnboardedRequest; +import com.backbase.audiences.collector.api.service.v1.model.CustomerOnboardedRequest.UserKindEnum; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServiceAgreementParticipantsGetResponseBody; import com.backbase.dbs.contact.api.service.v2.model.AccessContextScope; import com.backbase.dbs.contact.api.service.v2.model.ContactsBulkPostRequestBody; import com.backbase.dbs.contact.api.service.v2.model.ExternalAccessContext; @@ -21,31 +15,14 @@ import com.backbase.dbs.user.api.service.v2.model.GetUser; import com.backbase.dbs.user.api.service.v2.model.GetUsersList; import com.backbase.dbs.user.profile.api.service.v2.model.CreateUserProfile; +import com.backbase.stream.audiences.UserKindSegmentationSaga; +import com.backbase.stream.audiences.UserKindSegmentationTask; import com.backbase.stream.configuration.LegalEntitySagaConfigurationProperties; import com.backbase.stream.contact.ContactsSaga; import com.backbase.stream.contact.ContactsTask; import com.backbase.stream.exceptions.AccessGroupException; import com.backbase.stream.exceptions.LegalEntityException; -import com.backbase.stream.legalentity.model.BaseProduct; -import com.backbase.stream.legalentity.model.BaseProductGroup; -import com.backbase.stream.legalentity.model.BatchProductGroup; -import com.backbase.stream.legalentity.model.BusinessFunction; -import com.backbase.stream.legalentity.model.BusinessFunctionGroup; -import com.backbase.stream.legalentity.model.ExternalContact; -import com.backbase.stream.legalentity.model.IdentityUserLinkStrategy; -import com.backbase.stream.legalentity.model.JobProfileUser; -import com.backbase.stream.legalentity.model.JobRole; -import com.backbase.stream.legalentity.model.LegalEntity; -import com.backbase.stream.legalentity.model.LegalEntityParticipant; -import com.backbase.stream.legalentity.model.LegalEntityReference; -import com.backbase.stream.legalentity.model.LegalEntityStatus; -import com.backbase.stream.legalentity.model.Limit; -import com.backbase.stream.legalentity.model.Privilege; -import com.backbase.stream.legalentity.model.ProductGroup; -import com.backbase.stream.legalentity.model.ServiceAgreement; -import com.backbase.stream.legalentity.model.ServiceAgreementUserAction; -import com.backbase.stream.legalentity.model.User; -import com.backbase.stream.legalentity.model.UserProfile; +import com.backbase.stream.legalentity.model.*; import com.backbase.stream.limit.LimitsSaga; import com.backbase.stream.limit.LimitsTask; import com.backbase.stream.mapper.ExternalContactMapper; @@ -64,21 +41,6 @@ import com.backbase.stream.worker.StreamTaskExecutor; import com.backbase.stream.worker.exception.StreamTaskException; import com.backbase.stream.worker.model.StreamTask; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Function; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import javax.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.mapstruct.factory.Mappers; @@ -90,6 +52,19 @@ import reactor.core.publisher.Mono; import reactor.util.function.Tuples; +import javax.validation.Valid; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.backbase.stream.product.utils.StreamUtils.nullableCollectionToStream; +import static com.backbase.stream.service.UserService.REMOVED_PREFIX; +import static java.util.Objects.*; +import static java.util.Optional.ofNullable; +import static org.springframework.util.CollectionUtils.isEmpty; + /** * Legal Entity Saga. This Service creates Legal Entities and their supporting objects from a {@link LegalEntity} * aggregate object. For each Legal Entity object it will either retrieve the existing Legal Entity or create a new one. @@ -141,6 +116,8 @@ public class LegalEntitySaga implements StreamTaskExecutor { private final LimitsSaga limitsSaga; private final ContactsSaga contactsSaga; private final LegalEntitySagaConfigurationProperties legalEntitySagaConfigurationProperties; + private final UserKindSegmentationSaga userKindSegmentationSaga; + private static final ExternalContactMapper externalContactMapper = ExternalContactMapper.INSTANCE; public LegalEntitySaga(LegalEntityService legalEntityService, @@ -148,9 +125,11 @@ public LegalEntitySaga(LegalEntityService legalEntityService, UserProfileService userProfileService, AccessGroupService accessGroupService, ProductIngestionSaga productIngestionSaga, - BatchProductIngestionSaga batchProductIngestionSaga, LimitsSaga limitsSaga, - ContactsSaga contactsSaga, - LegalEntitySagaConfigurationProperties legalEntitySagaConfigurationProperties) { + BatchProductIngestionSaga batchProductIngestionSaga, + LimitsSaga limitsSaga, + ContactsSaga contactsSaga, + LegalEntitySagaConfigurationProperties legalEntitySagaConfigurationProperties, + UserKindSegmentationSaga userKindSegmentationSaga) { this.legalEntityService = legalEntityService; this.userService = userService; this.userProfileService = userProfileService; @@ -159,6 +138,7 @@ public LegalEntitySaga(LegalEntityService legalEntityService, this.limitsSaga = limitsSaga; this.contactsSaga = contactsSaga; this.legalEntitySagaConfigurationProperties = legalEntitySagaConfigurationProperties; + this.userKindSegmentationSaga = userKindSegmentationSaga; } @Override @@ -174,9 +154,70 @@ public Mono executeTask(@SpanTag(value = "streamTask") LegalEnt .flatMap(this::setupLimits) .flatMap(this::processProducts) .flatMap(this::postContacts) + .flatMap(this::processAudiencesSegmentation) .flatMap(this::processSubsidiaries); } + private Mono processAudiencesSegmentation(LegalEntityTask streamTask) { + if (!userKindSegmentationSaga.isEnabled()) { + log.info("Skipping audiences UserKind segmentation - feature is disabled."); + return Mono.just(streamTask); + } + + var le = streamTask.getData(); + + if (le.getLegalEntityType() != LegalEntityType.CUSTOMER) { + return Mono.just(streamTask); + } + + var customerCategory = le.getCustomerCategory(); + if (customerCategory == null) { + var defaultCategory = userKindSegmentationSaga.getDefaultCustomerCategory(); + if (defaultCategory == null) { + return Mono.error(new StreamTaskException(streamTask, + "Failed to determine LE customerCategory for UserKindSegmentationSage.")); + } + customerCategory = CustomerCategory.fromValue(defaultCategory); + } + + var userKind = customerCategoryToUserKind(customerCategory); + + if (userKind == null) { + log.info("Skipping audiences UserKind segmentation - customerCategory " + customerCategory + + " is not supported"); + return Mono.just(streamTask); + } + + log.info("Ingesting customers of LE into UserKind segment customerCategory: " + customerCategory); + + return Flux.fromStream(StreamUtils.nullableCollectionToStream(le.getUsers())) + .map(user -> { + var task = new UserKindSegmentationTask(); + task.setCustomerOnboardedRequest( + new CustomerOnboardedRequest() + .internalUserId(user.getUser().getInternalId()) + .userKind(userKind) + ); + return task; + }) + .flatMap(userKindSegmentationSaga::executeTask) + .then(Mono.just(streamTask)); + } + + private UserKindEnum customerCategoryToUserKind(CustomerCategory customerCategory) { + switch (customerCategory) { + case RETAIL -> { + return UserKindEnum.RETAILCUSTOMER; + } + case BUSINESS -> { + return UserKindEnum.SME; + } + default -> { + return null; + } + } + } + private Mono postContacts(LegalEntityTask streamTask) { return Mono.just(streamTask) .flatMap(this::postLegalEntityContacts) @@ -358,14 +399,6 @@ private Mono upsertLegalEntity(LegalEntityTask task) { LegalEntity legalEntity = task.getData(); // Pipeline for Existing Legal Entity Mono existingLegalEntity = legalEntityService.getLegalEntityByExternalId(legalEntity.getExternalId()) - .onErrorResume(throwable -> { - task.error(LEGAL_ENTITY, UPSERT_LEGAL_ENTITY, FAILED, legalEntity.getExternalId(), null, throwable, throwable.getMessage(), "Unexpected Error"); - return Mono.error(new StreamTaskException(task, throwable, "Failed to get Legal Entity: " + throwable.getMessage())); - }) - .onErrorResume(WebClientResponseException.class, throwable -> { - task.error(LEGAL_ENTITY, UPSERT_LEGAL_ENTITY, FAILED, legalEntity.getExternalId(), null, throwable, throwable.getResponseBodyAsString(), "Unexpected Web Client Exception"); - return Mono.error(new StreamTaskException(task, throwable, "Failed to get Legal Entity: " + throwable.getMessage())); - }) .flatMap(actual -> { task.getData().setInternalId(actual.getInternalId()); return legalEntityService.getLegalEntityByInternalId(actual.getInternalId()) @@ -378,22 +411,37 @@ private Mono upsertLegalEntity(LegalEntityTask task) { return Mono.just(task); }); }); + }) + .onErrorResume(throwable -> { + if (throwable instanceof WebClientResponseException webClientResponseException) { + task.error(LEGAL_ENTITY, UPSERT_LEGAL_ENTITY, FAILED, legalEntity.getExternalId(), null, + webClientResponseException, + webClientResponseException.getResponseBodyAsString(), "Unexpected Web Client Exception"); + } else { + task.error(LEGAL_ENTITY, UPSERT_LEGAL_ENTITY, FAILED, legalEntity.getExternalId(), null, throwable, + throwable.getMessage(), "Unexpected Error"); + } + return Mono.error( + new StreamTaskException(task, throwable, "Failed to get Legal Entity: " + throwable.getMessage())); }); // Pipeline for Creating New Legal Entity - Mono createNewLegalEntity = legalEntityService.createLegalEntity(legalEntity) + Mono createNewLegalEntity = Mono.defer(() -> legalEntityService.createLegalEntity(legalEntity) .flatMap(actual -> { task.getData().setInternalId(legalEntity.getInternalId()); return legalEntityService.getLegalEntityByInternalId(actual.getInternalId()) .flatMap(result -> { task.getData().setParentInternalId(result.getParentInternalId()); - task.info(LEGAL_ENTITY, UPSERT_LEGAL_ENTITY, CREATED, legalEntity.getExternalId(), legalEntity.getInternalId(), "Created new Legal Entity"); + task.info(LEGAL_ENTITY, UPSERT_LEGAL_ENTITY, CREATED, legalEntity.getExternalId(), + legalEntity.getInternalId(), "Created new Legal Entity"); return Mono.just(task); }); }) .onErrorResume(LegalEntityException.class, legalEntityException -> { - task.error(LEGAL_ENTITY, UPSERT_LEGAL_ENTITY, FAILED, legalEntity.getExternalId(), legalEntity.getInternalId(), legalEntityException, legalEntityException.getHttpResponse(), legalEntityException.getMessage()); + task.error(LEGAL_ENTITY, UPSERT_LEGAL_ENTITY, FAILED, legalEntity.getExternalId(), + legalEntity.getInternalId(), legalEntityException, legalEntityException.getHttpResponse(), + legalEntityException.getMessage()); return Mono.error(new StreamTaskException(task, legalEntityException)); - }); + })); return existingLegalEntity.switchIfEmpty(createNewLegalEntity); } @@ -575,11 +623,14 @@ private Mono setupUsers(LegalEntityTask streamTask) { .findFirst().get().getUser(); inputUser.setInternalId(upsertedUser.getInternalId()); return upsertUserProfile(inputUser) - .map(userProfile -> { + .flatMap(userProfile -> { log.info("User Profile upserted for: {}", userProfile.getUserName()); inputUser.setUserProfile(userProfile); - return userProfile; - }); + return userService.getUserProfile(inputUser.getInternalId()); + }) + .doOnNext(userCacheProfile -> + log.info("User Cache Profile is existed: {}", userCacheProfile.getFullName()) + ); })) .collectList() .thenReturn(streamTask); @@ -641,9 +692,8 @@ public Mono setupUserPermissions(LegalEntityTask legalEntityTas log.trace("Permissions {}", request); return accessGroupService.assignPermissionsBatch( new BatchProductGroupTask(BATCH_PRODUCT_GROUP_ID + System.currentTimeMillis(), new BatchProductGroup() - .serviceAgreement(retrieveServiceAgreement(legalEntity)), BatchProductIngestionMode.UPSERT), request) + .serviceAgreement(retrieveServiceAgreement(legalEntity)), legalEntityTask.getIngestionMode()), request) .thenReturn(legalEntityTask); - } public Mono setupAdministratorPermissions(LegalEntityTask legalEntityTask) { @@ -755,17 +805,16 @@ private Mono upsertIdentityUser(LegalEntityTask streamTask, User user) { streamTask.info(IDENTITY_USER, UPSERT, EXISTS, user.getExternalId(), user.getInternalId(), "User %s already exists", existingUser.getExternalId()); return user; }) - ; + .flatMap(userService::updateIdentity); + Mono createNewIdentityUser = - userService.setupRealm(legalEntity) - .switchIfEmpty(Mono.error(new StreamTaskException(streamTask, "Realm: " + legalEntity.getRealmName() + " not found!"))) - .then(userService.createOrImportIdentityUser(user, legalEntity.getInternalId(), streamTask) + userService.createOrImportIdentityUser(user, legalEntity.getInternalId(), streamTask) .flatMap(currentUser -> userService.updateUserState(currentUser, legalEntity.getRealmName())) .map(existingUser -> { user.setInternalId(existingUser.getInternalId()); streamTask.info(IDENTITY_USER, UPSERT, CREATED, user.getExternalId(), user.getInternalId(), "User %s created", existingUser.getExternalId()); return user; - })); + }); return getExistingIdentityUser.switchIfEmpty(createNewIdentityUser); } @@ -945,6 +994,7 @@ private Flux setSubsidiaryParentLegalEntityId(LegalEntity parentLeg private Mono setupLimits(LegalEntityTask streamTask) { return Mono.just(streamTask) .flatMap(this::setupLegalEntityLimits) + .flatMap(this::setupLegalEntityLevelBusinessFunctionLimits) .flatMap(this::setupServiceAgreementLimits) .flatMap(this::setupServiceAgreementParticipantLimits) .flatMap(this::retrieveUsersInternalIds) @@ -953,7 +1003,7 @@ private Mono setupLimits(LegalEntityTask streamTask) { private Mono setupLegalEntityLimits(LegalEntityTask streamTask) { LegalEntity legalEntity = streamTask.getData(); - if(isNull(legalEntity.getLimit())) { + if (isNull(legalEntity.getLimit()) || !validateLimit(legalEntity.getLimit())) { streamTask.info(LEGAL_ENTITY, PROCESS_LIMITS, FAILED, legalEntity.getInternalId(), legalEntity.getExternalId(), "Legal Entity: %s does not have any Legal Entity limits defined", legalEntity.getExternalId()); return Mono.just(streamTask); } @@ -963,18 +1013,78 @@ private Mono setupLegalEntityLimits(LegalEntityTask streamTask) .then(Mono.just(streamTask)); } + private boolean validateLimit(Limit limit) { + + return nonNull(limit) && + (nonNull(limit.getTransactional()) || + nonNull(limit.getDaily()) || + nonNull(limit.getWeekly()) || + nonNull(limit.getMonthly()) || + nonNull(limit.getQuarterly()) || + nonNull(limit.getYearly())); + } + + private Mono setupLegalEntityLevelBusinessFunctionLimits(LegalEntityTask streamTask) { + LegalEntity legalEntity = streamTask.getData(); + if (isNull(legalEntity.getLimit()) || isEmpty(legalEntity.getLimit().getBusinessFunctionLimits())) { + streamTask.info(LEGAL_ENTITY, PROCESS_LIMITS, FAILED, legalEntity.getInternalId(), legalEntity.getExternalId(), "Legal Entity: %s does not have any Legal Entity limits defined", legalEntity.getExternalId()); + return Mono.just(streamTask); + } + log.info("Ingesting Limit... {}", legalEntity); + return Flux.fromStream(legalEntity.getLimit().getBusinessFunctionLimits() + .stream() + .filter(businessFunctionLimit -> nonNull(businessFunctionLimit) + && nonNull(businessFunctionLimit.getPrivilege()) + && validateLimit(businessFunctionLimit.getPrivilege().getLimit()) + ).map(businessFunctionLimit -> createLimitsTask(streamTask, legalEntity.getInternalId(), businessFunctionLimit))) + .concatMap(limitsSaga::executeTask) + .map(limitsTask -> streamTask.addHistory(limitsTask.getHistory())) + .collectList() + .map(tasks -> { + boolean failed = tasks.stream().anyMatch(StreamTask::isFailed); + if (failed) { + streamTask.setState(StreamTask.State.FAILED); + } else { + streamTask.setState(StreamTask.State.COMPLETED); + } + return streamTask; + }); + } + private Mono setupServiceAgreementLimits(LegalEntityTask streamTask) { LegalEntity legalEntity = streamTask.getData(); ServiceAgreement serviceAgreement = retrieveServiceAgreement(legalEntity); - if(isNull(serviceAgreement.getLimit())) { + if (isNull(serviceAgreement.getLimit())) { streamTask.info(LEGAL_ENTITY, PROCESS_LIMITS, FAILED, legalEntity.getInternalId(), legalEntity.getExternalId(), "Legal Entity: %s does not have any Service Agreement limits defined", legalEntity.getExternalId()); return Mono.just(streamTask); } - return limitsSaga.executeTask(createLimitsTask(streamTask, serviceAgreement, null, serviceAgreement.getLimit())) + return limitsSaga.executeTask(createLimitsTask(streamTask, serviceAgreement, null, serviceAgreement.getLimit())) .flatMap(limitsTask -> requireNonNull(Mono.just(streamTask))) .then(Mono.just(streamTask)); } + private LimitsTask createLimitsTask(LegalEntityTask streamTask, String legalEntityId, BusinessFunctionLimit businessFunctionLimit) { + + log.info("Legal entity business function limit : {}", businessFunctionLimit); + var limitData = new CreateLimitRequestBody(); + var entities = new ArrayList(); + ofNullable(legalEntityId).ifPresent(le -> entities.add(new Entity().etype(LEGAL_ENTITY_E_TYPE).eref(le))); + ofNullable(businessFunctionLimit.getFunctionId()) + .ifPresent(functionId -> entities.add(new Entity().etype(FUNCTION_E_TYPE).eref(functionId))); + ofNullable(businessFunctionLimit.getPrivilege()) + .map(Privilege::getPrivilege) + .ifPresent(privilege -> entities.add(new Entity().etype(PRIVILEGE_E_TYPE).eref(privilege))); + limitData.entities(entities); + ofNullable(businessFunctionLimit.getPrivilege()) + .map(Privilege::getLimit).ifPresent(limit -> { + limitData.periodicLimitsBounds(periodicLimits(limit)) + .transactionalLimitsBound(transactionalLimits(limit)) + .shadow(businessFunctionLimit.getShadow()) + .currency(limit.getCurrencyCode()); + }); + return new LimitsTask(streamTask.getId() + "-" + LEGAL_ENTITY_LIMITS, limitData); + } + private LimitsTask createLimitsTask(LegalEntityTask streamTask, ServiceAgreement serviceAgreement, String legalEntityId, Limit limit) { var limitData = new CreateLimitRequestBody(); diff --git a/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/LegalEntityTask.java b/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/LegalEntityTask.java index 0a140ff81..e1584a332 100644 --- a/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/LegalEntityTask.java +++ b/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/LegalEntityTask.java @@ -1,6 +1,7 @@ package com.backbase.stream; import com.backbase.stream.legalentity.model.LegalEntity; +import com.backbase.stream.product.task.BatchProductIngestionMode; import com.backbase.stream.worker.model.StreamTask; import lombok.Data; import lombok.EqualsAndHashCode; @@ -12,11 +13,18 @@ public class LegalEntityTask extends StreamTask { private LegalEntity legalEntity; + private BatchProductIngestionMode ingestionMode; public LegalEntityTask(LegalEntity data) { super(data.getExternalId()); this.legalEntity = data; + this.ingestionMode = BatchProductIngestionMode.UPSERT; + } + public LegalEntityTask(LegalEntity data, BatchProductIngestionMode ingestionMode) { + super(data.getExternalId()); + this.legalEntity = data; + this.ingestionMode = ingestionMode; } public LegalEntity getData() { diff --git a/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/configuration/LegalEntitySagaConfiguration.java b/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/configuration/LegalEntitySagaConfiguration.java index e68aa8fbf..8ca3b4af4 100644 --- a/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/configuration/LegalEntitySagaConfiguration.java +++ b/stream-legal-entity/legal-entity-core/src/main/java/com/backbase/stream/configuration/LegalEntitySagaConfiguration.java @@ -3,6 +3,7 @@ import com.backbase.stream.LegalEntitySaga; import com.backbase.stream.LegalEntityTask; import com.backbase.stream.LegalEntityUnitOfWorkExecutor; +import com.backbase.stream.audiences.UserKindSegmentationSaga; import com.backbase.stream.contact.ContactsSaga; import com.backbase.stream.legalentity.repository.LegalEntityUnitOfWorkRepository; import com.backbase.stream.limit.LimitsSaga; @@ -28,7 +29,9 @@ AccessControlConfiguration.class, ProductIngestionSagaConfiguration.class, LimitsServiceConfiguration.class, - ContactsServiceConfiguration.class + ContactsServiceConfiguration.class, + LoansServiceConfiguration.class, + AudiencesSegmentationConfiguration.class }) @EnableConfigurationProperties( {LegalEntitySagaConfigurationProperties.class} @@ -45,16 +48,20 @@ public LegalEntitySaga reactiveLegalEntitySaga(LegalEntityService legalEntitySer LimitsSaga limitsSaga, ContactsSaga contactsSaga, LegalEntitySagaConfigurationProperties sinkConfigurationProperties, - ObjectMapper objectMapper) { + ObjectMapper objectMapper, + UserKindSegmentationSaga userKindSegmentationSaga + ) { return new LegalEntitySaga( legalEntityService, userService, userProfileService, accessGroupService, productIngestionSaga, - batchProductIngestionSaga, limitsSaga, - contactsSaga, - sinkConfigurationProperties + batchProductIngestionSaga, + limitsSaga, + contactsSaga, + sinkConfigurationProperties, + userKindSegmentationSaga ); } diff --git a/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/LegalEntitySagaTest.java b/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/LegalEntitySagaTest.java index 8eff280c0..aa9d471f3 100644 --- a/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/LegalEntitySagaTest.java +++ b/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/LegalEntitySagaTest.java @@ -1,6 +1,16 @@ package com.backbase.stream; -import com.backbase.dbs.accesscontrol.api.service.v2.model.ServiceAgreementParticipantsGetResponseBody; +import static com.backbase.stream.service.UserService.REMOVED_PREFIX; +import static java.util.Collections.singletonList; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.backbase.dbs.accesscontrol.api.service.v3.model.ServiceAgreementParticipantsGetResponseBody; import com.backbase.dbs.contact.api.service.v2.model.AccessContextScope; import com.backbase.dbs.contact.api.service.v2.model.ContactsBulkPostRequestBody; import com.backbase.dbs.contact.api.service.v2.model.ContactsBulkPostResponseBody; @@ -10,6 +20,9 @@ import com.backbase.dbs.user.api.service.v2.model.GetUser; import com.backbase.dbs.user.api.service.v2.model.GetUsersList; import com.backbase.dbs.user.api.service.v2.model.Realm; +import com.backbase.dbs.user.profile.api.service.v2.model.GetUserProfile; +import com.backbase.stream.audiences.UserKindSegmentationSaga; +import com.backbase.stream.audiences.UserKindSegmentationTask; import com.backbase.stream.configuration.LegalEntitySagaConfigurationProperties; import com.backbase.stream.contact.ContactsSaga; import com.backbase.stream.contact.ContactsTask; @@ -17,6 +30,7 @@ import com.backbase.stream.legalentity.model.BusinessFunction; import com.backbase.stream.legalentity.model.BusinessFunctionGroup; import com.backbase.stream.legalentity.model.CurrentAccount; +import com.backbase.stream.legalentity.model.CustomerCategory; import com.backbase.stream.legalentity.model.EmailAddress; import com.backbase.stream.legalentity.model.ExternalAccountInformation; import com.backbase.stream.legalentity.model.ExternalContact; @@ -28,6 +42,8 @@ import com.backbase.stream.legalentity.model.LegalEntityStatus; import com.backbase.stream.legalentity.model.LegalEntityType; import com.backbase.stream.legalentity.model.Limit; +import com.backbase.stream.legalentity.model.Loan; +import com.backbase.stream.legalentity.model.Multivalued; import com.backbase.stream.legalentity.model.PhoneNumber; import com.backbase.stream.legalentity.model.Privilege; import com.backbase.stream.legalentity.model.ProductGroup; @@ -35,6 +51,7 @@ import com.backbase.stream.legalentity.model.SavingsAccount; import com.backbase.stream.legalentity.model.ServiceAgreement; import com.backbase.stream.legalentity.model.User; +import com.backbase.stream.legalentity.model.UserProfile; import com.backbase.stream.limit.LimitsSaga; import com.backbase.stream.limit.LimitsTask; import com.backbase.stream.product.BatchProductIngestionSaga; @@ -44,36 +61,33 @@ import com.backbase.stream.service.LegalEntityService; import com.backbase.stream.service.UserProfileService; import com.backbase.stream.service.UserService; +import com.backbase.stream.worker.exception.StreamTaskException; +import java.math.BigDecimal; +import java.nio.charset.Charset; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Stream; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.stubbing.Answer; +import org.springframework.web.reactive.function.client.WebClientResponseException; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.math.BigDecimal; -import java.time.Duration; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; - -import static com.backbase.stream.service.UserService.REMOVED_PREFIX; -import static java.util.Collections.singletonList; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - @ExtendWith(MockitoExtension.class) class LegalEntitySagaTest { @@ -101,6 +115,9 @@ class LegalEntitySagaTest { @Mock private ContactsSaga contactsSaga; + @Mock + private UserKindSegmentationSaga userKindSegmentationSaga; + @Spy private final LegalEntitySagaConfigurationProperties legalEntitySagaConfigurationProperties = getLegalEntitySagaConfigurationProperties(); @@ -379,6 +396,7 @@ void productGroupsProcessedSequentially() { .name("Account 2") .currency("GBP") )) + .loans(singletonList(new Loan().IBAN("IBAN123321"))) )) .users(singletonList( new JobProfileUser() @@ -420,8 +438,6 @@ void productGroupsProcessedSequentially() { .thenReturn(Mono.just(legalEntityTask.getLegalEntity())); when(legalEntityService.getLegalEntityByInternalId("100001")) .thenReturn(Mono.just(legalEntityTask.getLegalEntity())); - when(legalEntityService.createLegalEntity(legalEntityTask.getLegalEntity())) - .thenReturn(Mono.empty()); when(legalEntityService.putLegalEntity(any())).thenReturn(Mono.just(legalEntityTask.getLegalEntity())); when(legalEntitySagaConfigurationProperties.isUseIdentityIntegration()) .thenReturn(true); @@ -429,6 +445,8 @@ void productGroupsProcessedSequentially() { .thenReturn(Mono.empty()); when(userService.linkLegalEntityToRealm(legalEntityTask.getLegalEntity())) .thenReturn(Mono.empty()); + when(userService.updateIdentity(any())) + .thenReturn(Mono.empty()); when(userService.getUserByExternalId("john.doe")) .thenReturn(Mono.just(new User().internalId("100").externalId("john.doe"))); when(userService.createOrImportIdentityUser(any(), any(), any())) @@ -463,9 +481,10 @@ void productGroupsProcessedSequentially() { }); }); + // When legalEntitySaga.executeTask(legalEntityTask) - .block(Duration.ofSeconds(10)); + .block(Duration.ofSeconds(20)); // Then Assertions.assertEquals( @@ -587,7 +606,6 @@ void updateLegalEntityName() { when(legalEntityService.getLegalEntityByExternalId(eq(leExternalId))).thenReturn(Mono.just(legalEntity)); when(legalEntityService.getLegalEntityByInternalId(eq(leInternalId))).thenReturn(Mono.just(legalEntity)); - when(legalEntityService.createLegalEntity(any())).thenReturn(Mono.empty()); when(legalEntityService.putLegalEntity(any())).thenReturn(Mono.just(newLE)); when(accessGroupService.getServiceAgreementByExternalId(eq(customSaExId))).thenReturn(Mono.empty()); when(accessGroupService.createServiceAgreement(any(), eq(customSa))).thenReturn(Mono.just(customSa)); @@ -648,7 +666,8 @@ void updateUserName() { when(accessGroupService.getServiceAgreementByExternalId(eq(customSaExId))).thenReturn(Mono.empty()); when(accessGroupService.createServiceAgreement(any(), eq(customSa))).thenReturn(Mono.just(customSa)); when(accessGroupService.setupJobRole(any(), any(), any())).thenReturn(Mono.just(jobRole)); - when(accessGroupService.updateServiceAgreementRegularUsers(any(), eq(customSa), any())).thenReturn(Mono.just(customSa)); + when(accessGroupService.updateServiceAgreementRegularUsers(any(), eq(customSa), any())).thenReturn( + Mono.just(customSa)); when(userService.getUserByExternalId(eq(regularUserExId))).thenReturn(Mono.just(oldRegularUser.getUser())); when(userService.getUserByExternalId(eq(adminExId))).thenReturn(Mono.just(adminUser)); when(userService.createUser(any(), any(), any())).thenReturn(Mono.just(adminUser)); @@ -839,6 +858,124 @@ void test_PostUserContacts() { Assertions.assertEquals(leExternalId, result.getData().getUsers().get(0).getContacts().get(0).getExternalId()); } + @Test + void userKindSegmentationIsDisabled() { + getMockLegalEntity(); + + when(userKindSegmentationSaga.isEnabled()).thenReturn(false); + + legalEntitySaga.executeTask(mockLegalEntityTask(legalEntity)).block(); + + verify(userKindSegmentationSaga, never()).executeTask(Mockito.any()); + } + + @Test + void userKindSegmentationUsesLegalEntityCustomerCategory() { + getMockLegalEntity(); + legalEntity.setCustomerCategory(CustomerCategory.RETAIL); + + when(userKindSegmentationSaga.isEnabled()).thenReturn(true); + + legalEntitySaga.executeTask(mockLegalEntityTask(legalEntity)).block(); + + verify(userKindSegmentationSaga, times(0)).getDefaultCustomerCategory(); + } + + @Test + void userKindSegmentationUsesDefaultCustomerCategory() { + getMockLegalEntity(); + legalEntity.legalEntityType(LegalEntityType.CUSTOMER); + + when(userKindSegmentationSaga.isEnabled()).thenReturn(true); + when(userKindSegmentationSaga.getDefaultCustomerCategory()).thenReturn(CustomerCategory.RETAIL.getValue()); + when(userKindSegmentationSaga.executeTask(any())).thenReturn( + Mono.just(Mockito.mock(UserKindSegmentationTask.class))); + + legalEntitySaga.executeTask(mockLegalEntityTask(legalEntity)).block(); + + verify(userKindSegmentationSaga, times(1)).getDefaultCustomerCategory(); + } + + @Test + void whenUserKindSegmentationIsEnabledAndNoCustomerCategoryCanBeDeterminedReturnsError() { + getMockLegalEntity(); + legalEntity.legalEntityType(LegalEntityType.CUSTOMER); + + when(userKindSegmentationSaga.isEnabled()).thenReturn(true); + when(userKindSegmentationSaga.getDefaultCustomerCategory()).thenReturn(null); + + var task = mockLegalEntityTask(legalEntity); + + Assertions.assertThrows( + StreamTaskException.class, + () -> executeLegalEntityTaskAndBlock(task), + "Failed to determine LE customerCategory for UserKindSegmentationSage." + ); + } + + @ParameterizedTest + @MethodSource("parameters_upster_error") + void upster_error(Exception ex, String error) { + + // Given + LegalEntityTask leTask = new LegalEntityTask(new LegalEntity().externalId(leExternalId)); + when(legalEntityService.getLegalEntityByExternalId(leExternalId)).thenReturn(Mono.error(ex)); + + // When + Mono result = legalEntitySaga.executeTask(leTask); + StreamTaskException stEx = null; + try { + result.block(); + } catch (StreamTaskException e) { + stEx = e; + } + + // Then + verify(legalEntityService).getLegalEntityByExternalId(any()); + org.assertj.core.api.Assertions.assertThat(stEx) + .isNotNull() + .extracting(e -> e.getTask().getHistory().get(1).getErrorMessage()) + .isEqualTo(error); + } + + @Test + void upsertUserWithProfile() { + getMockLegalEntity(); + User user = regularUser.getUser(); + + user.fullName("User With Profile") + .emailAddress(new EmailAddress().address("userwp@test.com").primary(true)) + .mobileNumber(new PhoneNumber().number("0123456").primary(true)) + .userProfile(new UserProfile() + .userId(user.getInternalId()) + .profileUrl("http://backbase.eu") + .addAdditionalEmailsItem(new Multivalued().primary(true).value("userwp@test.com")) + .addAdditionalPhoneNumbersItem(new Multivalued().primary(true).value("0123456"))); + + legalEntity.setUsers(singletonList(new JobProfileUser().user(user))); + + GetUserProfile getUserProfile = new GetUserProfile().userId(user.getInternalId()); + when(userProfileService.upsertUserProfile(any())).thenReturn(Mono.just(getUserProfile)); + when(userService.getUserProfile(user.getInternalId())).thenReturn( + Mono.just(new com.backbase.dbs.user.api.service.v2.model.UserProfile().fullName("User With Profile"))); + + legalEntitySaga.executeTask(mockLegalEntityTask(legalEntity)).block(); + + verify(userService).getUserProfile(user.getInternalId()); + } + + private static Stream parameters_upster_error() { + return Stream.of( + Arguments.of(new RuntimeException("Fake error"), "Fake error"), + Arguments.of(new WebClientResponseException(400, "Bad request", null, + "Fake validation error".getBytes(), Charset.defaultCharset()), "Fake validation error")); + } + + + private LegalEntityTask executeLegalEntityTaskAndBlock(LegalEntityTask task) { + return legalEntitySaga.executeTask(task).block(); + } + private List getUsers(int amount) { List users = new ArrayList<>(); @@ -861,6 +998,7 @@ private LegalEntityTask mockLegalEntityTask(LegalEntity legalEntity) { private LegalEntitySagaConfigurationProperties getLegalEntitySagaConfigurationProperties() { LegalEntitySagaConfigurationProperties sagaConfiguration = new LegalEntitySagaConfigurationProperties(); sagaConfiguration.setUseIdentityIntegration(true); + sagaConfiguration.setUserProfileEnabled(true); return sagaConfiguration; } diff --git a/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/UpdatedServiceAgreementSagaTest.java b/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/UpdatedServiceAgreementSagaTest.java index 00ce2c4a1..cc0aa72a6 100644 --- a/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/UpdatedServiceAgreementSagaTest.java +++ b/stream-legal-entity/legal-entity-core/src/test/java/com/backbase/stream/UpdatedServiceAgreementSagaTest.java @@ -7,7 +7,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.backbase.dbs.accesscontrol.api.service.v2.model.FunctionGroupItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupItem; import com.backbase.dbs.arrangement.api.service.v2.model.AccountArrangementItem; import com.backbase.dbs.user.api.service.v2.model.GetUser; import com.backbase.stream.legalentity.model.BaseProductGroup; @@ -30,8 +30,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; @@ -138,4 +136,4 @@ void updateServiceAgreement() { permissionsRequest.put(user2, permissionUser2); verify(accessGroupService).assignPermissionsBatch(any(), eq(permissionsRequest)); } -} \ No newline at end of file +} diff --git a/stream-legal-entity/legal-entity-http/src/test/java/com/backbase/stream/controller/LegalEntityAsyncControllerTest.java b/stream-legal-entity/legal-entity-http/src/test/java/com/backbase/stream/controller/LegalEntityAsyncControllerTest.java index ccbc72de9..cb1d3d124 100644 --- a/stream-legal-entity/legal-entity-http/src/test/java/com/backbase/stream/controller/LegalEntityAsyncControllerTest.java +++ b/stream-legal-entity/legal-entity-http/src/test/java/com/backbase/stream/controller/LegalEntityAsyncControllerTest.java @@ -7,9 +7,8 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -import com.backbase.dbs.accesscontrol.api.service.v2.LegalEntitiesApi; -import com.backbase.dbs.accesscontrol.api.service.v2.LegalEntityApi; -import com.backbase.dbs.accesscontrol.api.service.v2.model.FunctionGroupItem; +import com.backbase.dbs.accesscontrol.api.service.v3.LegalEntitiesApi; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupItem; import com.backbase.dbs.arrangement.api.service.v2.ArrangementsApi; import com.backbase.dbs.contact.api.service.v2.ContactsApi; import com.backbase.dbs.limit.api.service.v2.LimitsServiceApi; @@ -17,6 +16,8 @@ import com.backbase.dbs.user.api.service.v2.UserManagementApi; import com.backbase.dbs.user.api.service.v2.UserProfileManagementApi; import com.backbase.dbs.user.api.service.v2.model.GetUser; +import com.backbase.loan.inbound.api.service.v1.LoansApi; +import com.backbase.stream.audiences.UserKindSegmentationSaga; import com.backbase.stream.config.LegalEntityHttpConfiguration; import com.backbase.stream.configuration.LegalEntitySagaConfiguration; import com.backbase.stream.configuration.UpdatedServiceAgreementSagaConfiguration; @@ -96,7 +97,7 @@ class LegalEntityAsyncControllerTest { private LegalEntitiesApi legalEntitiesApi; @MockBean - private LegalEntityApi legalEntityApi; + private LoansApi loansApi; @MockBean private IdentityManagementApi identityManagementApi; @@ -110,6 +111,9 @@ class LegalEntityAsyncControllerTest { @MockBean private ArrangementsApi arrangementsApi; + @MockBean + private UserKindSegmentationSaga userKindSegmentationSaga; + @Autowired private WebTestClient webTestClient; diff --git a/stream-legal-entity/legal-entity-http/src/test/java/com/backbase/stream/it/LegalEntitySagaIT.java b/stream-legal-entity/legal-entity-http/src/test/java/com/backbase/stream/it/LegalEntitySagaIT.java index a78aec98b..8052c7411 100644 --- a/stream-legal-entity/legal-entity-http/src/test/java/com/backbase/stream/it/LegalEntitySagaIT.java +++ b/stream-legal-entity/legal-entity-http/src/test/java/com/backbase/stream/it/LegalEntitySagaIT.java @@ -3,8 +3,12 @@ import static com.backbase.stream.it.LegalEntitySagaIT.NotEmptyPattern.notEmpty; import static com.github.tomakehurst.wiremock.client.WireMock.stubFor; import static com.github.tomakehurst.wiremock.client.WireMock.verify; + + +import com.backbase.stream.legalentity.model.Loan; import java.util.Arrays; import java.util.Collections; +import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; @@ -32,6 +36,16 @@ import com.github.tomakehurst.wiremock.junit5.WireMockTest; import com.github.tomakehurst.wiremock.matching.MatchResult; import com.github.tomakehurst.wiremock.matching.StringValuePattern; +import java.util.Arrays; +import java.util.Collections; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.reactive.AutoConfigureWebTestClient; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.reactive.server.WebTestClient; @SpringBootTest @ActiveProfiles("it") @@ -94,6 +108,7 @@ private static LegalEntityTask defaultLegalEntityTask() { .name("Account 2") .currency("GBP") )) + .loans(List.of(new Loan().IBAN("IBAN"))) )) .users(Collections.singletonList( new JobProfileUser() @@ -138,21 +153,21 @@ private void setupWireMock() { ); stubFor( - WireMock.get("/access-control/service-api/v2/legalentities/external/100000") + WireMock.get("/access-control/service-api/v3/accesscontrol/legal-entities/external/100000") .willReturn(WireMock.aResponse() .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) .withBody("{\n\"additions\":{},\"id\":\"500000\",\"externalId\":\"100000\",\"name\":\"Legal Entity\",\"type\":\"CUSTOMER\"\n}")) ); stubFor( - WireMock.get("/access-control/service-api/v2/legalentities/500000") + WireMock.get("/access-control/service-api/v3/accesscontrol/legal-entities/500000") .willReturn(WireMock.aResponse() .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) .withBody("{\n\"additions\":{},\"id\":\"500000\",\"externalId\":\"100000\",\"name\":\"Legal Entity\",\"type\":\"CUSTOMER\"\n}")) ); stubFor( - WireMock.put("/access-control/service-api/v2/legalentities") + WireMock.put("/access-control/service-api/v3/accesscontrol/legal-entities") .willReturn(WireMock.aResponse() .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) .withBody("{\n\"additions\":{},\"id\":\"500000\",\"externalId\":\"100000\",\"name\":\"Legal Entity\",\"type\":\"CUSTOMER\"\n}")) @@ -185,53 +200,53 @@ private void setupWireMock() { ); stubFor( - WireMock.get("/access-control/service-api/v2/accessgroups/serviceagreements/external/Service_Agreement_Id") + WireMock.get("/access-control/service-api/v3/accesscontrol/service-agreements/external/Service_Agreement_Id") .willReturn(WireMock.aResponse() .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) .withBody("{\"additions\":{},\"creatorLegalEntity\":\"500000\",\"status\":\"ENABLED\",\"id\":\"500001\",\"externalId\":\"Service_Agreement_Id\",\"name\":\"\",\"description\":\"Custom Service Agreement\",\"isMaster\":false,\"validFromDate\":null,\"validFromTime\":null,\"validUntilDate\":null,\"validUntilTime\":null}")) ); stubFor( - WireMock.get("/access-control/service-api/v2/accessgroups/service-agreements/500001/participants") + WireMock.get("/access-control/service-api/v3/accesscontrol/service-agreements/500001/participants") .willReturn(WireMock.aResponse() .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) .withBody("[{\"additions\":{},\"id\":\"500000\",\"externalId\":\"100000\",\"name\":\"Legal Entity\",\"sharingUsers\":true,\"sharingAccounts\":true}]")) ); stubFor( - WireMock.put("/access-control/service-api/v2/accessgroups/serviceagreements/ingest/service-agreements/participants") + WireMock.put("/access-control/service-api/v3/accesscontrol/service-agreements/ingest/participants") .willReturn(WireMock.aResponse().withStatus(HttpStatus.ACCEPTED.value())) ); stubFor( - WireMock.get("/access-control/service-api/v2/accesscontrol/accessgroups/serviceagreements/500001/users") + WireMock.get("/access-control/service-api/v3/accesscontrol/service-agreements/500001/users") .willReturn(WireMock.aResponse().withStatus(HttpStatus.NOT_FOUND.value())) ); stubFor( - WireMock.put("/access-control/service-api/v2/accessgroups/serviceagreements/ingest/service-agreements/users") + WireMock.put("/access-control/service-api/v3/accesscontrol/service-agreements/ingest/users") .willReturn(WireMock.aResponse().withStatus(HttpStatus.ACCEPTED.value())) ); stubFor( - WireMock.get("/access-control/service-api/v2/accesscontrol/accessgroups/function-groups?serviceAgreementId=500001") + WireMock.get("/access-control/service-api/v3/accesscontrol/function-groups?serviceAgreementId=500001") .willReturn(WireMock.aResponse() .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) .withBody("[{\"additions\":{},\"id\":\"500002\",\"serviceAgreementId\":\"500001\",\"name\":\"Private - Read only\",\"description\":\"Private - Read only\",\"type\":\"TEMPLATE\",\"permissions\":[{\"functionId\":\"1029\",\"assignedPrivileges\":[{\"additions\":{},\"privilege\":\"view\"}]}]},{\"additions\":{},\"id\":\"500003\",\"serviceAgreementId\":\"500001\",\"name\":\"Private - Full access\",\"description\":\"Private - Full access\",\"type\":\"TEMPLATE\",\"permissions\":[{\"functionId\":\"1029\",\"assignedPrivileges\":[{\"additions\":{},\"privilege\":\"create\"},{\"additions\":{},\"privilege\":\"edit\"},{\"additions\":{},\"privilege\":\"delete\"},{\"additions\":{},\"privilege\":\"execute\"},{\"additions\":{},\"privilege\":\"view\"}]}]}]")) ); stubFor( - WireMock.put("/access-control/service-api/v2/accessgroups/function-groups/batch/update") + WireMock.put("/access-control/service-api/v3/accesscontrol/function-groups/batch/update") .willReturn(WireMock.aResponse().withStatus(HttpStatus.ACCEPTED.value())) ); stubFor( - WireMock.put("/access-control/service-api/v2/accessgroups/users/permissions/user-permissions") + WireMock.put("/access-control/service-api/v3/accessgroups/users/permissions/user-permissions") .willReturn(WireMock.aResponse().withStatus(HttpStatus.ACCEPTED.value())) ); stubFor( - WireMock.get("/access-control/service-api/v2/accesscontrol/accessgroups/users/9ac44fca/service-agreements/500001/permissions") + WireMock.get("/access-control/service-api/v3/accessgroups/users/9ac44fca/service-agreements/500001/permissions") .willReturn(WireMock.aResponse() .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) .withBody("{\"additions\":{},\"approvalId\":null,\"items\":[{\"additions\":{},\"functionGroupId\":\"500002\",\"dataGroupIds\":[],\"selfApprovalPolicies\":[]},{\"additions\":{},\"functionGroupId\":\"500003\",\"dataGroupIds\":[],\"selfApprovalPolicies\":[]}]}")) @@ -243,21 +258,39 @@ private void setupWireMock() { ); stubFor( - WireMock.post("/access-control/service-api/v2/accessgroups/data-groups") + WireMock.post("/access-control/service-api/v3/accesscontrol/data-groups") .willReturn(WireMock.aResponse().withStatus(HttpStatus.CREATED.value())) ); stubFor( - WireMock.get("/access-control/service-api/v2/accesscontrol/accessgroups/data-groups?serviceAgreementId=500001&includeItems=true") + WireMock.get("/access-control/service-api/v3/accesscontrol/data-groups?serviceAgreementId=500001&includeItems=true") .willReturn(WireMock.aResponse() .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) .withBody("[{\"additions\":{},\"id\":\"4002\",\"name\":\"Default PG\",\"description\":\"Default data group description\",\"serviceAgreementId\":\"500001\",\"type\":\"ARRANGEMENTS\",\"approvalId\":null,\"items\":[\"arrangement-id-1\"]},{\"additions\":{},\"id\":\"4003\",\"name\":\"Mixed PG\",\"description\":\"Default data group description\",\"serviceAgreementId\":\"500001\",\"type\":\"ARRANGEMENTS\",\"approvalId\":null,\"items\":[\"arrangement-id-2\"]}]")) ); stubFor( - WireMock.put("/access-control/service-api/v2/accessgroups/data-groups/batch/update/data-items") + WireMock.put("/access-control/service-api/v3/accesscontrol/data-groups/batch/update/data-items") .willReturn(WireMock.aResponse().withStatus(HttpStatus.ACCEPTED.value())) ); + stubFor( + WireMock.post("/loan/service-api/v1/loans/batch") + .willReturn(WireMock.aResponse().withStatus(HttpStatus.MULTI_STATUS.value()) + .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBody("[{\"arrangementId\":\"arrId1\",\"resourceId\":\"resId1\"}]")) + ); + stubFor( + WireMock.get("/user-manager/service-api/v2/users/identities/9ac44fca") + .willReturn(WireMock.aResponse().withStatus(HttpStatus.MULTI_STATUS.value()) + .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBody("{\"internalId\":\"9ac44fca\",\"externalId\":\"externalId\"}")) + ); + stubFor( + WireMock.put("/user-manager/service-api/v2/users/identities/9ac44fca") + .willReturn(WireMock.aResponse().withStatus(HttpStatus.MULTI_STATUS.value()) + .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) + .withBody("")) + ); } /** @@ -280,7 +313,7 @@ void legalEntitySaga() { .expectStatus().isEqualTo(200); // Then - verify(WireMock.getRequestedFor(WireMock.urlEqualTo("/access-control/service-api/v2/legalentities/500000")) + verify(WireMock.getRequestedFor(WireMock.urlEqualTo("/access-control/service-api/v3/accesscontrol/legal-entities/500000")) .withHeader("X-TID", WireMock.equalTo("tenant-id")) .withHeader("X-B3-TraceId", notEmpty()) .withHeader("X-B3-SpanId", notEmpty())); @@ -305,7 +338,7 @@ void legalEntitySagaEmptyProductGroup() { .expectStatus().isEqualTo(200); // Then - verify(WireMock.getRequestedFor(WireMock.urlEqualTo("/access-control/service-api/v2/legalentities/500000")) + verify(WireMock.getRequestedFor(WireMock.urlEqualTo("/access-control/service-api/v3/accesscontrol/legal-entities/500000")) .withHeader("X-B3-TraceId", notEmpty()) .withHeader("X-B3-SpanId", notEmpty())); } @@ -335,7 +368,7 @@ void legalEntitySagaUpdateLegalEntity() { // Then - verify(WireMock.getRequestedFor(WireMock.urlEqualTo("/access-control/service-api/v2/legalentities/500000")) + verify(WireMock.getRequestedFor(WireMock.urlEqualTo("/access-control/service-api/v3/accesscontrol/legal-entities/500000")) .withHeader("X-TID", WireMock.equalTo("tenant-id")) .withHeader("X-B3-TraceId", notEmpty()) .withHeader("X-B3-SpanId", notEmpty())); diff --git a/stream-legal-entity/legal-entity-http/src/test/resources/application-it.yml b/stream-legal-entity/legal-entity-http/src/test/resources/application-it.yml index ceff0336b..a25627230 100644 --- a/stream-legal-entity/legal-entity-http/src/test/resources/application-it.yml +++ b/stream-legal-entity/legal-entity-http/src/test/resources/application-it.yml @@ -20,6 +20,10 @@ spring: - uri: http://localhost:10000 metadata: contextPath: /arrangement-manager + loan: + - uri: http://localhost:10000 + metadata: + contextPath: /loan kubernetes: config: enabled: false diff --git a/stream-limits/limits-core/src/main/java/com/backbase/stream/configuration/LimitsServiceConfiguration.java b/stream-limits/limits-core/src/main/java/com/backbase/stream/configuration/LimitsServiceConfiguration.java index a55d98347..5e80832f2 100644 --- a/stream-limits/limits-core/src/main/java/com/backbase/stream/configuration/LimitsServiceConfiguration.java +++ b/stream-limits/limits-core/src/main/java/com/backbase/stream/configuration/LimitsServiceConfiguration.java @@ -19,9 +19,11 @@ @Configuration public class LimitsServiceConfiguration { + private final LimitsWorkerConfigurationProperties limitsWorkerConfigurationProperties; + @Bean public LimitsSaga limitsSaga(LimitsServiceApi limitsServiceApi) { - return new LimitsSaga(limitsServiceApi); + return new LimitsSaga(limitsServiceApi,limitsWorkerConfigurationProperties); } public static class InMemoryLimitsUnitOfWorkRepository extends diff --git a/stream-limits/limits-core/src/main/java/com/backbase/stream/configuration/LimitsWorkerConfigurationProperties.java b/stream-limits/limits-core/src/main/java/com/backbase/stream/configuration/LimitsWorkerConfigurationProperties.java index fa3958995..605097241 100644 --- a/stream-limits/limits-core/src/main/java/com/backbase/stream/configuration/LimitsWorkerConfigurationProperties.java +++ b/stream-limits/limits-core/src/main/java/com/backbase/stream/configuration/LimitsWorkerConfigurationProperties.java @@ -10,4 +10,5 @@ @Data public class LimitsWorkerConfigurationProperties extends StreamWorkerConfiguration { private boolean continueOnError; + private boolean enabled = true; } diff --git a/stream-limits/limits-core/src/main/java/com/backbase/stream/limit/LimitsSaga.java b/stream-limits/limits-core/src/main/java/com/backbase/stream/limit/LimitsSaga.java index a30764575..a25b8247a 100644 --- a/stream-limits/limits-core/src/main/java/com/backbase/stream/limit/LimitsSaga.java +++ b/stream-limits/limits-core/src/main/java/com/backbase/stream/limit/LimitsSaga.java @@ -5,6 +5,7 @@ import com.backbase.dbs.limit.api.service.v2.LimitsServiceApi; import com.backbase.dbs.limit.api.service.v2.model.CreateLimitRequestBody; import com.backbase.dbs.limit.api.service.v2.model.LimitsRetrievalPostResponseBody; +import com.backbase.stream.configuration.LimitsWorkerConfigurationProperties; import com.backbase.stream.mapper.LimitsMapper; import com.backbase.stream.worker.StreamTaskExecutor; import com.backbase.stream.worker.exception.StreamTaskException; @@ -14,6 +15,7 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.mapstruct.factory.Mappers; +import org.springframework.web.reactive.function.client.WebClientResponseException; import reactor.core.publisher.Mono; @Slf4j @@ -21,6 +23,7 @@ public class LimitsSaga implements StreamTaskExecutor { public static final String LIMIT = "limit"; + public static final String RETRIEVE = "retrieve"; public static final String CREATE = "create"; public static final String SUCCESS = "success"; public static final String ERROR = "error"; @@ -31,16 +34,33 @@ public class LimitsSaga implements StreamTaskExecutor { public static final String UPDATED_SUCCESSFULLY = "Limit updated successfully"; public static final String FAILED_TO_INGEST_LIMITS = "Failed to ingest limits"; private final LimitsServiceApi limitsApi; + + private final LimitsWorkerConfigurationProperties limitsWorkerConfigurationProperties; private final LimitsMapper mapper = Mappers.getMapper(LimitsMapper.class); @Override public Mono executeTask(LimitsTask limitsTask) { CreateLimitRequestBody item = limitsTask.getData(); + if (!limitsWorkerConfigurationProperties.isEnabled()) { + log.info("backbase.stream.limits.worker.enabled is false, Skipping limits ingestion"); + return Mono.just(limitsTask); + } + log.info("Started ingestion of limits {} for user {}", - item.getEntities().stream().map(entity -> entity.getEtype() + COLON + SPACE + entity.getEref()) - .collect(Collectors.joining(COMMA + SPACE)), item.getUserBBID()); + item.getEntities().stream().map(entity -> entity.getEtype() + COLON + SPACE + entity.getEref()) + .collect(Collectors.joining(COMMA + SPACE)), item.getUserBBID()); return limitsApi.postLimitsRetrieval(mapper.map(item)) + .onErrorResume(throwable -> { + if (throwable instanceof WebClientResponseException webClientResponseException) { + limitsTask.error(LIMIT, RETRIEVE, ERROR, item.getUserBBID(), null, webClientResponseException, + webClientResponseException.getResponseBodyAsString(), FAILED_TO_INGEST_LIMITS); + } else { + limitsTask.error(LIMIT, RETRIEVE, ERROR, item.getUserBBID(), null, throwable, + throwable.getMessage(), FAILED_TO_INGEST_LIMITS); + } + return Mono.error(new StreamTaskException(limitsTask, throwable, FAILED_TO_INGEST_LIMITS)); + }) .collectList() .flatMap(limitsRetrievalPostResponseBody -> { if (isEmpty(limitsRetrievalPostResponseBody)) { diff --git a/stream-limits/limits-core/src/test/java/com/backbase/stream/limit/LimitsSagaTest.java b/stream-limits/limits-core/src/test/java/com/backbase/stream/limit/LimitsSagaTest.java index 32e1cbd50..578ffd231 100644 --- a/stream-limits/limits-core/src/test/java/com/backbase/stream/limit/LimitsSagaTest.java +++ b/stream-limits/limits-core/src/test/java/com/backbase/stream/limit/LimitsSagaTest.java @@ -3,21 +3,30 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; - +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; import com.backbase.dbs.limit.api.service.v2.LimitsServiceApi; import com.backbase.dbs.limit.api.service.v2.model.CreateLimitRequestBody; import com.backbase.dbs.limit.api.service.v2.model.Entity; import com.backbase.dbs.limit.api.service.v2.model.LimitByUuidPutResponseBody; import com.backbase.dbs.limit.api.service.v2.model.LimitsPostResponseBody; import com.backbase.dbs.limit.api.service.v2.model.LimitsRetrievalPostResponseBody; +import com.backbase.stream.configuration.LimitsWorkerConfigurationProperties; +import com.backbase.stream.worker.exception.StreamTaskException; +import java.nio.charset.Charset; import java.util.List; +import java.util.stream.Stream; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; +import org.springframework.web.reactive.function.client.WebClientResponseException; @ExtendWith(MockitoExtension.class) class LimitsSagaTest { @@ -28,6 +37,47 @@ class LimitsSagaTest { @Mock private LimitsServiceApi limitsApi; + @Mock + private LimitsWorkerConfigurationProperties limitsWorkerConfigurationProperties; + + @BeforeEach + public void init() { + when(limitsWorkerConfigurationProperties.isEnabled()).thenReturn(Boolean.TRUE); + } + + @ParameterizedTest + @MethodSource("parameters_retrieveLimits_error") + void retrieveLimits_error(Exception ex, String error) { + + // Given + LimitsTask limitsTask = createTask(); + when(limitsApi.postLimitsRetrieval(any())) + .thenReturn(Flux.error(ex)); + + // When + Mono result = limitsSaga.executeTask(limitsTask); + StreamTaskException stEx = null; + try { + result.block(); + } catch (StreamTaskException e) { + stEx = e; + } + + // Then + verify(limitsApi).postLimitsRetrieval(any()); + Assertions.assertThat(stEx) + .isNotNull() + .extracting(e -> e.getTask().getHistory().get(0).getErrorMessage()) + .isEqualTo(error); + } + + private static Stream parameters_retrieveLimits_error() { + return Stream.of( + Arguments.of(new RuntimeException("Fake error"), "Fake error"), + Arguments.of(new WebClientResponseException(422, "Unprocessable Entity", null, + "Fake validation error".getBytes(), Charset.defaultCharset()), "Fake validation error")); + } + @Test void createLimits() { @@ -41,7 +91,6 @@ void createLimits() { result.block(); // Then - verify(limitsApi).postLimits(any()); verify(limitsApi).postLimitsRetrieval(any()); } diff --git a/stream-loans/loans-core/pom.xml b/stream-loans/loans-core/pom.xml new file mode 100644 index 000000000..ad3133103 --- /dev/null +++ b/stream-loans/loans-core/pom.xml @@ -0,0 +1,92 @@ + + + 4.0.0 + + + com.backbase.stream + stream-loans + 3.61.0 + + + loans-core + jar + Stream :: Loans Core + + + true + + + + + com.backbase.buildingblocks + backbase-building-blocks-release + compile + + + + + + + + com.backbase.stream + legal-entity-model + ${project.version} + + + com.backbase.stream + stream-dbs-clients + ${project.version} + + + com.backbase.stream + stream-worker + ${project.version} + + + + io.projectreactor + reactor-test + test + + + + com.backbase.buildingblocks + service-sdk-starter-test + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + + lombok + org.projectlombok + ${lombok.version} + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + + -parameters + -Amapstruct.verbose=true + + + + + + diff --git a/stream-loans/loans-core/src/main/java/com/backbase/stream/configuration/LoansServiceConfiguration.java b/stream-loans/loans-core/src/main/java/com/backbase/stream/configuration/LoansServiceConfiguration.java new file mode 100644 index 000000000..f47af3d58 --- /dev/null +++ b/stream-loans/loans-core/src/main/java/com/backbase/stream/configuration/LoansServiceConfiguration.java @@ -0,0 +1,46 @@ +package com.backbase.stream.configuration; + +import com.backbase.loan.inbound.api.service.v1.LoansApi; +import com.backbase.stream.loan.LoansSaga; +import com.backbase.stream.loan.LoansTask; +import com.backbase.stream.loan.LoansUnitOfWorkExecutor; +import com.backbase.stream.loan.repository.LoansUnitOfWorkRepository; +import com.backbase.stream.worker.repository.impl.InMemoryReactiveUnitOfWorkRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@EnableConfigurationProperties({ + LoansWorkerConfigurationProperties.class +}) +@RequiredArgsConstructor +@Configuration +public class LoansServiceConfiguration { + + @Bean + public LoansSaga loansSaga(LoansApi loansApi) { + return new LoansSaga(loansApi); + } + + public static class InMemoryLoansUnitOfWorkRepository extends + InMemoryReactiveUnitOfWorkRepository implements LoansUnitOfWorkRepository { + + } + + @Bean + @ConditionalOnProperty(name = "backbase.stream.persistence", havingValue = "memory", matchIfMissing = true) + public LoansUnitOfWorkRepository loansUnitOfWorkRepository() { + return new InMemoryLoansUnitOfWorkRepository(); + } + + @Bean + public LoansUnitOfWorkExecutor loansUnitOfWorkExecutor( + LoansUnitOfWorkRepository repository, LoansSaga saga, + LoansWorkerConfigurationProperties configurationProperties + ) { + return new LoansUnitOfWorkExecutor(repository, saga, configurationProperties); + } + +} diff --git a/stream-loans/loans-core/src/main/java/com/backbase/stream/configuration/LoansWorkerConfigurationProperties.java b/stream-loans/loans-core/src/main/java/com/backbase/stream/configuration/LoansWorkerConfigurationProperties.java new file mode 100644 index 000000000..607f799bb --- /dev/null +++ b/stream-loans/loans-core/src/main/java/com/backbase/stream/configuration/LoansWorkerConfigurationProperties.java @@ -0,0 +1,13 @@ +package com.backbase.stream.configuration; + +import com.backbase.stream.worker.configuration.StreamWorkerConfiguration; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "backbase.stream.loans.worker") +@Slf4j +@Data +public class LoansWorkerConfigurationProperties extends StreamWorkerConfiguration { + private boolean continueOnError; +} diff --git a/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/LoanMapper.java b/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/LoanMapper.java new file mode 100644 index 000000000..5867992e8 --- /dev/null +++ b/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/LoanMapper.java @@ -0,0 +1,105 @@ +package com.backbase.stream.loan; + +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationBorrower; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationFrequency; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationLoan; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationLoanStatus; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationTermUnit; +import com.backbase.stream.legalentity.model.FrequencyUnit; +import com.backbase.stream.legalentity.model.Loan; +import com.backbase.stream.legalentity.model.LoanStatus; +import com.backbase.stream.legalentity.model.TermUnit; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.time.ZoneOffset; +import java.util.List; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.ValueMapping; + +@Mapper +public interface LoanMapper { + + @Mapping(target = "arrangementAttributes.internalId", source = "internalId") + @Mapping(target = "arrangementAttributes.externalId", source = "externalId") + @Mapping(target = "currencyCode", source = "currency") + @Mapping(target = "loanStatus", source = "status") + @Mapping(target = "loanType", source = "type") + @Mapping(target = "branchCode", source = "bankBranchCode") + @Mapping(target = "availableBalance", source = "availableBalance.amount") + @Mapping(target = "creditLimit", source = "creditLimit.amount") + @Mapping(target = "amortizationSchedule", ignore = true) + @Mapping(target = "debtAttributes.calculationPeriodStartDateTime", source = "calculationPeriodStartDateTime") + @Mapping(target = "debtAttributes.calculationPeriodEndDateTime", source = "calculationPeriodEndDateTime") + @Mapping(target = "debtAttributes.outstandingAmount", source = "outstandingPayment") + @Mapping(target = "debtAttributes.drawnAmount", source = "outstandingPrincipalAmount") + @Mapping(target = "debtAttributes.feesDueAmount", source = "feesDueAmount") + @Mapping(target = "debtAttributes.interestDueAmount", source = "accruedInterest") + @Mapping(target = "debtAttributes.paidAmount", source = "paidAmount") + @Mapping(target = "debtAttributes.taxesOnInterestAmount", source = "taxesOnInterestAmount") + @Mapping(target = "interestAttributes.interestRate", source = "accountInterestRate") + @Mapping(target = "interestAttributes.interestPaymentFrequency", source = "interestPaymentFrequency") + @Mapping(target = "interestAttributes.totalAnnualCostPercentage", source = "totalAnnualCostPercentage") + @Mapping(target = "overdueAttributes.inArrearsAmount", source = "amountInArrear") + @Mapping(target = "overdueAttributes.inArrearsDateTime", expression = "java( toOffsetDateTime(loan.getOverdueSince()) )") + @Mapping(target = "overdueAttributes.overduePaymentsCount", source = "overduePaymentsCount") + @Mapping(target = "overdueAttributes.overduePenaltyAmount", source = "overduePenaltyAmount") + @Mapping(target = "overdueAttributes.overdueInterestAmount", source = "overdueInterestAmount") + @Mapping(target = "overdueAttributes.overdueEscrowPaymentAmount", source = "overdueEscrowPaymentAmount") + @Mapping(target = "overdueAttributes.overduePrincipalPaymentAmount", source = "overduePrincipalPaymentAmount") + @Mapping(target = "overdueAttributes.overdueTaxesOnInterestAmount", source = "overdueTaxesOnInterestAmount") + @Mapping(target = "overdueAttributes.actualLatePaymentCommissionAmount", source = "actualLatePaymentCommissionAmount") + @Mapping(target = "overdueAttributes.contractLatePaymentCommissionAmount", source = "contractLatePaymentCommissionAmount") + @Mapping(target = "overdueAttributes.latePaymentCommissionTaxesAmount", source = "latePaymentCommissionTaxesAmount") + @Mapping(target = "paymentAttributes.nextRepaymentDateTime", source = "nextRepaymentDateTime") + @Mapping(target = "paymentAttributes.nextRepaymentAmount", source = "nextRepaymentAmount") + @Mapping(target = "paymentAttributes.paymentFrequency", source = "paymentFrequency") + @Mapping(target = "paymentAttributes.numberOfPaymentsMade", source = "numberOfPaymentsMade") + @Mapping(target = "paymentAttributes.totalNumberOfPayments", source = "totalNumberOfPayments") + @Mapping(target = "paymentAttributes.totalDirectAmortizationUnit", source = "totalDirectAmortizationUnit") + @Mapping(target = "paymentAttributes.totalDirectAmortization", source = "totalDirectAmortization") + @Mapping(target = "paymentAttributes.totalIndirectAmortizationUnit", source = "totalIndirectAmortizationUnit") + @Mapping(target = "paymentAttributes.totalIndirectAmortization", source = "totalIndirectAmortization") + @Mapping(target = "startDateTime", source = "accountOpeningDate") + @Mapping(target = "endDateTime", source = "maturityDate") + @Mapping(target = "defaultSettlementAccount.number", source = "defaultSettlementAccountNumber") + @Mapping(target = "defaultSettlementAccount.name", source = "accountHolderName") + @Mapping(target = "defaultSettlementAccount.internalId", source = "defaultSettlementAccountInternalId") + @Mapping(target = "defaultSettlementAccount.externalId", source = "defaultSettlementAccountExternalId") + InboundIntegrationLoan map(Loan loan); + + @ValueMapping(target = "DAY", source = "DAILY") + @ValueMapping(target = "WEEK", source = "WEEKLY") + @ValueMapping(target = "MONTH", source = "MONTHLY") + @ValueMapping(target = "YEAR", source = "QUARTERLY") + @ValueMapping(target = "YEAR", source = "YEARLY") + InboundIntegrationTermUnit map(TermUnit termUnit); + + @ValueMapping(target = "WEEKLY", source = "WEEKLY") + @ValueMapping(target = "BIWEEKLY", source = "BIWEEKLY") + @ValueMapping(target = "TWICEMONTHLY", source = "TWICEMONTHLY") + @ValueMapping(target = "MONTHLY", source = "MONTHLY") + @ValueMapping(target = "FOURWEEKS", source = "FOURWEEKS") + @ValueMapping(target = "BIMONTHLY", source = "BIMONTHLY") + @ValueMapping(target = "QUARTERLY", source = "QUARTERLY") + @ValueMapping(target = "SEMIANNUALLY", source = "SEMIANNUALLY") + @ValueMapping(target = "ANNUALLY", source = "ANNUALLY") + @ValueMapping(target = "MATURITY", source = "MATURITY") + InboundIntegrationFrequency map(FrequencyUnit frequencyUnit); + + + @Mapping(target = "name", expression = "java( name )") + InboundIntegrationBorrower map(String name); + + List map(List name); + + @ValueMapping(target = "ACTIVE", source = "ACTIVE") + @ValueMapping(target = "INACTIVE", source = "INACTIVE") + @ValueMapping(target = "PENDING", source = "PENDING") + InboundIntegrationLoanStatus map(LoanStatus status); + + default OffsetDateTime toOffsetDateTime(LocalDate localDate) { + return localDate != null ? localDate.atStartOfDay().atOffset(ZoneOffset.UTC) : null; + } + +} diff --git a/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/LoansSaga.java b/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/LoansSaga.java new file mode 100644 index 000000000..9eab7cc18 --- /dev/null +++ b/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/LoansSaga.java @@ -0,0 +1,54 @@ +package com.backbase.stream.loan; + +import com.backbase.loan.inbound.api.service.v1.LoansApi; +import com.backbase.loan.inbound.api.service.v1.model.BatchUpsertLoans; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationArrangementAttributes; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationLoan; +import com.backbase.stream.worker.StreamTaskExecutor; +import java.util.List; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.mapstruct.factory.Mappers; +import org.springframework.cloud.sleuth.annotation.SpanTag; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +@Slf4j +@RequiredArgsConstructor +public class LoansSaga implements StreamTaskExecutor { + + private final LoanMapper loanMapper = Mappers.getMapper(LoanMapper.class); + + private final LoansApi loansApi; + + @Override + public Mono executeTask(@SpanTag(value = "streamTask") LoansTask streamTask) { + List inboundLoans = + streamTask.getData().stream() + .map(loanMapper::map) + .toList(); + List externalIds = inboundLoans.stream() + .map(InboundIntegrationLoan::getArrangementAttributes) + .map(InboundIntegrationArrangementAttributes::getExternalId) + .toList(); + log.info("Started ingestion of loans with external arrangement ids {}", externalIds); + return Flux.fromIterable(inboundLoans) + .buffer(50) + .concatMap(batch -> + loansApi.postBatchUpsertLoans(new BatchUpsertLoans().loans(batch)) + .doOnNext(upsertLoanResponse -> { + streamTask.setResponse(upsertLoanResponse); + streamTask.info("loan", "upsert", "success", upsertLoanResponse.getResourceId(), + upsertLoanResponse.getArrangementId(), "upsert is successful"); + }) + .collectList()) + .collectList() + .thenReturn(streamTask); + } + + @Override + public Mono rollBack(LoansTask streamTask) { + return null; + } +} diff --git a/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/LoansTask.java b/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/LoansTask.java new file mode 100644 index 000000000..0ffdd7698 --- /dev/null +++ b/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/LoansTask.java @@ -0,0 +1,43 @@ +package com.backbase.stream.loan; + +import com.backbase.loan.inbound.api.service.v1.model.BatchResponseItemExtended; +import com.backbase.stream.legalentity.model.Loan; +import com.backbase.stream.worker.model.StreamTask; +import java.util.List; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class LoansTask extends StreamTask { + + public LoansTask(String unitOfWorkId, List data) { + super(unitOfWorkId); + this.data = data; + } + + private List data; + + public List getData() { + return data; + } + + public void setData(List data) { + this.data = data; + } + + public BatchResponseItemExtended getResponse() { + return response; + } + + public void setResponse(BatchResponseItemExtended response) { + this.response = response; + } + + private BatchResponseItemExtended response; + + @Override + public String getName() { + return "loan"; + } +} diff --git a/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/LoansUnitOfWorkExecutor.java b/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/LoansUnitOfWorkExecutor.java new file mode 100644 index 000000000..160136171 --- /dev/null +++ b/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/LoansUnitOfWorkExecutor.java @@ -0,0 +1,15 @@ +package com.backbase.stream.loan; + +import com.backbase.stream.worker.StreamTaskExecutor; +import com.backbase.stream.worker.UnitOfWorkExecutor; +import com.backbase.stream.worker.configuration.StreamWorkerConfiguration; +import com.backbase.stream.worker.repository.UnitOfWorkRepository; + +public class LoansUnitOfWorkExecutor extends UnitOfWorkExecutor { + + public LoansUnitOfWorkExecutor(UnitOfWorkRepository repository, + StreamTaskExecutor streamTaskExecutor, StreamWorkerConfiguration streamWorkerConfiguration) { + super(repository, streamTaskExecutor, streamWorkerConfiguration); + } + +} diff --git a/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/repository/LoansUnitOfWorkRepository.java b/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/repository/LoansUnitOfWorkRepository.java new file mode 100644 index 000000000..c37657fea --- /dev/null +++ b/stream-loans/loans-core/src/main/java/com/backbase/stream/loan/repository/LoansUnitOfWorkRepository.java @@ -0,0 +1,10 @@ +package com.backbase.stream.loan.repository; + +import com.backbase.stream.loan.LoansTask; +import com.backbase.stream.worker.repository.UnitOfWorkRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface LoansUnitOfWorkRepository extends UnitOfWorkRepository { + +} diff --git a/stream-loans/loans-core/src/test/java/com/backbase/stream/loan/LoansSagaTest.java b/stream-loans/loans-core/src/test/java/com/backbase/stream/loan/LoansSagaTest.java new file mode 100644 index 000000000..4d76fb54d --- /dev/null +++ b/stream-loans/loans-core/src/test/java/com/backbase/stream/loan/LoansSagaTest.java @@ -0,0 +1,244 @@ +package com.backbase.stream.loan; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.backbase.loan.inbound.api.service.v1.LoansApi; +import com.backbase.loan.inbound.api.service.v1.model.BatchResponseItemExtended; +import com.backbase.loan.inbound.api.service.v1.model.BatchUpsertLoans; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationArrangementAttributes; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationBorrower; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationCollateral; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationDebtAttributes; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationDefaultSettlementAccount; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationFrequency; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationInterestAttributes; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationLoan; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationLoanStatus; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationOverdueAttributes; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationPaymentAttributes; +import com.backbase.loan.inbound.api.service.v1.model.InboundIntegrationTermUnit; +import com.backbase.stream.legalentity.model.AvailableBalance; +import com.backbase.stream.legalentity.model.Collateral; +import com.backbase.stream.legalentity.model.CreditLimit; +import com.backbase.stream.legalentity.model.DocumentMetadata; +import com.backbase.stream.legalentity.model.FrequencyUnit; +import com.backbase.stream.legalentity.model.Loan; +import com.backbase.stream.legalentity.model.LoanStatus; +import com.backbase.stream.legalentity.model.TermUnit; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.OffsetDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutionException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import reactor.core.publisher.Flux; + +@ExtendWith(MockitoExtension.class) +class LoansSagaTest { + + @Mock + private LoansApi loansApi; + + @InjectMocks + private LoansSaga loansSaga; + + @Test + void executeTask() throws ExecutionException, InterruptedException { + Loan loanEmpty = new Loan(); + String internalId = "6c520d87-f736-403a-8679-b08c5f3d9a70"; + loanEmpty.setInternalId(internalId); + String externalId = "fece07ee-bb19-4872-b0cc-f27efee17b9f"; + loanEmpty.setExternalId(externalId); + Loan loanFull = createLoan(); + + LoansTask streamTask = new LoansTask(); + streamTask.setData(List.of(loanEmpty, loanFull)); + ArgumentCaptor upsertLoansCaptor = ArgumentCaptor.forClass( + BatchUpsertLoans.class); + Mockito.when(loansApi.postBatchUpsertLoans(upsertLoansCaptor.capture())) + .thenReturn(Flux.just(new BatchResponseItemExtended().arrangementId(internalId).resourceId(externalId))); + + loansSaga.executeTask(streamTask).toFuture().get(); + + BatchUpsertLoans value = upsertLoansCaptor.getValue(); + Assertions.assertNotNull(value); + Assertions.assertNotNull(value.getLoans()); + Assertions.assertEquals(2, value.getLoans().size()); + Assertions.assertNotNull(value.getLoans().get(0)); + Assertions.assertNotNull(value.getLoans().get(0).getArrangementAttributes()); + assertEquals(externalId, value.getLoans().get(0).getArrangementAttributes().getExternalId()); + assertEquals(internalId, value.getLoans().get(0).getArrangementAttributes().getInternalId()); + assertLoan(value.getLoans().get(1)); + } + + private void assertLoan(InboundIntegrationLoan loan) { + InboundIntegrationArrangementAttributes arrangementAttributes = loan.getArrangementAttributes(); + assertEquals("0b72a53e-d8b8-45fe-b3d4-5fc62637ee0d", arrangementAttributes.getInternalId()); + assertEquals("2e41b450-1ef7-486b-b118-69481aa3624b", arrangementAttributes.getExternalId()); + assertEquals("USD", loan.getCurrencyCode()); + assertEquals(InboundIntegrationLoanStatus.ACTIVE, loan.getLoanStatus()); + assertEquals("LineOfCredit", loan.getLoanType()); + assertEquals("0001", loan.getBranchCode()); + assertEquals(new BigDecimal("100500.00000"), loan.getAvailableBalance()); + assertEquals(new BigDecimal("100501.00000"), loan.getCreditLimit()); + assertEquals(OffsetDateTime.parse("1991-08-24T16:22:00+00:00"), loan.getStartDateTime()); + assertEquals(OffsetDateTime.parse("1991-08-24T16:23:00+00:00"), loan.getEndDateTime()); + assertTrue(loan.getIsOverdue()); + assertTrue(loan.getRevolving()); + assertTrue(loan.getIsFullyRepaid()); + assertEquals(InboundIntegrationTermUnit.DAY, loan.getTermUnit()); + assertEquals(365, loan.getTermCount()); + assertEquals(InboundIntegrationTermUnit.YEAR, loan.getRemainingTermUnit()); + assertEquals(101, loan.getRemainingTermCount()); + assertEquals("v1", loan.getAdditions().get("k1")); + + InboundIntegrationDebtAttributes debtAttributes = loan.getDebtAttributes(); + assertNotNull(debtAttributes); + assertEquals(OffsetDateTime.parse("1991-08-24T16:20:00+00:00"), + debtAttributes.getCalculationPeriodStartDateTime()); + assertEquals(OffsetDateTime.parse("1991-08-24T04:20:00+00:00"), + debtAttributes.getCalculationPeriodEndDateTime()); + assertEquals(new BigDecimal("4004.004"), debtAttributes.getOutstandingAmount()); + assertEquals(new BigDecimal("6006.006"), debtAttributes.getDrawnAmount()); + assertEquals(new BigDecimal("7007.007"), debtAttributes.getFeesDueAmount()); + assertEquals(new BigDecimal("7008.008"), debtAttributes.getInterestDueAmount()); + assertEquals(new BigDecimal("7009.009"), debtAttributes.getPaidAmount()); + assertEquals(new BigDecimal("5005.005"), debtAttributes.getTaxesOnInterestAmount()); + + InboundIntegrationInterestAttributes interestAttributes = loan.getInterestAttributes(); + assertNotNull(interestAttributes); + assertEquals("8008.008", interestAttributes.getInterestRate()); + assertEquals(InboundIntegrationFrequency.ANNUALLY, interestAttributes.getInterestPaymentFrequency()); + assertEquals(new BigDecimal("10.20"), interestAttributes.getTotalAnnualCostPercentage()); + + InboundIntegrationOverdueAttributes overdueAttributes = loan.getOverdueAttributes(); + assertNotNull(overdueAttributes); + assertEquals(new BigDecimal("9009.009"), overdueAttributes.getInArrearsAmount()); + assertEquals(OffsetDateTime.parse("1991-08-24T00:00:00+00:00"), overdueAttributes.getInArrearsDateTime()); + assertEquals(4, overdueAttributes.getOverduePaymentsCount()); + assertEquals(new BigDecimal("10000.01"), overdueAttributes.getOverduePenaltyAmount()); + assertEquals(new BigDecimal("11000.011"), overdueAttributes.getOverdueInterestAmount()); + assertEquals(new BigDecimal("12000.012"), overdueAttributes.getOverdueEscrowPaymentAmount()); + assertEquals(new BigDecimal("13000.013"), overdueAttributes.getOverduePrincipalPaymentAmount()); + assertEquals(new BigDecimal("14000.014"), overdueAttributes.getOverdueTaxesOnInterestAmount()); + assertEquals(new BigDecimal("15000.015"), overdueAttributes.getActualLatePaymentCommissionAmount()); + assertEquals(new BigDecimal("16000.016"), overdueAttributes.getContractLatePaymentCommissionAmount()); + assertEquals(new BigDecimal("17000.017"), overdueAttributes.getLatePaymentCommissionTaxesAmount()); + + InboundIntegrationPaymentAttributes paymentAttributes = loan.getPaymentAttributes(); + assertNotNull(paymentAttributes); + assertEquals(OffsetDateTime.parse("1991-08-24T16:21:00+00:00"), paymentAttributes.getNextRepaymentDateTime()); + assertEquals(new BigDecimal("18000.018"), paymentAttributes.getNextRepaymentAmount()); + assertEquals(InboundIntegrationFrequency.MONTHLY, paymentAttributes.getPaymentFrequency()); + assertEquals(42, paymentAttributes.getNumberOfPaymentsMade()); + assertEquals(142, paymentAttributes.getTotalNumberOfPayments()); + assertEquals(InboundIntegrationTermUnit.WEEK, paymentAttributes.getTotalDirectAmortizationUnit()); + assertEquals(new BigDecimal("19000.019"), paymentAttributes.getTotalDirectAmortization()); + assertEquals(InboundIntegrationTermUnit.MONTH, paymentAttributes.getTotalIndirectAmortizationUnit()); + assertEquals(new BigDecimal("20000.02"), paymentAttributes.getTotalIndirectAmortization()); + + InboundIntegrationDefaultSettlementAccount defaultSettlementAccount = loan.getDefaultSettlementAccount(); + assertNotNull(defaultSettlementAccount); + assertEquals("DEFSETACCNUM01", defaultSettlementAccount.getNumber()); + assertEquals("ACCHOLDNAME01", defaultSettlementAccount.getName()); + assertEquals("DEFSETACCINTID01", defaultSettlementAccount.getInternalId()); + assertEquals("DEFSETACCEXTID01", defaultSettlementAccount.getExternalId()); + + List borrowers = loan.getBorrowers(); + assertNotNull(borrowers); + assertEquals(1, borrowers.size()); + assertEquals(new InboundIntegrationBorrower().name("borrower01"), borrowers.get(0)); + + List collaterals = loan.getCollaterals(); + assertNotNull(collaterals); + assertEquals(1, collaterals.size()); + InboundIntegrationCollateral expectedCollateral = new InboundIntegrationCollateral() + .currencyCode("USD") + .type("property") + .currentValue(new BigDecimal("21000.021")) + .specification("SPECIFICATION") + .nextRevaluationDateTime(OffsetDateTime.parse("1991-08-24T16:24:00+00:00")); + assertEquals(expectedCollateral, collaterals.get(0)); + } + + private Loan createLoan() { + Loan loan = new Loan(); + loan.setInternalId("0b72a53e-d8b8-45fe-b3d4-5fc62637ee0d"); + loan.setExternalId("2e41b450-1ef7-486b-b118-69481aa3624b"); + loan.setCurrency("USD"); + loan.setStatus(LoanStatus.ACTIVE); + loan.setType("LineOfCredit"); + loan.setBankBranchCode("0001"); + loan.setAvailableBalance(new AvailableBalance().currencyCode("USD").amount(new BigDecimal("100500.00000"))); + loan.setCreditLimit(new CreditLimit().currencyCode("USD").amount(new BigDecimal("100501.00000"))); + loan.setCalculationPeriodStartDateTime(OffsetDateTime.parse("1991-08-24T16:20:00+00:00")); + loan.setCalculationPeriodEndDateTime(OffsetDateTime.parse("1991-08-24T04:20:00+00:00")); + loan.setOutstandingPayment(new BigDecimal("4004.004")); + loan.setOutstandingPrincipalAmount(new BigDecimal("6006.006")); + loan.setFeesDueAmount(new BigDecimal("7007.007")); + loan.setAccruedInterest(new BigDecimal("7008.008")); + loan.setPaidAmount(new BigDecimal("7009.009")); + loan.setTaxesOnInterestAmount(new BigDecimal("5005.005")); + loan.setAccountInterestRate(new BigDecimal("8008.008")); + loan.setInterestPaymentFrequency(FrequencyUnit.ANNUALLY); + loan.setTotalAnnualCostPercentage(new BigDecimal("10.20")); + loan.setAmountInArrear(new BigDecimal("9009.009")); + loan.setOverdueSince(LocalDate.parse("1991-08-24")); + loan.setOverduePaymentsCount(4); + loan.setOverduePenaltyAmount(new BigDecimal("10000.01")); + loan.setOverdueInterestAmount(new BigDecimal("11000.011")); + loan.setOverdueEscrowPaymentAmount(new BigDecimal("12000.012")); + loan.setOverduePrincipalPaymentAmount(new BigDecimal("13000.013")); + loan.setOverdueTaxesOnInterestAmount(new BigDecimal("14000.014")); + loan.setActualLatePaymentCommissionAmount(new BigDecimal("15000.015")); + loan.setContractLatePaymentCommissionAmount(new BigDecimal("16000.016")); + loan.setLatePaymentCommissionTaxesAmount(new BigDecimal("17000.017")); + loan.setNextRepaymentDateTime(OffsetDateTime.parse("1991-08-24T16:21:00+00:00")); + loan.setNextRepaymentAmount(new BigDecimal("18000.018")); + loan.setPaymentFrequency(FrequencyUnit.MONTHLY); + loan.setNumberOfPaymentsMade(42); + loan.setTotalNumberOfPayments(142); + loan.setTotalDirectAmortizationUnit(TermUnit.WEEKLY); + loan.setTotalDirectAmortization(new BigDecimal("19000.019")); + loan.setTotalIndirectAmortizationUnit(TermUnit.MONTHLY); + loan.setTotalIndirectAmortization(new BigDecimal("20000.02")); + loan.setAccountOpeningDate(OffsetDateTime.parse("1991-08-24T16:22:00+00:00")); + loan.setMaturityDate(OffsetDateTime.parse("1991-08-24T16:23:00+00:00")); + loan.setDefaultSettlementAccountNumber("DEFSETACCNUM01"); + loan.setAccountHolderName("ACCHOLDNAME01"); + loan.setDefaultSettlementAccountInternalId("DEFSETACCINTID01"); + loan.setDefaultSettlementAccountExternalId("DEFSETACCEXTID01"); + loan.setDocumentMetadatas(Collections.singletonList(new DocumentMetadata() + .id("3b427323-8d5d-4863-a87f-57c94fb290d6") + .name("docName") + .contentType("application/json"))); + loan.setBorrowers(Collections.singletonList("borrower01")); + loan.setIsOverdue(true); + loan.setRevolving(true); + loan.setIsFullyRepaid(true); + loan.setTermUnit(TermUnit.DAILY); + loan.setTermCount(365); + loan.setRemainingTermUnit(TermUnit.QUARTERLY); + loan.setRemainingTermCount(101); + loan.setCollaterals(Collections.singletonList( + new Collateral() + .currencyCode("USD") + .type("property") + .currentValue(new BigDecimal("21000.021")) + .specification("SPECIFICATION") + .nextRevaluationDateTime(OffsetDateTime.parse("1991-08-24T16:24:00+00:00")))); + loan.setAdditions(Map.of("k1", "v1")); + return loan; + } +} \ No newline at end of file diff --git a/stream-loans/pom.xml b/stream-loans/pom.xml new file mode 100644 index 000000000..3cf42d764 --- /dev/null +++ b/stream-loans/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + + com.backbase.stream + stream-services + 3.61.0 + + + stream-loans + + pom + Stream :: Loans + + + loans-core + + diff --git a/stream-payment-order/payment-order-core/src/main/java/com/backbase/stream/config/PaymentOrderServiceConfiguration.java b/stream-payment-order/payment-order-core/src/main/java/com/backbase/stream/config/PaymentOrderServiceConfiguration.java index 9c330b16e..ae50acc70 100644 --- a/stream-payment-order/payment-order-core/src/main/java/com/backbase/stream/config/PaymentOrderServiceConfiguration.java +++ b/stream-payment-order/payment-order-core/src/main/java/com/backbase/stream/config/PaymentOrderServiceConfiguration.java @@ -1,5 +1,6 @@ package com.backbase.stream.config; +import com.backbase.dbs.arrangement.api.service.v2.ArrangementsApi; import com.backbase.dbs.paymentorder.api.service.v2.PaymentOrdersApi; import com.backbase.stream.PaymentOrderService; import com.backbase.stream.PaymentOrderServiceImpl; @@ -34,10 +35,11 @@ public PaymentOrderUnitOfWorkExecutor paymentOrderUnitOfWorkExecutor( PaymentOrderTaskExecutor paymentOrderTaskExecutor, PaymentOrderUnitOfWorkRepository paymentOrderUnitOfWorkRepository, PaymentOrderWorkerConfigurationProperties paymentOrderWorkerConfigurationProperties, - PaymentOrdersApi paymentOrdersApi) { + PaymentOrdersApi paymentOrdersApi, + ArrangementsApi arrangementsApi) { return new PaymentOrderUnitOfWorkExecutor(paymentOrderUnitOfWorkRepository, paymentOrderTaskExecutor, - paymentOrderWorkerConfigurationProperties, paymentOrdersApi, paymentOrderTypeMapper); + paymentOrderWorkerConfigurationProperties, paymentOrdersApi, arrangementsApi, paymentOrderTypeMapper); } @Bean diff --git a/stream-payment-order/payment-order-core/src/main/java/com/backbase/stream/model/PaymentOrderIngestContext.java b/stream-payment-order/payment-order-core/src/main/java/com/backbase/stream/model/PaymentOrderIngestContext.java index 6ad6271c7..a8c847ab3 100644 --- a/stream-payment-order/payment-order-core/src/main/java/com/backbase/stream/model/PaymentOrderIngestContext.java +++ b/stream-payment-order/payment-order-core/src/main/java/com/backbase/stream/model/PaymentOrderIngestContext.java @@ -1,5 +1,6 @@ package com.backbase.stream.model; +import com.backbase.dbs.arrangement.api.service.v2.model.AccountArrangementItems; import java.util.ArrayList; import java.util.List; @@ -16,5 +17,5 @@ public class PaymentOrderIngestContext { private String internalUserId; private List corePaymentOrder = new ArrayList<>(); private List existingPaymentOrder = new ArrayList<>(); - + private List arrangementIds = new ArrayList<>(); } diff --git a/stream-payment-order/payment-order-core/src/main/java/com/backbase/stream/paymentorder/PaymentOrderUnitOfWorkExecutor.java b/stream-payment-order/payment-order-core/src/main/java/com/backbase/stream/paymentorder/PaymentOrderUnitOfWorkExecutor.java index 62826d612..01d854fbc 100644 --- a/stream-payment-order/payment-order-core/src/main/java/com/backbase/stream/paymentorder/PaymentOrderUnitOfWorkExecutor.java +++ b/stream-payment-order/payment-order-core/src/main/java/com/backbase/stream/paymentorder/PaymentOrderUnitOfWorkExecutor.java @@ -7,7 +7,12 @@ import static com.backbase.dbs.paymentorder.api.service.v2.model.Status.READY; import static com.backbase.dbs.paymentorder.api.service.v2.model.Status.REJECTED; +import com.backbase.dbs.arrangement.api.service.v2.ArrangementsApi; +import com.backbase.dbs.arrangement.api.service.v2.model.AccountArrangementItem; +import com.backbase.dbs.arrangement.api.service.v2.model.AccountArrangementItems; +import com.backbase.dbs.arrangement.api.service.v2.model.AccountArrangementsFilter; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.stream.Stream; @@ -40,15 +45,18 @@ public class PaymentOrderUnitOfWorkExecutor extends UnitOfWorkExecutor { private final PaymentOrdersApi paymentOrdersApi; + private final ArrangementsApi arrangementsApi; private final PaymentOrderTypeMapper paymentOrderTypeMapper; public PaymentOrderUnitOfWorkExecutor(UnitOfWorkRepository repository, - StreamTaskExecutor streamTaskExecutor, - StreamWorkerConfiguration streamWorkerConfiguration, - PaymentOrdersApi paymentOrdersApi, - PaymentOrderTypeMapper paymentOrderTypeMapper) { + StreamTaskExecutor streamTaskExecutor, + StreamWorkerConfiguration streamWorkerConfiguration, + PaymentOrdersApi paymentOrdersApi, + ArrangementsApi arrangementsApi, + PaymentOrderTypeMapper paymentOrderTypeMapper) { super(repository, streamTaskExecutor, streamWorkerConfiguration); this.paymentOrdersApi = paymentOrdersApi; + this.arrangementsApi = arrangementsApi; this.paymentOrderTypeMapper = paymentOrderTypeMapper; } @@ -63,11 +71,12 @@ public Flux> prepareUnitOfWork(List> prepareUnitOfWork(Flux items) { return items.collectList() - .map(paymentOrderPostRequests -> this.createPaymentOrderIngestContext(paymentOrderPostRequests)) - .flatMap(this::getPersistedScheduledTransfers) - .flatMapMany(this::getPaymentOrderIngestRequest) - .bufferTimeout(streamWorkerConfiguration.getBufferSize(), streamWorkerConfiguration.getBufferMaxTime()) - .flatMap(this::prepareUnitOfWork); + .map(paymentOrderPostRequests -> this.createPaymentOrderIngestContext(paymentOrderPostRequests)) + .flatMap(this::addArrangementIdMap) + .flatMap(this::getPersistedScheduledTransfers) + .flatMapMany(this::getPaymentOrderIngestRequest) + .bufferTimeout(streamWorkerConfiguration.getBufferSize(), streamWorkerConfiguration.getBufferMaxTime()) + .flatMap(this::prepareUnitOfWork); } private PaymentOrderIngestContext createPaymentOrderIngestContext(List paymentOrderPostRequests) { @@ -90,7 +99,7 @@ private PaymentOrderIngestContext createPaymentOrderIngestContext(List { - listOfPayments.addAll(response.getPaymentOrders()); + listOfPayments.addAll(response.getPaymentOrders()); return listOfPayments; }) .map(getPaymentOrderResponses -> paymentOrderIngestContext2.existingPaymentOrder(getPaymentOrderResponses)) @@ -98,6 +107,21 @@ private PaymentOrderIngestContext createPaymentOrderIngestContext(List addArrangementIdMap(PaymentOrderIngestContext paymentOrderIngestContext) { + + return Flux.fromIterable(paymentOrderIngestContext.corePaymentOrder()) + .flatMap(this::getArrangement) + .distinct() + .collectList() + .map(accountInternalIdGetResponseBody -> paymentOrderIngestContext.arrangementIds(accountInternalIdGetResponseBody)); + } + + private Mono getArrangement(PaymentOrderPostRequest paymentOrderPostRequest) { + AccountArrangementsFilter accountArrangementsFilter = new AccountArrangementsFilter() + .externalArrangementIds(Collections.singletonList(paymentOrderPostRequest.getOriginatorAccount().getExternalArrangementId())); + return arrangementsApi.postFilter(accountArrangementsFilter); + } + /** * Calls the payment order service to retrieve existing payments. * @@ -105,13 +129,14 @@ private PaymentOrderIngestContext createPaymentOrderIngestContext(List getPayments(String internalUserId) { + var paymentOrderPostFilterRequest = new PaymentOrderPostFilterRequest(); paymentOrderPostFilterRequest.setStatuses( List.of(READY, ACCEPTED, PROCESSED, CANCELLED, REJECTED, CANCELLATION_PENDING)); return paymentOrdersApi.postFilterPaymentOrders( null, null, null, null, null, null, null, null, null, null, null, - internalUserId, null, null, Integer.MAX_VALUE, null, + internalUserId, null, null, null, Integer.MAX_VALUE, null, null, paymentOrderPostFilterRequest); } @@ -134,6 +159,11 @@ private Flux getPaymentOrderIngestRequest(PaymentOrde // build new payment list (Bank ref is in core, but not in DBS) paymentOrderIngestContext.corePaymentOrder().forEach(corePaymentOrder -> { if(!existingBankRefIds.contains(corePaymentOrder.getBankReferenceId())) { + AccountArrangementItem accountArrangementItem = getInternalArrangementId(paymentOrderIngestContext.arrangementIds(), + corePaymentOrder.getOriginatorAccount().getExternalArrangementId()); + if (accountArrangementItem != null) { + corePaymentOrder.getOriginatorAccount().setArrangementId(accountArrangementItem.getId()); + } paymentOrderIngestRequests.add(new NewPaymentOrderIngestRequest(corePaymentOrder)); } }); @@ -157,4 +187,13 @@ private Flux getPaymentOrderIngestRequest(PaymentOrde return Flux.fromIterable(paymentOrderIngestRequests); } + + private AccountArrangementItem getInternalArrangementId(List accountArrangementItemsList, String externalArrangementId) { + + return accountArrangementItemsList.stream() + .flatMap(a -> a.getArrangementElements().stream()) + .filter(b -> b.getExternalArrangementId().equalsIgnoreCase(externalArrangementId)) + .findFirst() + .orElse(null); + } } diff --git a/stream-payment-order/payment-order-core/src/test/java/com/backbase/stream/task/PaymentOrderUnitOfWorkExecutorTest.java b/stream-payment-order/payment-order-core/src/test/java/com/backbase/stream/task/PaymentOrderUnitOfWorkExecutorTest.java index 825603cb7..d3b3e7e1a 100644 --- a/stream-payment-order/payment-order-core/src/test/java/com/backbase/stream/task/PaymentOrderUnitOfWorkExecutorTest.java +++ b/stream-payment-order/payment-order-core/src/test/java/com/backbase/stream/task/PaymentOrderUnitOfWorkExecutorTest.java @@ -1,6 +1,16 @@ package com.backbase.stream.task; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; + +import com.backbase.dbs.arrangement.api.service.v2.ArrangementsApi; +import com.backbase.dbs.arrangement.api.service.v2.model.AccountArrangementItem; +import com.backbase.dbs.arrangement.api.service.v2.model.AccountArrangementItems; import com.backbase.dbs.paymentorder.api.service.v2.PaymentOrdersApi; +import com.backbase.dbs.paymentorder.api.service.v2.model.GetPaymentOrderResponse; +import com.backbase.dbs.paymentorder.api.service.v2.model.PaymentOrderPostFilterResponse; import com.backbase.dbs.paymentorder.api.service.v2.model.PaymentOrderPostRequest; import com.backbase.dbs.paymentorder.api.service.v2.model.PaymentOrderPostResponse; import com.backbase.stream.common.PaymentOrderBaseTest; @@ -12,7 +22,9 @@ import com.backbase.stream.paymentorder.PaymentOrderUnitOfWorkExecutor; import com.backbase.stream.worker.model.UnitOfWork; import com.backbase.stream.worker.repository.UnitOfWorkRepository; +import java.math.BigDecimal; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; @@ -31,6 +43,9 @@ public class PaymentOrderUnitOfWorkExecutorTest extends PaymentOrderBaseTest { @Mock private PaymentOrdersApi paymentOrdersApi; + @Mock + private ArrangementsApi arrangementsApi; + @Mock private UnitOfWorkRepository repository; @@ -38,10 +53,15 @@ public class PaymentOrderUnitOfWorkExecutorTest extends PaymentOrderBaseTest { private final PaymentOrderWorkerConfigurationProperties streamWorkerConfiguration = new PaymentOrderWorkerConfigurationProperties(); - @InjectMocks - private PaymentOrderUnitOfWorkExecutor paymentOrderUnitOfWorkExecutor = new PaymentOrderUnitOfWorkExecutor( - repository, streamTaskExecutor, streamWorkerConfiguration, - paymentOrdersApi, paymentOrderTypeMapper);; +// @InjectMocks + private PaymentOrderUnitOfWorkExecutor paymentOrderUnitOfWorkExecutor; + + @BeforeEach + void setup() { + paymentOrderUnitOfWorkExecutor = new PaymentOrderUnitOfWorkExecutor( + repository, streamTaskExecutor, streamWorkerConfiguration, + paymentOrdersApi, arrangementsApi, paymentOrderTypeMapper); + } @Test void test_prepareUnitOfWork_paymentOrderIngestRequestList() { @@ -57,13 +77,24 @@ void test_prepareUnitOfWork_paymentOrderIngestRequestList() { Mockito.lenient().when(paymentOrdersApi.postPaymentOrder(Mockito.any())) .thenReturn(Mono.just(paymentOrderPostResponse)); + AccountArrangementItem accountArrangementItem = new AccountArrangementItem() + .id("arrangementId_1") + .externalArrangementId("externalArrangementId_1"); + + AccountArrangementItems accountArrangementItems = new AccountArrangementItems() + .addArrangementElementsItem(accountArrangementItem); + + Mockito.lenient().when(arrangementsApi.postFilter(Mockito.any())) + .thenReturn(Mono.just(accountArrangementItems)); + StepVerifier.create(paymentOrderUnitOfWorkExecutor.prepareUnitOfWork(paymentOrderIngestRequestList)) .assertNext(unitOfWork -> { Assertions.assertTrue(unitOfWork.getUnitOfOWorkId().startsWith("payment-orders-mixed-")); Assertions.assertEquals(UnitOfWork.State.NEW, unitOfWork.getState()); Assertions.assertEquals(1, unitOfWork.getStreamTasks().size()); Assertions.assertEquals(paymentOrderIngestRequestList.size(), unitOfWork.getStreamTasks().get(0).getData().size()); - }); + }) + .verifyComplete(); } @Test @@ -74,16 +105,34 @@ void test_prepareUnitOfWork_paymentOrderPostRequestFlux() { .id("po_post_resp_id") .putAdditionsItem("key", "val"); - Mockito.lenient().when(paymentOrdersApi.postPaymentOrder(Mockito.any())) + Mockito.lenient().when(paymentOrdersApi.postPaymentOrder(any())) .thenReturn(Mono.just(paymentOrderPostResponse)); + GetPaymentOrderResponse getPaymentOrderResponse = new GetPaymentOrderResponse() + .id("arrangementId_1"); + PaymentOrderPostFilterResponse paymentOrderPostFilterResponse = new PaymentOrderPostFilterResponse() + .addPaymentOrdersItem(getPaymentOrderResponse) + .totalElements(new BigDecimal(1)); + + doReturn(Mono.just(paymentOrderPostFilterResponse)).when(paymentOrdersApi).postFilterPaymentOrders(any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any()); + + AccountArrangementItem accountArrangementItem = new AccountArrangementItem() + .id("arrangementId_1") + .externalArrangementId("externalArrangementId_1"); + AccountArrangementItems accountArrangementItems = new AccountArrangementItems() + .addArrangementElementsItem(accountArrangementItem); + + Mockito.when(arrangementsApi.postFilter(Mockito.any())) + .thenReturn(Mono.just(accountArrangementItems)); + StepVerifier.create(paymentOrderUnitOfWorkExecutor.prepareUnitOfWork(paymentOrderPostRequestFlux)) - .assertNext(unitOfWork -> { - Assertions.assertTrue(unitOfWork.getUnitOfOWorkId().startsWith("payment-orders-mixed-")); - Assertions.assertEquals(UnitOfWork.State.NEW, unitOfWork.getState()); - Assertions.assertEquals(1, unitOfWork.getStreamTasks().size()); - Assertions.assertEquals(paymentOrderPostRequest.size(), unitOfWork.getStreamTasks().get(0).getData().size()); - }); + .assertNext(unitOfWork -> { + Assertions.assertTrue(unitOfWork.getUnitOfOWorkId().startsWith("payment-orders-mixed-")); + Assertions.assertEquals(UnitOfWork.State.NEW, unitOfWork.getState()); + Assertions.assertEquals(1, unitOfWork.getStreamTasks().size()); + Assertions.assertEquals(paymentOrderPostRequest.size(), unitOfWork.getStreamTasks().get(0).getData().size()); + }) + .verifyComplete(); } } diff --git a/stream-portfolio/portfolio-core/src/main/java/com/backbase/stream/portfolio/mapper/PortfolioMapper.java b/stream-portfolio/portfolio-core/src/main/java/com/backbase/stream/portfolio/mapper/PortfolioMapper.java index 48d8482c2..6e0dd0564 100644 --- a/stream-portfolio/portfolio-core/src/main/java/com/backbase/stream/portfolio/mapper/PortfolioMapper.java +++ b/stream-portfolio/portfolio-core/src/main/java/com/backbase/stream/portfolio/mapper/PortfolioMapper.java @@ -39,9 +39,9 @@ @Mapper public interface PortfolioMapper { - PortfoliosPostRequest mapPortfolio(Portfolio region); + PortfoliosPostRequest mapPortfolio(Portfolio portfolio); - PortfoliosPutRequest mapPutPortfolio(Portfolio region); + PortfoliosPutRequest mapPutPortfolio(Portfolio portfolio); SubPortfoliosPostRequest mapSubPortfolio(SubPortfolio subPortfolio); diff --git a/stream-product/product-core/src/main/java/com/backbase/stream/product/mapping/ProductMapper.java b/stream-product/product-core/src/main/java/com/backbase/stream/product/mapping/ProductMapper.java index 7f9a67558..c4579298c 100644 --- a/stream-product/product-core/src/main/java/com/backbase/stream/product/mapping/ProductMapper.java +++ b/stream-product/product-core/src/main/java/com/backbase/stream/product/mapping/ProductMapper.java @@ -36,6 +36,7 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.MappingConstants; +import org.mapstruct.Named; import org.mapstruct.ReportingPolicy; import org.mapstruct.ValueMapping; import org.mapstruct.ValueMappings; @@ -67,6 +68,7 @@ public interface ProductMapper { @InheritConfiguration @Mapping(source = "debitCardsItems", target = "debitCards") @Mapping(source = ProductMapperConstants.ACCOUNT_HOLDER_NAME, target = ProductMapperConstants.ACCOUNT_HOLDER_NAMES) + @Mapping(source = ProductMapperConstants.PAN_SUFFIX, target = ProductMapperConstants.NUMBER) AccountArrangementItemPost toPresentation(CurrentAccount currentAccount); @@ -75,6 +77,7 @@ public interface ProductMapper { @Mapping(source = ProductMapperConstants.LEGAL_ENTITIES, target = ProductMapperConstants.EXTERNAL_LEGAL_ENTITY_IDS) @Mapping(source = ProductMapperConstants.DEBIT_CARDS_ITEMS, target = ProductMapperConstants.DEBIT_CARDS) @Mapping(source = ProductMapperConstants.ACCOUNT_HOLDER_NAME, target = ProductMapperConstants.ACCOUNT_HOLDER_NAMES) + @Mapping(source = ProductMapperConstants.PAN_SUFFIX, target = ProductMapperConstants.NUMBER) @InheritConfiguration AccountArrangementItemPost toPresentation(SavingsAccount savingsAccount); @@ -83,6 +86,7 @@ public interface ProductMapper { @Mapping(source = ProductMapperConstants.PRODUCT_TYPE_EXTERNAL_ID, target = ProductMapperConstants.EXTERNAL_PRODUCT_ID) @Mapping(source = ProductMapperConstants.LEGAL_ENTITIES, target = ProductMapperConstants.EXTERNAL_LEGAL_ENTITY_IDS) @Mapping(source = ProductMapperConstants.ACCOUNT_HOLDER_NAME, target = ProductMapperConstants.ACCOUNT_HOLDER_NAMES) + @Mapping(source = "debitCard", qualifiedByName = "mapDebitCardNumber", target = ProductMapperConstants.NUMBER) @InheritConfiguration AccountArrangementItemPost toPresentation(DebitCard debitCard); @@ -91,6 +95,7 @@ public interface ProductMapper { @Mapping(source = ProductMapperConstants.PRODUCT_TYPE_EXTERNAL_ID, target = ProductMapperConstants.EXTERNAL_PRODUCT_ID) @Mapping(source = ProductMapperConstants.LEGAL_ENTITIES, target = ProductMapperConstants.EXTERNAL_LEGAL_ENTITY_IDS) @Mapping(source = ProductMapperConstants.ACCOUNT_HOLDER_NAME, target = ProductMapperConstants.ACCOUNT_HOLDER_NAMES) + @Mapping(source = "creditCard", qualifiedByName = "mapCreditCardNumber", target = ProductMapperConstants.NUMBER) @InheritConfiguration AccountArrangementItemPost toPresentation(CreditCard creditCard); @@ -98,6 +103,7 @@ public interface ProductMapper { @Mapping(source = ProductMapperConstants.PRODUCT_TYPE_EXTERNAL_ID, target = ProductMapperConstants.EXTERNAL_PRODUCT_ID) @Mapping(source = ProductMapperConstants.LEGAL_ENTITIES, target = ProductMapperConstants.EXTERNAL_LEGAL_ENTITY_IDS) @Mapping(source = ProductMapperConstants.ACCOUNT_HOLDER_NAME, target = ProductMapperConstants.ACCOUNT_HOLDER_NAMES) + @Mapping(source = ProductMapperConstants.PAN_SUFFIX, target = ProductMapperConstants.NUMBER) @InheritConfiguration AccountArrangementItemPost toPresentation(TermDeposit termDeposit); @@ -105,6 +111,7 @@ public interface ProductMapper { @Mapping(source = ProductMapperConstants.PRODUCT_TYPE_EXTERNAL_ID, target = ProductMapperConstants.EXTERNAL_PRODUCT_ID) @Mapping(source = ProductMapperConstants.LEGAL_ENTITIES, target = ProductMapperConstants.EXTERNAL_LEGAL_ENTITY_IDS) @Mapping(source = "currentInvestment.amount", target = "currentInvestmentValue") + @Mapping(source = ProductMapperConstants.PAN_SUFFIX, target = ProductMapperConstants.NUMBER) @InheritConfiguration AccountArrangementItemPost toPresentation(InvestmentAccount investmentAccount); @@ -113,6 +120,7 @@ public interface ProductMapper { @Mapping(source = ProductMapperConstants.PRODUCT_TYPE_EXTERNAL_ID, target = ProductMapperConstants.EXTERNAL_PRODUCT_ID) @Mapping(source = ProductMapperConstants.LEGAL_ENTITIES, target = ProductMapperConstants.EXTERNAL_LEGAL_ENTITY_IDS) @Mapping(source = ProductMapperConstants.ACCOUNT_HOLDER_NAME, target = ProductMapperConstants.ACCOUNT_HOLDER_NAMES) + @Mapping(source = ProductMapperConstants.PAN_SUFFIX, target = ProductMapperConstants.NUMBER) @InheritConfiguration AccountArrangementItemPost toPresentation(Loan loan); @@ -357,4 +365,19 @@ default List mapLegalEntityReference(List value) { @Mapping(source = "userExternalId", target = "userId") AccountUserPreferencesItemPut mapUserPreference(UserPreferences userPreferences); + @Named("mapDebitCardNumber") + default String mapDebitCardNumber(DebitCard debitCard) { + if (StringUtils.hasText(debitCard.getNumber())) { + return debitCard.getNumber(); + } + return debitCard.getPanSuffix(); + } + + @Named("mapCreditCardNumber") + default String mapCreditCardNumber(CreditCard creditCard) { + if (StringUtils.hasText(creditCard.getNumber())) { + return creditCard.getNumber(); + } + return creditCard.getPanSuffix(); + } } diff --git a/stream-product/product-core/src/main/java/com/backbase/stream/product/mapping/ProductMapperConstants.java b/stream-product/product-core/src/main/java/com/backbase/stream/product/mapping/ProductMapperConstants.java index 098e010f5..ac6471bbd 100644 --- a/stream-product/product-core/src/main/java/com/backbase/stream/product/mapping/ProductMapperConstants.java +++ b/stream-product/product-core/src/main/java/com/backbase/stream/product/mapping/ProductMapperConstants.java @@ -31,5 +31,7 @@ class ProductMapperConstants { static final String INTERNAL_ID = "internalId"; static final String DEBIT_CARDS_ITEMS = "debitCardsItems"; static final String DEBIT_CARDS = "debitCards"; + static final String NUMBER = "number"; + static final String PAN_SUFFIX = "panSuffix"; } diff --git a/stream-product/product-core/src/main/java/com/backbase/stream/product/task/BatchProductIngestionMode.java b/stream-product/product-core/src/main/java/com/backbase/stream/product/task/BatchProductIngestionMode.java index 2d67ff11e..72182c889 100644 --- a/stream-product/product-core/src/main/java/com/backbase/stream/product/task/BatchProductIngestionMode.java +++ b/stream-product/product-core/src/main/java/com/backbase/stream/product/task/BatchProductIngestionMode.java @@ -17,9 +17,9 @@ @AllArgsConstructor public class BatchProductIngestionMode { - private FunctionGroupsMode functionGroupsMode = FunctionGroupsMode.UPSERT; - private DataGroupsMode dataGroupIngestionMode = DataGroupsMode.UPSERT; - private ArrangementsMode arrangementsMode = ArrangementsMode.UPSERT; + private FunctionGroupsMode functionGroupsMode; + private DataGroupsMode dataGroupIngestionMode; + private ArrangementsMode arrangementsMode; public static final BatchProductIngestionMode UPSERT = upsert(); public static final BatchProductIngestionMode REPLACE = replace(); diff --git a/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/BatchProductIngestionSaga.java b/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/BatchProductIngestionSaga.java index 65a552590..385c9d3e3 100644 --- a/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/BatchProductIngestionSaga.java +++ b/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/BatchProductIngestionSaga.java @@ -4,7 +4,6 @@ import static java.util.Comparator.naturalOrder; import static java.util.Comparator.nullsFirst; - import com.backbase.dbs.arrangement.api.service.v2.model.AccountArrangementItemPost; import com.backbase.stream.legalentity.model.BaseProduct; import com.backbase.stream.legalentity.model.BaseProductGroup; @@ -12,9 +11,12 @@ import com.backbase.stream.legalentity.model.BusinessFunctionGroup; import com.backbase.stream.legalentity.model.CustomDataGroupItem; import com.backbase.stream.legalentity.model.JobProfileUser; +import com.backbase.stream.legalentity.model.Loan; import com.backbase.stream.legalentity.model.ProductGroup; import com.backbase.stream.legalentity.model.ServiceAgreement; import com.backbase.stream.legalentity.model.User; +import com.backbase.stream.loan.LoansSaga; +import com.backbase.stream.loan.LoansTask; import com.backbase.stream.product.configuration.ProductIngestionSagaConfigurationProperties; import com.backbase.stream.product.service.ArrangementService; import com.backbase.stream.product.task.BatchProductGroupTask; @@ -30,6 +32,8 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; @@ -52,8 +56,8 @@ public class BatchProductIngestionSaga extends ProductIngestionSaga { public static final String BATCH_PRODUCT_GROUP = "batch-product-group"; - public BatchProductIngestionSaga(ArrangementService arrangementService, AccessGroupService accessGroupService, UserService userService, ProductIngestionSagaConfigurationProperties configurationProperties) { - super(arrangementService, accessGroupService, userService, configurationProperties); + public BatchProductIngestionSaga(ArrangementService arrangementService, AccessGroupService accessGroupService, UserService userService, ProductIngestionSagaConfigurationProperties configurationProperties, LoansSaga loansSaga) { + super(arrangementService, accessGroupService, userService, configurationProperties, loansSaga); } public Mono process(ProductGroupTask streamTask) { @@ -97,7 +101,30 @@ public Mono process(BatchProductGroupTask streamTask) { }) .flatMap(this::upsertArrangementsBatch) .flatMap(this::setupProductGroupsBatch) - .flatMap(this::setupBusinessFunctionsAndPermissionsBatch); + .flatMap(this::setupBusinessFunctionsAndPermissionsBatch) + .flatMap(this::setupLoans); + } + + @SuppressWarnings("java:S2259") + private Mono setupLoans(BatchProductGroupTask batchProductGroupTask) { + BatchProductGroup data = batchProductGroupTask.getData(); + List loans = Optional.ofNullable(data) + .map(BatchProductGroup::getProductGroups).stream() + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .map(BaseProductGroup::getLoans) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .toList(); + + if (!loans.isEmpty()) { + LoansTask loansTask = new LoansTask( + String.format(data.getServiceAgreement().getExternalId(), batchProductGroupTask.getId()), loans); + return Mono.just(loansTask) + .map(loansSaga::executeTask) + .thenReturn(batchProductGroupTask); + } + return Mono.just(batchProductGroupTask); } diff --git a/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/BusinessFunctionGroupMapper.java b/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/BusinessFunctionGroupMapper.java index 70053b85c..f4c594d25 100644 --- a/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/BusinessFunctionGroupMapper.java +++ b/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/BusinessFunctionGroupMapper.java @@ -1,7 +1,7 @@ package com.backbase.stream.product; -import com.backbase.dbs.accesscontrol.api.service.v2.model.FunctionGroupItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.Permission; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.Permission; import com.backbase.stream.legalentity.model.BusinessFunction; import com.backbase.stream.legalentity.model.BusinessFunctionGroup; import org.mapstruct.Mapper; diff --git a/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/ProductIngestionSaga.java b/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/ProductIngestionSaga.java index c3cfa222b..ed7f36519 100644 --- a/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/ProductIngestionSaga.java +++ b/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/ProductIngestionSaga.java @@ -5,7 +5,7 @@ import static java.util.Comparator.nullsFirst; import static org.springframework.util.CollectionUtils.isEmpty; -import com.backbase.dbs.accesscontrol.api.service.v2.model.FunctionGroupItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupItem; import com.backbase.dbs.arrangement.api.service.v2.model.AccountArrangementItem; import com.backbase.dbs.arrangement.api.service.v2.model.AccountArrangementItemPost; import com.backbase.dbs.arrangement.api.service.v2.model.AccountArrangementItemPut; @@ -25,6 +25,7 @@ import com.backbase.stream.legalentity.model.ServiceAgreement; import com.backbase.stream.legalentity.model.TermDeposit; import com.backbase.stream.legalentity.model.User; +import com.backbase.stream.loan.LoansSaga; import com.backbase.stream.product.configuration.ProductIngestionSagaConfigurationProperties; import com.backbase.stream.product.exception.ArrangementCreationException; import com.backbase.stream.product.exception.ArrangementUpdateException; @@ -80,12 +81,14 @@ public class ProductIngestionSaga { protected final AccessGroupService accessGroupService; protected final UserService userService; protected final ProductIngestionSagaConfigurationProperties configurationProperties; + protected final LoansSaga loansSaga; - public ProductIngestionSaga(ArrangementService arrangementService, AccessGroupService accessGroupService, UserService userService, ProductIngestionSagaConfigurationProperties configurationProperties) { + public ProductIngestionSaga(ArrangementService arrangementService, AccessGroupService accessGroupService, UserService userService, ProductIngestionSagaConfigurationProperties configurationProperties, LoansSaga loansSaga) { this.arrangementService = arrangementService; this.accessGroupService = accessGroupService; this.userService = userService; this.configurationProperties = configurationProperties; + this.loansSaga = loansSaga; } @ContinueSpan(log = "processProducts") @@ -302,7 +305,8 @@ public Mono upsertArrangement(ProductGroupTask streamTas return arrangementService.updateArrangement(arrangemenItemBase) .onErrorResume(ArrangementUpdateException.class, e -> { streamTask.error(ARRANGEMENT, UPDATE_ARRANGEMENT, FAILED, arrangementItemPost.getExternalArrangementId(), internalId, e, e.getHttpResponse(), "Failed to update arrangement: %s", arrangementItemPost.getExternalArrangementId()); - return Mono.error(new StreamTaskException(streamTask, e, "Failed to update arrangement")); + return Mono.error(new StreamTaskException(streamTask, e.getCause(), + e.getMessage() + " " + e.getCause().getMessage())); }) .map(actual -> { log.info("Updated arrangement: {}", actual.getExternalArrangementId()); diff --git a/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/ProductIngestionSagaConfiguration.java b/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/ProductIngestionSagaConfiguration.java index e2c1d2b64..f71e0388e 100644 --- a/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/ProductIngestionSagaConfiguration.java +++ b/stream-product/product-ingestion-saga/src/main/java/com/backbase/stream/product/ProductIngestionSagaConfiguration.java @@ -1,5 +1,6 @@ package com.backbase.stream.product; +import com.backbase.stream.loan.LoansSaga; import com.backbase.stream.product.configuration.ProductConfiguration; import com.backbase.stream.product.configuration.ProductIngestionSagaConfigurationProperties; import com.backbase.stream.product.service.ArrangementService; @@ -19,12 +20,14 @@ public class ProductIngestionSagaConfiguration { public ProductIngestionSaga productIngestionSaga(ArrangementService arrangementService, AccessGroupService accessGroupService, UserService userService, - ProductIngestionSagaConfigurationProperties configurationProperties) { + ProductIngestionSagaConfigurationProperties configurationProperties, + LoansSaga loansSaga) { return new ProductIngestionSaga( arrangementService, accessGroupService, userService, - configurationProperties + configurationProperties, + loansSaga ); } @@ -32,12 +35,14 @@ public ProductIngestionSaga productIngestionSaga(ArrangementService arrangementS public BatchProductIngestionSaga batchProductIngestionSaga(ArrangementService arrangementService, AccessGroupService accessGroupService, UserService userService, - ProductIngestionSagaConfigurationProperties configurationProperties) { + ProductIngestionSagaConfigurationProperties configurationProperties, + LoansSaga loansSaga) { return new BatchProductIngestionSaga( arrangementService, accessGroupService, userService, - configurationProperties + configurationProperties, + loansSaga ); } } diff --git a/stream-product/product-ingestion-saga/src/test/java/com/backbase/stream/product/BusinessFunctionGroupMapperTest.java b/stream-product/product-ingestion-saga/src/test/java/com/backbase/stream/product/BusinessFunctionGroupMapperTest.java index 9cbd35bef..4614e8a21 100644 --- a/stream-product/product-ingestion-saga/src/test/java/com/backbase/stream/product/BusinessFunctionGroupMapperTest.java +++ b/stream-product/product-ingestion-saga/src/test/java/com/backbase/stream/product/BusinessFunctionGroupMapperTest.java @@ -1,15 +1,14 @@ package com.backbase.stream.product; -import com.backbase.dbs.accesscontrol.api.service.v2.model.FunctionGroupItem; -import com.backbase.dbs.accesscontrol.api.service.v2.model.Permission; -import com.backbase.dbs.accesscontrol.api.service.v2.model.Privilege; -import org.junit.jupiter.api.Test; - -import java.util.Collections; - import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import com.backbase.dbs.accesscontrol.api.service.v3.model.FunctionGroupItem; +import com.backbase.dbs.accesscontrol.api.service.v3.model.Permission; +import com.backbase.dbs.accesscontrol.api.service.v3.model.Privilege; +import java.util.Collections; +import org.junit.jupiter.api.Test; + class BusinessFunctionGroupMapperTest { private final BusinessFunctionGroupMapper mapper = new BusinessFunctionGroupMapperImpl(); @@ -41,15 +40,15 @@ private FunctionGroupItem createFunctionGroupItem() { item.setId("1001"); item.setServiceAgreementId("S001"); item.setName("Payments"); - item.setPermissions(Collections.singletonList(createPermisson())); + item.setPermissions(Collections.singletonList(createPermission())); return item; } - private Permission createPermisson() { + private Permission createPermission() { Permission permission = new Permission(); permission.setFunctionId("F001"); permission.setAssignedPrivileges(Collections.singletonList(new Privilege().privilege("CREATE"))); return permission; } -} \ No newline at end of file +} diff --git a/stream-sdk/stream-parent/stream-worker/src/main/java/com/backbase/stream/worker/model/StreamTask.java b/stream-sdk/stream-parent/stream-worker/src/main/java/com/backbase/stream/worker/model/StreamTask.java index 2d5ec6809..683ea237f 100644 --- a/stream-sdk/stream-parent/stream-worker/src/main/java/com/backbase/stream/worker/model/StreamTask.java +++ b/stream-sdk/stream-parent/stream-worker/src/main/java/com/backbase/stream/worker/model/StreamTask.java @@ -54,9 +54,11 @@ public void warn(String entity, String operation, String result, String external */ public void error(String entity, String operation, String result, String externalId, String internalId, String message, Object... messageArgs) { - addHistory(entity, operation, result, externalId, internalId, String.format(message, messageArgs), + String formattedMessage = String.format(message, messageArgs); + + addHistory(entity, operation, result, externalId, internalId, formattedMessage, TaskHistory.Severity.ERROR, null, null); - error = message; + error = formattedMessage; } /** @@ -72,9 +74,11 @@ public void error(String entity, String operation, String result, String externa */ public void error(String entity, String operation, String result, String externalId, String internalId, Throwable throwable, String errorMessage, String message, Object... messageArgs) { - addHistory(entity, operation, result, externalId, internalId, String.format(message, messageArgs), + String formattedMessage = String.format(message, messageArgs); + + addHistory(entity, operation, result, externalId, internalId, formattedMessage, TaskHistory.Severity.ERROR, throwable, errorMessage); - error = message; + error = formattedMessage; } /**