Skip to content

Commit

Permalink
PLUG-127: add psr16 cache adapter for truelayer client (#36)
Browse files Browse the repository at this point in the history
  • Loading branch information
artyom-jaksov-tl authored Oct 28, 2024
1 parent 51c447a commit 6a2ea8e
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 4 deletions.
1 change: 1 addition & 0 deletions Api/Config/System/ConnectionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ interface ConnectionInterface extends DebugInterface
public const XML_PATH_PRODUCTION_CLIENT_SECRET = 'payment/truelayer/production_client_secret';
public const XML_PATH_PRODUCTION_PRIVATE_KEY = 'payment/truelayer/production_private_key';
public const XML_PATH_PRODUCTION_KEY_ID = 'payment/truelayer/production_key_id';
public const XML_PATH_CACHE_ENCRYPTION_KEY = 'payment/truelayer/cache_encryption_key';

/**
* Get Merchant Account Name
Expand Down
3 changes: 2 additions & 1 deletion Controller/Adminhtml/Credentials/Check.php
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ private function getCredentials(): array
'client_id' => $clientId,
'client_secret' => $clientSecret,
'private_key' => $this->getPrivateKeyPath($configCredentials),
'key_id' => $keyId
'key_id' => $keyId,
'cache_encryption_key' => $configCredentials['cache_encryption_key']
]
];
}
Expand Down
29 changes: 29 additions & 0 deletions Model/Cache/CacheType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace TrueLayer\Connect\Model\Cache;

use Magento\Framework\App\Cache\Type\FrontendPool;
use Magento\Framework\Cache\Frontend\Decorator\TagScope;

class CacheType extends TagScope
{
/**
* Cache type code unique among all cache types
* @var string
*/
public const TYPE_IDENTIFIER = 'truelayer';

/**
* The tag name that limits the cache cleaning scope within a particular tag
* @var string
*/
public const CACHE_TAG = 'TRUELAYER';

public function __construct(FrontendPool $cacheFrontendPool)
{
parent::__construct(
$cacheFrontendPool->get(self::TYPE_IDENTIFIER),
self::CACHE_TAG
);
}
}
18 changes: 17 additions & 1 deletion Model/Config/System/ConnectionRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public function getCredentials(?int $storeId = null, ?bool $forceSandbox = null)
"client_id" => $this->getClientId($storeId, $isSandBox),
"client_secret" => $this->getClientSecret($storeId, $isSandBox),
"private_key" => $this->getPathToPrivateKey($storeId, $isSandBox),
"key_id" => $this->getKeyId($storeId, $isSandBox)
"key_id" => $this->getKeyId($storeId, $isSandBox),
"cache_encryption_key" => $this->getCacheEncryptionKey($storeId)
];
}

Expand Down Expand Up @@ -103,4 +104,19 @@ private function getClientSecret(?int $storeId = null, bool $isSandBox = false):

return '';
}

