Skip to content

Commit

Permalink
[Feature/Fix docker config]: Pull from bachelor (#58)
Browse files Browse the repository at this point in the history
* Add tests for nordigen synchronization process
* Composer update & change docker config
* Update docker config
* Fix docker config
  • Loading branch information
michalmytych authored Dec 31, 2023
1 parent 4fdaaef commit 4349ba7
Show file tree
Hide file tree
Showing 33 changed files with 1,106 additions and 708 deletions.
4 changes: 2 additions & 2 deletions app/Http/Client/Traits/DecodesHttpJsonResponse.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
namespace App\Http\Client\Traits;

use Throwable;
use GuzzleHttp\Psr7\Response;
use Psr\Http\Message\ResponseInterface;

trait DecodesHttpJsonResponse
{
protected function decodedResponse(Response $response): array
protected function decodedResponse(ResponseInterface $response): array
{
$contents = $response
->getBody()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use App\Models\Transaction\PersonalAccount;
use App\Contracts\Infrastructure\Cache\CacheAdapterInterface;

class PersonalAccountController extends Controller
{
public function __construct(private readonly CacheAdapterInterface $cacheAdapter)
{
}

public function index(Request $request): View
{
$personalAccounts = PersonalAccount::whereUser($request->user())
Expand All @@ -36,13 +41,17 @@ public function update(Request $request): RedirectResponse
{
// @todo - handle editing multiplte personal account saldos
$personalAccount = $request->user()->personalAccounts()->first();

$request->validate([
'value' => 'numeric|gte:0'
]);

$personalAccount->update([
'value' => $request->input('value')
]);

$this->cacheAdapter->clearUserCache($request->user());

return to_route('home');
}
}
2 changes: 1 addition & 1 deletion app/Http/Requests/Web/File/FileUploadRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public function rules(): array
return [
'import_setting_id' => 'exists:import_settings,id|exclude_if:type,' . File::USER_AVATAR,
'columns_mapping_id' => 'exists:columns_mappings,id|exclude_if:type,' . File::USER_AVATAR,
'file' => 'required|mimes:jpeg|max:6000',
'file' => 'required|max:6000',
'type' => 'string'
];
}
Expand Down
1 change: 1 addition & 0 deletions app/Models/File.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ class File extends Model
use HasFactory, BelongsToUser;

public const USER_AVATAR = 'USER_AVATAR';
public const TRANSACTIONS_IMPORT = 'TRANSACTIONS_IMPORT';

protected $fillable = [
'name',
Expand Down
1 change: 1 addition & 0 deletions app/Models/Nordigen/Requisition.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* @method static latest()
* @method static whereUser($user)
* @property mixed $nordigen_requisition_id
* @property mixed $id
*/
class Requisition extends Model
{
Expand Down
8 changes: 8 additions & 0 deletions app/Models/Synchronization/Account.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
namespace App\Models\Synchronization;

use App\Models\Traits\BelongsToUser;
use App\Models\Transaction\PersonalAccount;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasOne;

/**
* @method static create(array $array)
Expand All @@ -14,6 +16,7 @@
* @method static whereUser(User $user)
* @property int $id
* @property string $nordigen_account_id
* @property PersonalAccount|null $personalAccount
*/
class Account extends Model
{
Expand All @@ -24,4 +27,9 @@ class Account extends Model
'nordigen_account_id',
'synchronization_id'
];

public function personalAccount(): HasOne
{
return $this->hasOne(PersonalAccount::class);
}
}
4 changes: 4 additions & 0 deletions app/Models/Transaction/PersonalAccount.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,17 @@

/**
* @property mixed $value
* @property int $id
* @method static whereUser(mixed $user)
* @method static firstOrCreate(array $attributes, array $data)
*/
class PersonalAccount extends Model
{
use HasFactory, BelongsToUser;

protected $fillable = [
'external_reference',
'account_id',
'user_id',
'value',
'name'
Expand Down
8 changes: 4 additions & 4 deletions app/Services/File/FileUploadService.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ public function upload(User $user, UploadedFile $file, array $data): void

try {
$this->transactionFileService->uploadTransactions(
$file,
$data['import_setting_id'],
$data['columns_mapping_id'],
$user
requestFile: $file,
importSettingId: $data['import_setting_id'],
columnMappingId: $data['columns_mapping_id'],
user: $user
);

} catch(\Throwable) {
Expand Down
4 changes: 2 additions & 2 deletions app/Services/File/TransactionFileService.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ public function uploadTransactions(mixed $requestFile, mixed $importSettingId, m

$fileModel = new File();
$fileModel->user_id = $user->id;
$fileModel->name = $file->getClientOriginalName();
$fileModel->path = "uploads/{$fileName}";
$fileModel->size = $file->getSize();
$fileModel->path = "uploads/{$fileName}";
$fileModel->import_setting_id = $importSettingId;
$fileModel->name = $file->getClientOriginalName();

$this->registerAndStoreFile($fileModel, $file, $fileName);

Expand Down
9 changes: 6 additions & 3 deletions app/Services/Import/ImportService.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,19 @@ public function importFromFile(int $fileId, int $importSettingId, int $columnsMa

$import = new Import([
'user_id' => $user->id,
'file_id' => $file->id,
'status' => Import::STATUS_PROCESSING,
'columns_mapping_id' => $columnsMappingId,
'import_setting_id' => $importSettingId,
'file_id' => $file->id,
'columns_mapping_id' => $columnsMappingId,
]);

$import->save();

DB::transaction(function () use ($file, $importSetting, $columnsMapping, $import, $user) {
Excel::import(new TransactionsImport($importSetting, $columnsMapping, $import, $user), $file->path);
Excel::queueImport(
new TransactionsImport($importSetting, $columnsMapping, $import, $user),
$file->path
);
});

$import->update(['status' => Import::STATUS_SAVED]);
Expand Down
4 changes: 4 additions & 0 deletions app/Services/Nordigen/DataObjects/TransactionDataObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public function __construct(

/** @var string|null $receiverAccountNumber Receiver IBAN Number */
public ?string $receiverAccountNumber,

/** @var int $personalAccountId Personal account id. */
public int $personalAccountId,
)
{
}
Expand All @@ -49,6 +52,7 @@ public static function make(mixed $data): self
remittanceInformationUnstructured: data_get($data, 'remittanceInformationUnstructured'),
senderAccountNumber: data_get($data, 'debtorAccount.iban'),
receiverAccountNumber: data_get($data, 'creditorAccount.iban'),
personalAccountId: data_get($data, 'personalAccountId'),
);
}
}
41 changes: 39 additions & 2 deletions app/Services/Nordigen/NordigenClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,45 @@
namespace App\Services\Nordigen;

use App\Http\Client\Client;
use Illuminate\Support\Facades\Log;
use Psr\Http\Message\ResponseInterface;

class NordigenClient extends Client
class NordigenClient extends Client implements NordigenClientInterface
{
//
public function request(string $method, $uri = '', array $options = []): ResponseInterface
{
$response = parent::request($method, $uri, $options);

$this->log(
[
'uri' => $uri,
'method' => $method,
'options' => $options
],
$response
);

return $response;
}

public function log(array $clientParameters, ResponseInterface $response): void
{
$log = [
'type' => '[NordigenClient Requests Log]',
'time' => time(),
'meta' => [
'client_parameters' => $clientParameters,
'response' => [
'status_code' => $response->getStatusCode(),
'body' => $response->getBody(),
'headers' => $response->getHeaders(),
'protocol_version' => $response->getProtocolVersion()
]
]
];

Log::debug(
json_encode($log)
);
}
}
8 changes: 8 additions & 0 deletions app/Services/Nordigen/NordigenClientInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace App\Services\Nordigen;

interface NordigenClientInterface
{
//
}
61 changes: 46 additions & 15 deletions app/Services/Nordigen/NordigenService.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Services\Nordigen;


use Exception;
use Throwable;
use App\Models\User;
Expand All @@ -11,11 +12,12 @@
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use App\Models\Nordigen\Requisition;
use App\Models\Synchronization\Account;
use GuzzleHttp\Exception\GuzzleException;
use App\Models\Nordigen\EndUserAgreement;
use App\Models\Transaction\PersonalAccount;
use App\Models\Synchronization\Synchronization;
use App\Http\Client\Traits\DecodesHttpJsonResponse;
use App\Models\Synchronization\Account as NordigenAccount;
use App\Services\Nordigen\DataObjects\InstitutionDataObject;
use App\Services\Nordigen\DataObjects\TransactionDataObject;
use App\Contracts\Infrastructure\Cache\CacheAdapterInterface;
Expand Down Expand Up @@ -43,7 +45,7 @@ class NordigenService implements TransactionSyncServiceInterface
private const REQUISITION_CREATED_STATUS = 'CR';

public function __construct(
private readonly NordigenClient $httpClient,
private readonly NordigenClientInterface $httpClient,
private readonly CacheAdapterInterface $cacheAdapter,
private readonly NordigenAccountService $nordigenAccountService,
private readonly NordigenTransactionServiceInterface $nordigenTransactionService
Expand Down Expand Up @@ -78,11 +80,33 @@ public function syncTransactions(mixed $requisitionId, mixed $synchronizationId,
}
}

/**
* @throws GuzzleException
*/
public function getAccountBalance(NordigenAccount $account): int|float|null
{
$uri = self::ACCOUNTS_URI . $account->nordigen_account_id . '/balances/';

$response = $this->httpClient->get($uri, [
'headers' => $this->getAuthorizationHeader(),
]);

$balancesData = $this->decodedResponse($response);
$balances = data_get($balancesData, 'balances');

$availableBalance = array_filter(
$balances,
fn(array $balance) => $balance['balanceType'] === 'forwardAvailable'
);

return data_get($availableBalance, 'balanceAmount.amount');
}

/**
* @throws GuzzleException
* @throws Exception
*/
protected function syncTransactionsByAccount(Account $account, Import $import, User $user): void
protected function syncTransactionsByAccount(NordigenAccount $account, Import $import, User $user): void
{
$uri = self::ACCOUNTS_URI . $account->nordigen_account_id . '/transactions/';

Expand All @@ -91,6 +115,8 @@ protected function syncTransactionsByAccount(Account $account, Import $import, U
]);

$transactionsData = $this->decodedResponse($response);
$personalAccountId = $account->refresh()->personalAccount?->id;
$transactionsData['personalAccountId'] = $personalAccountId;

if (data_get($transactionsData, 'transactions')) {
$booked = data_get($transactionsData, 'transactions.booked');
Expand All @@ -100,23 +126,14 @@ protected function syncTransactionsByAccount(Account $account, Import $import, U

foreach ($all as $transactionData) {
$transactionDataObject = TransactionDataObject::make($transactionData);
$this->nordigenTransactionService->addNewSynchronizedTransaction(
$transactionDataObject,
$import,
$user
);
$this->nordigenTransactionService->addNewSynchronizedTransaction($transactionDataObject, $import, $user);
}
} else {
Log::debug(json_encode($transactionsData));
throw new Exception('Invalid transaction data received. Response was logged to debug logs.');
}
}

public function getAccounts(User $user): Collection
{
return Account::whereUser($user)->latest()->get();
}

/**
* @throws GuzzleException
* @throws Exception
Expand All @@ -134,15 +151,29 @@ public function syncAccounts(mixed $requisitionId, mixed $synchronizationId, Use

if (is_array($accountsIds)) {
foreach ($accountsIds as $accountId) {
// @todo add deleting non existing accounts
Account::firstOrCreate([
$account = NordigenAccount::firstOrCreate([
'user_id' => $user->id,
'nordigen_account_id' => $accountId,
], [
'user_id' => $user->id,
'nordigen_account_id' => $accountId,
'synchronization_id' => $synchronizationId,
]);

$institutionId = data_get($accountsData, 'institution_id');
$accountReference = data_get($accountsData, 'reference');

$accountBalance = $this->getAccountBalance($account);

PersonalAccount::firstOrCreate([
'user_id' => $user->id,
'external_reference' => $accountReference
], [
'user_id' => $user->id,
'nordigen_account_id' => $account->id,
'name' => $institutionId . ' ' . $accountReference,
'value' => $accountBalance
]);
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion app/Services/Nordigen/Provider/NordigenServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Services\Nordigen\Provider;

use App\Services\Nordigen\NordigenClient;
use App\Services\Nordigen\NordigenClientInterface;
use App\Services\Nordigen\Synchronization\NordigenTransactionServiceInterface;
use App\Services\Transaction\TransactionSyncService;
use Illuminate\Support\ServiceProvider;
Expand All @@ -11,7 +12,7 @@ class NordigenServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(NordigenClient::class, function ($app) {
$this->app->singleton(NordigenClientInterface::class, function ($app) {
return new NordigenClient([
'base_uri' => $app->config->get('nordigen.base_uri'),
'headers' => $app->config->get('nordigen.headers'),
Expand Down
Loading

0 comments on commit 4349ba7

Please sign in to comment.