Skip to content

Commit

Permalink
🧹 optimise GoCardless bank sync to use fewer api calls (#436)
Browse files Browse the repository at this point in the history
* don't fetch balance from gocardless unless it's needed

* release note

* refactor into common function

* Update src/app-gocardless/app-gocardless.js

Co-authored-by: Matiss Janis Aboltins <[email protected]>

---------

Co-authored-by: Matiss Janis Aboltins <[email protected]>
  • Loading branch information
matt-fidd and MatissJanis authored Aug 26, 2024
1 parent 2b37d5a commit d613a6b
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 33 deletions.
74 changes: 52 additions & 22 deletions src/app-gocardless/app-gocardless.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,34 +142,64 @@ app.post(
app.post(
'/transactions',
handleError(async (req, res) => {
const { requisitionId, startDate, endDate, accountId } = req.body;
const {
requisitionId,
startDate,
endDate,
accountId,
includeBalance = true,
} = req.body;

try {
const {
balances,
institutionId,
startingBalance,
transactions: { booked, pending, all },
} = await goCardlessService.getTransactionsWithBalance(
requisitionId,
accountId,
startDate,
endDate,
);

res.send({
status: 'ok',
data: {
if (includeBalance) {
const {
balances,
institutionId,
startingBalance,
transactions: {
booked,
pending,
all,
transactions: { booked, pending, all },
} = await goCardlessService.getTransactionsWithBalance(
requisitionId,
accountId,
startDate,
endDate,
);

res.send({
status: 'ok',
data: {
balances,
institutionId,
startingBalance,
transactions: {
booked,
pending,
all,
},
},
},
});
});
} else {
const {
institutionId,
transactions: { booked, pending, all },
} = await goCardlessService.getNormalizedTransactions(
requisitionId,
accountId,
startDate,
endDate,
);

res.send({
status: 'ok',
data: {
institutionId,
transactions: {
booked,
pending,
all,
},
},
});
}
} catch (error) {
const sendErrorResponse = (data) =>
res.send({ status: 'ok', data: { ...data, details: error.details } });
Expand Down
70 changes: 59 additions & 11 deletions src/app-gocardless/services/gocardless-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,16 +206,71 @@ export const goCardlessService = {
throw new AccountNotLinedToRequisition(accountId, requisitionId);
}

const [transactions, accountBalance] = await Promise.all([
goCardlessService.getTransactions({
institutionId: institution_id,
const [normalizedTransactions, accountBalance] = await Promise.all([
goCardlessService.getNormalizedTransactions(
requisitionId,
accountId,
startDate,
endDate,
}),
),
goCardlessService.getBalances(accountId),
]);

const transactions = normalizedTransactions.transactions;

const bank = BankFactory(institution_id);

const startingBalance = bank.calculateStartingBalance(
transactions.booked,
accountBalance.balances,
);

return {
balances: accountBalance.balances,
institutionId: institution_id,
startingBalance,
transactions,
};
},

/**
*
* @param requisitionId
* @param accountId
* @param startDate
* @param endDate
* @throws {AccountNotLinedToRequisition} Will throw an error if requisition not includes provided account id
* @throws {RequisitionNotLinked} Will throw an error if requisition is not in Linked
* @throws {InvalidInputDataError}
* @throws {InvalidGoCardlessTokenError}
* @throws {AccessDeniedError}
* @throws {NotFoundError}
* @throws {ResourceSuspended}
* @throws {RateLimitError}
* @throws {UnknownError}
* @throws {ServiceError}
* @returns {Promise<{institutionId: string, transactions: {booked: Array<import('../gocardless-node.types.js').Transaction>, pending: Array<import('../gocardless-node.types.js').Transaction>, all: Array<import('../gocardless.types.js').TransactionWithBookedStatus>}}>}
*/
getNormalizedTransactions: async (
requisitionId,
accountId,
startDate,
endDate,
) => {
const { institution_id, accounts: accountIds } =
await goCardlessService.getLinkedRequisition(requisitionId);

if (!accountIds.includes(accountId)) {
throw new AccountNotLinedToRequisition(accountId, requisitionId);
}

const transactions = await goCardlessService.getTransactions({
institutionId: institution_id,
accountId,
startDate,
endDate,
});

const bank = BankFactory(institution_id);
const sortedBookedTransactions = bank.sortTransactions(
transactions.transactions?.booked,
Expand All @@ -231,15 +286,8 @@ export const goCardlessService = {
);
const sortedAllTransactions = bank.sortTransactions(allTransactions);

const startingBalance = bank.calculateStartingBalance(
sortedBookedTransactions,
accountBalance.balances,
);

return {
balances: accountBalance.balances,
institutionId: institution_id,
startingBalance,
transactions: {
booked: sortedBookedTransactions,
pending: sortedPendingTransactions,
Expand Down
6 changes: 6 additions & 0 deletions upcoming-release-notes/436.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [matt-fidd]
---

Optimise GoCardless sync to reduce API usage by removing balance information

0 comments on commit d613a6b

Please sign in to comment.