/**
* @param int|null $storeId
*
* @return string
*/
private function getCacheEncryptionKey(?int $storeId = null): ?string
{
$path = self::XML_PATH_CACHE_ENCRYPTION_KEY;
$value = $this->getStoreValue($path, $storeId);
if (!empty($value)) {
return $this->encryptor->decrypt($value);
}
return null;
}
}
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ bin/magento cache:flush
bin/magento setup:static-content:deploy
```
7. After the installation, go to your Magento® admin portal and open ‘Stores’ > ‘Configuration’ > ‘Sales’ > ‘TrueLayer’.
8. It's recommended that you also enable the cache for TrueLayer. There's two ways you can do this.
1. In your Magento® admin portal open ‘System‘ > ‘Cache Management‘, click the checkbox for TrueLayer, select ‘Enable‘ from ‘Actions‘, and click ‘Submit‘.
2. On your server running Magento® 2 run the following command from the command line: `bin/magento cache:enable truelayer`

# Local development
A basic docker-compose configuration is provided to make local development easier. To start it, run the following:
Expand Down
11 changes: 11 additions & 0 deletions Service/Cache/InvalidArgumentException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace TrueLayer\Connect\Service\Cache;

class InvalidArgumentException extends \InvalidArgumentException implements \Psr\Cache\InvalidArgumentException
{
public function __construct(string $message)
{
parent::__construct($message);
}
}
125 changes: 125 additions & 0 deletions Service/Cache/Psr16CacheAdapter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php

namespace TrueLayer\Connect\Service\Cache;

use Psr\SimpleCache\CacheInterface;
use TrueLayer\Connect\Model\Cache\CacheType;
use TrueLayer\Connect\Service\Cache\InvalidArgumentException;

class Psr16CacheAdapter implements CacheInterface
{
private CacheType $cacheFrontend;

public function __construct(CacheType $cacheFrontend)
{
$this->cacheFrontend = $cacheFrontend;
}

/**
* {@inheritdoc}
*/
public function get(string $key, mixed $default = null): mixed
{
$item = $this->cacheFrontend->load($key);

if ($item === false) {
return $default;
}

return unserialize($item);
}

/**
* {@inheritdoc}
*/
public function set(string $key, $value, $ttl = null): bool
{
$value = serialize($value);
return $this->cacheFrontend->save(
$value,
$key,
[CacheType::CACHE_TAG],
$ttl
);
}

/**
* {@inheritdoc}
*/
public function delete($key): bool
{
return $this->cacheFrontend->remove($key);
}

/**
* {@inheritdoc}
*/
public function clear(): bool
{
return $this->cacheFrontend->clean();
}

/**
* {@inheritdoc}
*/
public function getMultiple(iterable $keys, mixed $default = null): iterable
{
if ($keys instanceof \Traversable) {
$keys = iterator_to_array($keys, false);
}
$values = [];
foreach ($keys as $key) {
$values[$key] = $this->get($key, $default);
}
return $values;
}

/**
* {@inheritdoc}
* @param iterable<int|string, mixed> $values
*/
public function setMultiple(iterable $values, null|int|\DateInterval $ttl = null): bool
{
$stringKeyedValues = [];
foreach ($values as $key => $value) {
if (is_int($key)) {
$key = (string) $key;
}

if (!is_string($key)) {
throw new InvalidArgumentException(sprintf('Cache key must be string, "%s" given', gettype($key)));
}

$stringKeyedValues[$key] = $value;
}
$success = true;
foreach ($stringKeyedValues as $key => $value) {
$success = $this->set($key, $value, $ttl) && $success;
}

return $success;
}

/**
* {@inheritdoc}
*/
public function deleteMultiple(iterable $keys): bool
{
if ($keys instanceof \Traversable) {
$keys = iterator_to_array($keys, false);
}
$success = true;
foreach ($keys as $key) {
$success = $this->delete($key) && $success;
}
return $success;
}

/**
* {@inheritdoc}
*/
public function has(string $key): bool
{
return $this->cacheFrontend->test($key);
}
}
10 changes: 10 additions & 0 deletions Service/Client/ClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use TrueLayer\Client;
use TrueLayer\Connect\Api\Config\RepositoryInterface as ConfigRepository;
use TrueLayer\Connect\Api\Log\LogServiceInterface;
use TrueLayer\Connect\Service\Cache\Psr16CacheAdapter;
use TrueLayer\Exceptions\InvalidArgumentException;
use TrueLayer\Exceptions\SignerException;
use TrueLayer\Interfaces\Client\ClientInterface;
Expand All @@ -21,6 +22,7 @@ class ClientFactory
{
private ConfigRepository $configProvider;
private LogServiceInterface $logger;
private Psr16CacheAdapter $cacheAdapter;

/**
* @param ConfigRepository $configProvider
Expand All @@ -29,9 +31,11 @@ class ClientFactory
public function __construct(
ConfigRepository $configProvider,
LogServiceInterface $logger,
Psr16CacheAdapter $cacheAdapter,
) {
$this->configProvider = $configProvider;
$this->logger = $logger;
$this->cacheAdapter = $cacheAdapter;
}

/**
Expand Down Expand Up @@ -64,13 +68,19 @@ private function createClient(array $credentials, ?bool $forceSandbox = null): ?
{
Settings::tlAgent('truelayer-magento/' . $this->configProvider->getExtensionVersion());

$cacheEncryptionKey = $credentials['cache_encryption_key'];

$clientFactory = Client::configure();
$clientFactory->clientId($credentials['client_id'])
->clientSecret($credentials['client_secret'])
->keyId($credentials['key_id'])
->pemFile($credentials['private_key'])
->useProduction(is_null($forceSandbox) ? !$this->configProvider->isSandbox() : !$forceSandbox);

if ($cacheEncryptionKey) {
$clientFactory->cache($this->cacheAdapter, $cacheEncryptionKey);
}

return $clientFactory->create();
}
}
30 changes: 30 additions & 0 deletions Setup/Recurring.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace TrueLayer\Connect\Setup;

use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Config\Model\ResourceModel\Config;
use TrueLayer\Connect\Api\Config\RepositoryInterface;
use TrueLayer\Connect\Api\Config\System\ConnectionInterface;
use Magento\Framework\Encryption\EncryptorInterface;

class Recurring implements InstallSchemaInterface
{
public function __construct(private RepositoryInterface $configRepository, private EncryptorInterface $encryptor, private Config $resourceConfig)
{

}
public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
$setup->startSetup();

$path = ConnectionInterface::XML_PATH_CACHE_ENCRYPTION_KEY;
$value = bin2hex(openssl_random_pseudo_bytes(32));
$value = $this->encryptor->encrypt($value);
$this->resourceConfig->saveConfig($path, $value, 'default', 0);

$setup->endSetup();
}
}
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
"magento/module-payment": ">=100.1.0",
"magento/module-checkout": ">=100.1.0",
"magento/module-sales": ">=100.1.0",
"truelayer/client": ">=1.2.0",
"php": ">=7.4.0"
"truelayer/client": ">=2.6.0",
"php": ">=8.1.0"
},
"require-dev": {
"phpstan/phpstan": "*",
Expand Down
7 changes: 7 additions & 0 deletions etc/cache.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Cache/etc/cache.xsd">
<type name="truelayer" translate="label,description" instance="TrueLayer\Connect\Model\Cache\CacheType">
<label>Truelayer</label>
<description>Cache used by TrueLayer Plugin</description>
</type>
</config>

0 comments on commit 6a2ea8e

Please sign in to comment.