Refactored transaction controller #21

Merged
inga-lovinde merged 2 commits from refactor-10-refactor-transaction-controller into main 10 months ago
  1. 11
      src/shared/rules.ts
  2. 60
      src/transaction/transaction.controller.spec.ts
  3. 79
      src/transaction/transaction.controller.ts

@ -0,0 +1,11 @@
import _ from 'lodash';
type RuleResult = number | false | undefined;
export const getMinimumRuleOutput = async <T>(
rules: ((input: T) => RuleResult | Promise<RuleResult>)[],
input: T,
) => {
const results = await Promise.all(rules.map((rule) => rule(input)));
return Math.min(..._.compact(results));
};

@ -71,91 +71,55 @@ describe('TransactionController Unit Tests', () => {
it('calling applyRules method minimum result of applied rules should be got', async () => { it('calling applyRules method minimum result of applied rules should be got', async () => {
{ {
const result = await transactionController.applyRules( const result = await transactionController.applyRules({
[
transactionController.discountRule,
transactionController.turnoverRule,
],
{
base_amount: 1000, base_amount: 1000,
client_id: 1, client_id: 1,
}, });
);
expect(result).to.eql(5); expect(result).to.eql(5);
} }
{ {
const result = await transactionController.applyRules( const result = await transactionController.applyRules({
[
transactionController.discountRule,
transactionController.turnoverRule,
],
{
base_amount: 1, base_amount: 1,
client_id: 1, client_id: 1,
}, });
);
expect(result).to.eql(0.05); expect(result).to.eql(0.05);
} }
{ {
const result = await transactionController.applyRules( const result = await transactionController.applyRules({
[
transactionController.discountRule,
transactionController.turnoverRule,
],
{
base_amount: 1000, base_amount: 1000,
client_id: 42, client_id: 42,
}, });
);
expect(result).to.eql(0.03); expect(result).to.eql(0.03);
} }
{ {
const result = await transactionController.applyRules( const result = await transactionController.applyRules({
[
transactionController.discountRule,
transactionController.turnoverRule,
],
{
base_amount: 1, base_amount: 1,
client_id: 42, client_id: 42,
}, });
);
expect(result).to.eql(0.03); expect(result).to.eql(0.03);
} }
{ {
const result = await transactionController.applyRules( const result = await transactionController.applyRules({
[
transactionController.discountRule,
transactionController.turnoverRule,
],
{
base_amount: 1000, base_amount: 1000,
client_id: 99, client_id: 99,
}, });
);
expect(result).to.eql(0.03); expect(result).to.eql(0.03);
} }
{ {
const result = await transactionController.applyRules( const result = await transactionController.applyRules({
[
transactionController.discountRule,
transactionController.turnoverRule,
],
{
base_amount: 1, base_amount: 1,
client_id: 99, client_id: 99,
}, });
);
expect(result).to.eql(0.03); expect(result).to.eql(0.03);
} }

@ -1,5 +1,4 @@
import { Controller, Post, UsePipes, Body } from '@nestjs/common'; import { Controller, Post, UsePipes, Body } from '@nestjs/common';
import _ from 'lodash';
import { import {
ExchangeRateInput, ExchangeRateInput,
ExchangeRateResponse, ExchangeRateResponse,
@ -17,6 +16,7 @@ import {
HighTurnoverDiscount, HighTurnoverDiscount,
} from './transaction.dto'; } from './transaction.dto';
import { Transaction } from './transaction.entity'; import { Transaction } from './transaction.entity';
import { getMinimumRuleOutput } from 'src/shared/rules';
type TransactionRuleData = Pick<Transaction, 'base_amount' | 'client_id'>; type TransactionRuleData = Pick<Transaction, 'base_amount' | 'client_id'>;
@ -33,10 +33,7 @@ export class TransactionController {
@Body() transactionInput: TransactionInput, @Body() transactionInput: TransactionInput,
): Promise<string> { ): Promise<string> {
return JSON.stringify({ return JSON.stringify({
amount: amount: (await this.getCommissionAmount(transactionInput)).toFixed(2),
transactionInput.currency !== Currency.EUR
? (await this.getAmountWithExchange(transactionInput)).toFixed(2)
: (await this.getAmountWithoutExchange(transactionInput)).toFixed(2),
currency: Currency.EUR, currency: Currency.EUR,
}); });
} }
@ -90,19 +87,14 @@ export class TransactionController {
: commissionAmount; : commissionAmount;
} }
async applyRules( async applyRules(transactionInput: TransactionRuleData) {
rules: (( return getMinimumRuleOutput(
transactionInput: TransactionRuleData, [this.turnoverRule, this.discountRule, this.defaultRule],
) => number | false | undefined | Promise<number | false | undefined>)[], transactionInput,
transactionInput: TransactionRuleData,
) {
const commissions = await Promise.all(
[...rules, this.defaultRule].map((rule) => rule(transactionInput)),
); );
return Math.min(..._.compact(commissions));
} }
getExchangeRate(exchangeRateInput: ExchangeRateInput) { getExchangeRateResponse(exchangeRateInput: ExchangeRateInput) {
return new Promise<ExchangeRateResponse>((resolve, reject) => { return new Promise<ExchangeRateResponse>((resolve, reject) => {
try { try {
this.exchangeRateService.convertCurrency(exchangeRateInput).subscribe({ this.exchangeRateService.convertCurrency(exchangeRateInput).subscribe({
@ -115,11 +107,21 @@ export class TransactionController {
}); });
} }
async getAmountWithExchange(transactionInput: TransactionInput) { async getExchangeRateIfNeeded(transactionInput: TransactionInput) {
const exchangeRateResponse = await this.getExchangeRate({ if (transactionInput.currency === Currency.EUR) {
return 1;
}
const exchangeRateResponse = await this.getExchangeRateResponse({
date: transactionInput.date, date: transactionInput.date,
}); });
return exchangeRateResponse[transactionInput.currency];
}
async getCommissionAmount(transactionInput: TransactionInput) {
const exchangeRate = await this.getExchangeRateIfNeeded(transactionInput);
const transactionAmount = parseInt(transactionInput.amount); const transactionAmount = parseInt(transactionInput.amount);
const transactionData = { const transactionData = {
date: transactionInput.date, date: transactionInput.date,
@ -127,53 +129,18 @@ export class TransactionController {
currency: transactionInput.currency, currency: transactionInput.currency,
client_id: transactionInput.client_id, client_id: transactionInput.client_id,
base_currency: Currency.EUR, base_currency: Currency.EUR,
base_amount: base_amount: transactionAmount / exchangeRate,
transactionAmount / exchangeRateResponse[transactionInput.currency],
}; };
const commissionAmount = this.applyRules( const commission = await this.applyRules(transactionData);
[this.turnoverRule, this.discountRule],
transactionData,
);
commissionAmount
.then((commission) =>
this.transactionService.insertOne({
...transactionData,
commission,
}),
)
.catch((error) => console.log(error));
return commissionAmount;
}
async getAmountWithoutExchange(transactionInput: TransactionInput) {
const transactionData = {
date: transactionInput.date,
amount: parseInt(transactionInput.amount),
currency: transactionInput.currency,
client_id: transactionInput.client_id,
base_currency: Currency.EUR,
base_amount: parseInt(transactionInput.amount),
};
const commissionAmount = await this.applyRules(
[this.turnoverRule, this.discountRule],
transactionData,
);
try {
this.transactionService this.transactionService
.insertOne({ .insertOne({
...transactionData, ...transactionData,
commission: commissionAmount, commission,
}) })
.catch((error) => console.log(error)); .catch((error) => console.log(error));
} catch (error) {
console.log(error);
throw error;
}
return commissionAmount; return commission;
} }
} }

Loading…
Cancel
Save