Skip to content

Commit

Permalink
Merge pull request #370 from Backbase/feature/enhance_legal_entity_Limit
Browse files Browse the repository at this point in the history
Enhance Legal Entity level Limit Object to set Limit based on Privilege, Business Function and Legal Entity
  • Loading branch information
Jatin-Patel-30 authored Aug 31, 2023
2 parents f44e163 + 463826d commit 77d4089
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 19 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog
All notable changes to this project will be documented in this file.

## [3.60.3](https://github.com/Backbase/stream-services/compare/3.60.1...3.60.2)
### Changed
- Enhance Legal Entity level Limit Object to set Limit based on Privilege, Business Function and Legal Entity

## [3.60.2](https://github.com/Backbase/stream-services/compare/3.60.1...3.60.2)
### Changed
- Fix formatting of StreamTask error messages
Expand Down
33 changes: 31 additions & 2 deletions api/stream-legal-entity/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1335,6 +1335,35 @@ 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: <project_prefix>.<1xxx>
maxLength: 36
minLength: 1
type: string
example: "bankA.1000"
shadow:
description: shadow limit or not
type: boolean
default: false
example: true
privileges:
type: array
items:
$ref: '#/components/schemas/Privilege'
UserProfile:
title: UserProfile
description: |
Expand Down Expand Up @@ -2356,8 +2385,8 @@ components:
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.
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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,7 @@ private Flux<LegalEntity> setSubsidiaryParentLegalEntityId(LegalEntity parentLeg
private Mono<LegalEntityTask> setupLimits(LegalEntityTask streamTask) {
return Mono.just(streamTask)
.flatMap(this::setupLegalEntityLimits)
.flatMap(this::setupLegalEntityLevelBusinessFunctionLimits)
.flatMap(this::setupServiceAgreementLimits)
.flatMap(this::setupServiceAgreementParticipantLimits)
.flatMap(this::retrieveUsersInternalIds)
Expand All @@ -1002,7 +1003,7 @@ private Mono<LegalEntityTask> setupLimits(LegalEntityTask streamTask) {

private Mono<LegalEntityTask> 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);
}
Expand All @@ -1012,18 +1013,81 @@ private Mono<LegalEntityTask> 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<LegalEntityTask> 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);
}
return Flux.fromStream(legalEntity.getLimit().getBusinessFunctionLimits()
.stream()
.filter(businessFunctionLimit -> nonNull(businessFunctionLimit)
&& !CollectionUtils.isEmpty(businessFunctionLimit.getPrivileges()))
.flatMap(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<LegalEntityTask> 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 Stream<LimitsTask> createLimitsTask(LegalEntityTask streamTask, String legalEntityId, BusinessFunctionLimit businessFunctionLimit) {

if(isNull(businessFunctionLimit) || CollectionUtils.isEmpty(businessFunctionLimit.getPrivileges())){
return Stream.of();
}
return businessFunctionLimit.getPrivileges()
.stream()
.filter(privilege -> validateLimit(privilege.getLimit()))
.map(privilege -> {
var limitData = new CreateLimitRequestBody();
var entities = new ArrayList<Entity>();
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(privilege.getPrivilege())
.ifPresent(prv -> entities.add(new Entity().etype(PRIVILEGE_E_TYPE).eref(prv)));
limitData.entities(entities);
Optional.of(privilege)
.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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
import static com.github.tomakehurst.wiremock.client.WireMock.verify;


import com.backbase.stream.legalentity.model.Loan;
import com.backbase.stream.legalentity.model.*;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
Expand All @@ -18,19 +20,6 @@
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.reactive.server.WebTestClient;
import com.backbase.stream.LegalEntityTask;
import com.backbase.stream.legalentity.model.BaseProductGroup;
import com.backbase.stream.legalentity.model.CurrentAccount;
import com.backbase.stream.legalentity.model.EmailAddress;
import com.backbase.stream.legalentity.model.IdentityUserLinkStrategy;
import com.backbase.stream.legalentity.model.JobProfileUser;
import com.backbase.stream.legalentity.model.LegalEntity;
import com.backbase.stream.legalentity.model.LegalEntityParticipant;
import com.backbase.stream.legalentity.model.LegalEntityStatus;
import com.backbase.stream.legalentity.model.LegalEntityType;
import com.backbase.stream.legalentity.model.PhoneNumber;
import com.backbase.stream.legalentity.model.ProductGroup;
import com.backbase.stream.legalentity.model.ServiceAgreement;
import com.backbase.stream.legalentity.model.User;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.junit5.WireMockTest;
Expand Down Expand Up @@ -65,6 +54,7 @@ private static LegalEntityTask defaultLegalEntityTask() {
.parentExternalId("parent-100000")
.legalEntityType(LegalEntityType.CUSTOMER)
.realmName("customer-bank")
.limit(legalEntityLimit())
.productGroups(Arrays.asList(
(ProductGroup) new ProductGroup()
.productGroupType(BaseProductGroup.ProductGroupTypeEnum.ARRANGEMENTS)
Expand Down Expand Up @@ -144,6 +134,16 @@ private static LegalEntityTask defaultLegalEntityTask() {
);
}

private static Limit legalEntityLimit(){
return new Limit()
.addBusinessFunctionLimitsItem(new BusinessFunctionLimit()
.functionId("1001")
.shadow(true)
.addPrivilegesItem(new Privilege().privilege("create").limit(new Limit().daily(BigDecimal.valueOf(2500d))))
.addPrivilegesItem(new Privilege().privilege("execute").limit(new Limit().daily(BigDecimal.valueOf(2500d))))
);
}

private void setupWireMock() {
stubFor(
WireMock.post("/oauth/token")
Expand Down Expand Up @@ -291,6 +291,18 @@ private void setupWireMock() {
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.withBody(""))
);
stubFor(
WireMock.post("/limit/service-api/v2/limits/retrieval")
.willReturn(WireMock.aResponse().withStatus(HttpStatus.OK.value())
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.withBody(""))
);
stubFor(
WireMock.post("/limit/service-api/v2/limits")
.willReturn(WireMock.aResponse().withStatus(HttpStatus.CREATED.value())
.withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.withBody(""))
);
}

/**
Expand Down Expand Up @@ -374,6 +386,34 @@ void legalEntitySagaUpdateLegalEntity() {
.withHeader("X-B3-SpanId", notEmpty()));
}

@Test
void legalEntitySagaSetLimitLegalEntity() {
// Given
setupWireMock();
LegalEntityTask legalEntityTask = defaultLegalEntityTask();

// When
webTestClient.post()
.uri("/legal-entity")
.header("Content-Type", "application/json")
.header("X-TID", "tenant-id")
.bodyValue(legalEntityTask.getLegalEntity())
.exchange()
.expectStatus().isEqualTo(200);


// Then
verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/limit/service-api/v2/limits/retrieval"))
.withHeader("X-TID", WireMock.equalTo("tenant-id"))
.withHeader("X-B3-TraceId", notEmpty())
.withHeader("X-B3-SpanId", notEmpty()));

verify(WireMock.postRequestedFor(WireMock.urlEqualTo("/limit/service-api/v2/limits"))
.withHeader("X-TID", WireMock.equalTo("tenant-id"))
.withHeader("X-B3-TraceId", notEmpty())
.withHeader("X-B3-SpanId", notEmpty()));
}

static class NotEmptyPattern extends StringValuePattern {
public NotEmptyPattern(@JsonProperty("something") String expectedValue) {
super(expectedValue);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ spring:
- uri: http://localhost:10000
metadata:
contextPath: /loan
limit:
- uri: http://localhost:10000
metadata:
contextPath: /limit
kubernetes:
config:
enabled: false
Expand Down

0 comments on commit 77d4089

Please sign in to comment.