1
0
mirror of synced 2024-11-21 12:26:04 +03:00

component skeleton for managing base product schemas, rename Client to TopClient to avoid collisions with PSR-18 clients

This commit is contained in:
Pavel 2020-10-02 18:01:52 +03:00
parent 12410f5a40
commit 341585c523
10 changed files with 292 additions and 61 deletions

View File

@ -16,12 +16,12 @@ Details about those third-party libraries and why you need to install them can b
2. Instantiate client like that:
```php
use RetailCrm\Component\AppData;
use RetailCrm\Builder\ClientBuilder;
use RetailCrm\Builder\TopClientBuilder;
use RetailCrm\Builder\ContainerBuilder;
use RetailCrm\Component\Authenticator\TokenAuthenticator;
$appData = new AppData(AppData::OVERSEAS_ENDPOINT, 'appKey', 'appSecret');
$client = ClientBuilder::create()
$client = TopClientBuilder::create()
->setContainer(ContainerBuilder::create()->build())
->setAppData($appData)
->setAuthenticator(new TokenAuthenticator('session token here'))
@ -35,12 +35,12 @@ use RetailCrm\Model\Request\Taobao\HttpDnsGetRequest;
$request = new HttpDnsGetRequest();
```
4. Send request using `Client::sendRequest` or `Client::sendAuthenticatedRequest` (you can't send authenticated request using client without authenticator). `taobao.httpdns.get` can be sent like this:
4. Send request using `TopClient::sendRequest` or `TopClient::sendAuthenticatedRequest` (you can't send authenticated request using client without authenticator). `taobao.httpdns.get` can be sent like this:
```php
/** @var \RetailCrm\Model\Response\Taobao\HttpDnsGetResponse $response */
$response = $client->sendRequest(new HttpDnsGetRequest());
```
This particular request doesn't require authorization, so, it can be sent via `Client::sendRequest` method. For any other requests which require authorization you must use `Client::sendAuthenticatedRequest` method (an example of such request would be `aliexpress.solution.seller.category.tree.query`, which class FQN is `\RetailCrm\Model\Request\AliExpress\SolutionSellerCategoryTreeQuery`).
This particular request doesn't require authorization, so, it can be sent via `TopClient::sendRequest` method. For any other requests which require authorization you must use `TopClient::sendAuthenticatedRequest` method (an example of such request would be `aliexpress.solution.seller.category.tree.query`, which class FQN is `\RetailCrm\Model\Request\AliExpress\SolutionSellerCategoryTreeQuery`).
**Friendly note.** Use response type annotations. Both client methods which returns responses actually returns `ResponseInterface` (not the PSR one). Actual response type will be determined by the request model. Your IDE will not recognize any response options unless you put a proper type annotation for the response variable.
**Another friendly note.**
@ -49,17 +49,18 @@ This particular request doesn't require authorization, so, it can be sent via `C
This library uses Container pattern under the hood. You can pass additional dependencies using `ContainerBuilder`. For example:
```php
use Http\Client\Curl\Client;
use Nyholm\Psr7\Factory\Psr17Factory;
use RetailCrm\Component\AppData;
use RetailCrm\Component\Environment;
use RetailCrm\Component\Logger\StdoutLogger;
use RetailCrm\Builder\ClientBuilder;
use Nyholm\Psr7\Factory\Psr17Factory;
use RetailCrm\Builder\TopClientBuilder;
use RetailCrm\Builder\ContainerBuilder;
use RetailCrm\Component\Logger\StdoutLogger;
use RetailCrm\Component\Authenticator\TokenAuthenticator;
$client = new Client();
$logger = new StdoutLogger();
$factory = new Psr17Factory();
$authenticator = new TokenAuthenticator('appKey', 'token');
$authenticator = new TokenAuthenticator('token');
$appData = new AppData(AppData::OVERSEAS_ENDPOINT, 'appKey', 'appSecret');
$container = ContainerBuilder::create()
->setEnv(Environment::TEST)
@ -69,11 +70,11 @@ $container = ContainerBuilder::create()
->setRequestFactory($factory)
->setUriFactory($factory)
->build();
$client = ClientBuilder::create()
$client = TopClientBuilder::create()
->setContainer($container)
->setAppData($appData)
->build();
```
Logger should implement `Psr\Log\LoggerInterface` (PSR-3), HTTP client should implement `Psr\Http\Client\ClientInterface` (PSR-18), HTTP objects must be compliant to PSR-7.
Logger should implement `Psr\Log\LoggerInterface` (PSR-3), HTTP client should implement `Psr\Http\TopClient\TopClientInterface` (PSR-18), HTTP objects must be compliant to PSR-7.
You can use your own container - it must be compliant to PSR-11. This is strongly discouraged because it'll be much easier to just integrate library with your own application, and your own DI system.

View File

@ -20,19 +20,11 @@
<rule ref="rulesets/codesize.xml">
<exclude name="TooManyPublicMethods" />
</rule>
<rule ref="rulesets/design.xml">
<exclude name="CouplingBetweenObjects" />
</rule>
<rule ref="rulesets/codesize.xml/TooManyPublicMethods">
<properties>
<property name="maxmethods" value="15" />
</properties>
</rule>
<rule ref="rulesets/design.xml/CouplingBetweenObjects">
<properties>
<property name="maximum" value="20" />
</properties>
</rule>
<rule ref="rulesets/naming.xml/ShortVariable">
<properties>
<property name="minimum" value="2" />

View File

@ -28,6 +28,7 @@ use RetailCrm\Component\DependencyInjection\Container;
use RetailCrm\Component\Environment;
use RetailCrm\Component\ServiceLocator;
use RetailCrm\Factory\FileItemFactory;
use RetailCrm\Factory\ProductSchemaStorageFactory;
use RetailCrm\Factory\SerializerFactory;
use RetailCrm\Factory\TopRequestFactory;
use RetailCrm\Interfaces\BuilderInterface;
@ -223,6 +224,9 @@ class ContainerBuilder implements BuilderInterface
$container->set(FileItemFactoryInterface::class, function (ContainerInterface $container) {
return new FileItemFactory($container->get(StreamFactoryInterface::class));
});
$container->set(ProductSchemaStorageFactory::class, function (ContainerInterface $container) {
return new ProductSchemaStorageFactory($container->get(Constants::CACHE));
});
$container->set(RequestDataFilter::class, new RequestDataFilter());
$container->set(RequestSignerInterface::class, function (ContainerInterface $container) {
return new RequestSigner(

View File

@ -3,7 +3,7 @@
/**
* PHP version 7.3
*
* @category ClientBuilder
* @category TopClientBuilder
* @package RetailCrm\Builder
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT https://mit-license.org
@ -14,26 +14,28 @@ namespace RetailCrm\Builder;
use RetailCrm\Component\Constants;
use RetailCrm\Component\ServiceLocator;
use RetailCrm\Component\Storage\ProductSchemaStorage;
use RetailCrm\Factory\ProductSchemaStorageFactory;
use RetailCrm\Interfaces\AppDataInterface;
use RetailCrm\Interfaces\AuthenticatorInterface;
use RetailCrm\Interfaces\BuilderInterface;
use RetailCrm\Interfaces\ContainerAwareInterface;
use RetailCrm\Interfaces\TopRequestFactoryInterface;
use RetailCrm\Interfaces\TopRequestProcessorInterface;
use RetailCrm\TopClient\Client;
use RetailCrm\TopClient\TopClient;
use RetailCrm\Traits\ContainerAwareTrait;
/**
* Class ClientBuilder
* Class TopClientBuilder
*
* @category ClientBuilder
* @category TopClientBuilder
* @package RetailCrm\Builder
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT https://mit-license.org
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class ClientBuilder implements ContainerAwareInterface, BuilderInterface
class TopClientBuilder implements ContainerAwareInterface, BuilderInterface
{
use ContainerAwareTrait;
@ -54,9 +56,9 @@ class ClientBuilder implements ContainerAwareInterface, BuilderInterface
/**
* @param \RetailCrm\Interfaces\AppDataInterface $appData
*
* @return ClientBuilder
* @return TopClientBuilder
*/
public function setAppData(AppDataInterface $appData): ClientBuilder
public function setAppData(AppDataInterface $appData): TopClientBuilder
{
$this->appData = $appData;
return $this;
@ -65,27 +67,28 @@ class ClientBuilder implements ContainerAwareInterface, BuilderInterface
/**
* @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator
*
* @return ClientBuilder
* @return TopClientBuilder
*/
public function setAuthenticator(AuthenticatorInterface $authenticator): ClientBuilder
public function setAuthenticator(AuthenticatorInterface $authenticator): TopClientBuilder
{
$this->authenticator = $authenticator;
return $this;
}
/**
* @return \RetailCrm\TopClient\Client
* @return \RetailCrm\TopClient\TopClient
* @throws \RetailCrm\Component\Exception\ValidationException
*/
public function build(): Client
public function build(): TopClient
{
$client = new Client($this->appData);
$client = new TopClient($this->appData);
$client->setHttpClient($this->container->get(Constants::HTTP_CLIENT));
$client->setSerializer($this->container->get(Constants::SERIALIZER));
$client->setValidator($this->container->get(Constants::VALIDATOR));
$client->setRequestFactory($this->container->get(TopRequestFactoryInterface::class));
$client->setServiceLocator($this->container->get(ServiceLocator::class));
$client->setProcessor($this->container->get(TopRequestProcessorInterface::class));
$client->setProductSchemaStorageFactory($this->container->get(ProductSchemaStorageFactory::class));
if (null !== $this->authenticator) {
$client->setAuthenticator($this->authenticator);

View File

@ -0,0 +1,59 @@
<?php
/**
* PHP version 7.3
*
* @category ProductSchemaStorage
* @package RetailCrm\Component\Storage
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Component\Storage;
use Psr\Cache\CacheItemPoolInterface;
use RetailCrm\Interfaces\TopClientInterface;
/**
* Class ProductSchemaStorage
*
* @category ProductSchemaStorage
* @package RetailCrm\Component\Storage
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license https://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class ProductSchemaStorage
{
/** @var TopClientInterface */
private $client;
/** @var CacheItemPoolInterface $cache */
private $cache;
/**
* ProductSchemaStorage constructor.
*
* @param \RetailCrm\Interfaces\TopClientInterface $client
* @param \Psr\Cache\CacheItemPoolInterface $cache
*/
public function __construct(TopClientInterface $client, CacheItemPoolInterface $cache)
{
$this->client = $client;
$this->cache = $cache;
}
/**
* Returns product schema (updates it automatically if it's TTL has expired)
*
* @param int $ttl in seconds
*
* @return string
*/
public function getProductSchema(int $ttl = 86400): string
{
// TODO: Implement product schema fetching and refreshing.
}
}

View File

@ -0,0 +1,66 @@
<?php
/**
* PHP version 7.3
*
* @category ProductSchemaStorageFactory
* @package RetailCrm\Factory
* @author RetailCRM <integration@retailcrm.ru>
* @license http://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Factory;
use Psr\Cache\CacheItemPoolInterface;
use RetailCrm\Component\Storage\ProductSchemaStorage;
use RetailCrm\Interfaces\FactoryInterface;
use RetailCrm\Interfaces\TopClientInterface;
/**
* Class ProductSchemaStorageFactory
*
* @category ProductSchemaStorageFactory
* @package RetailCrm\Factory
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license https://retailcrm.ru Proprietary
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class ProductSchemaStorageFactory implements FactoryInterface
{
/** @var CacheItemPoolInterface $cache */
private $cache;
/** @var TopClientInterface $client */
private $client;
/**
* ProductSchemaStorageFactory constructor.
*
* @param \Psr\Cache\CacheItemPoolInterface $cache
*/
public function __construct(CacheItemPoolInterface $cache)
{
$this->cache = $cache;
}
/**
* @param \RetailCrm\Interfaces\TopClientInterface $client
*
* @return ProductSchemaStorageFactory
*/
public function setClient(TopClientInterface $client): ProductSchemaStorageFactory
{
$this->client = $client;
return $this;
}
/**
* @inheritDoc
*/
public function create()
{
return new ProductSchemaStorage($this->client, $this->cache);
}
}

View File

@ -0,0 +1,80 @@
<?php
/**
* PHP version 7.3
*
* @category TopClientInterface
* @package RetailCrm\Interfaces
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT https://mit-license.org
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Interfaces;
use RetailCrm\Component\ServiceLocator;
use RetailCrm\Model\Request\BaseRequest;
use RetailCrm\Model\Response\TopResponseInterface;
/**
* Class TopClientInterface
*
* @category ContainerBuilder
* @package RetailCrm\Interfaces
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT https://mit-license.org
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*
*/
interface TopClientInterface
{
/**
* @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator
*
* @return TopClientInterface
*/
public function setAuthenticator(AuthenticatorInterface $authenticator);
/**
* @return \RetailCrm\Component\ServiceLocator
*/
public function getServiceLocator(): ServiceLocator;
/**
* @param bool $withState
*
* @return BuilderInterface
*
* $withState is passed to AuthorizationUriBuilder.
* @see AuthorizationUriBuilder::__construct
* @SuppressWarnings(PHPMD.BooleanArgumentFlag)
*/
public function getAuthorizationUriBuilder(bool $withState = false): BuilderInterface;
/**
* Send TOP request
*
* @param \RetailCrm\Model\Request\BaseRequest $request
*
* @return TopResponseInterface
* @throws \RetailCrm\Component\Exception\ValidationException
* @throws \RetailCrm\Component\Exception\FactoryException
* @throws \RetailCrm\Component\Exception\TopClientException
* @throws \RetailCrm\Component\Exception\TopApiException
*/
public function sendRequest(BaseRequest $request): TopResponseInterface;
/**
* Send authenticated TOP request
*
* @param \RetailCrm\Model\Request\BaseRequest $request
*
* @return \RetailCrm\Model\Response\TopResponseInterface
* @throws \RetailCrm\Component\Exception\FactoryException
* @throws \RetailCrm\Component\Exception\TopApiException
* @throws \RetailCrm\Component\Exception\TopClientException
* @throws \RetailCrm\Component\Exception\ValidationException
*/
public function sendAuthenticatedRequest(BaseRequest $request): TopResponseInterface;
}

View File

@ -3,7 +3,7 @@
/**
* PHP version 7.3
*
* @category Client
* @category TopClient
* @package RetailCrm\TopClient
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT https://mit-license.org
@ -12,9 +12,7 @@
*/
namespace RetailCrm\TopClient;
use DateTime;
use JMS\Serializer\SerializerInterface;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Http\Client\ClientExceptionInterface;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\StreamInterface;
@ -22,8 +20,12 @@ use RetailCrm\Builder\AuthorizationUriBuilder;
use RetailCrm\Component\Exception\TopApiException;
use RetailCrm\Component\Exception\TopClientException;
use RetailCrm\Component\ServiceLocator;
use RetailCrm\Component\Storage\ProductSchemaStorage;
use RetailCrm\Factory\ProductSchemaStorageFactory;
use RetailCrm\Interfaces\AppDataInterface;
use RetailCrm\Interfaces\AuthenticatorInterface;
use RetailCrm\Interfaces\BuilderInterface;
use RetailCrm\Interfaces\TopClientInterface;
use RetailCrm\Interfaces\TopRequestFactoryInterface;
use RetailCrm\Interfaces\TopRequestProcessorInterface;
use RetailCrm\Model\Request\BaseRequest;
@ -33,16 +35,17 @@ use RetailCrm\Traits\ValidatorAwareTrait;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Class Client
* Class TopClient
*
* @category Client
* @category TopClient
* @package RetailCrm\TopClient
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT https://mit-license.org
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class Client
class TopClient implements TopClientInterface
{
use ValidatorAwareTrait;
@ -90,7 +93,12 @@ class Client
protected $authenticator;
/**
* Client constructor.
* @var ProductSchemaStorageFactory $productSchemaStorageFactory
*/
protected $productSchemaStorageFactory;
/**
* TopClient constructor.
*
* @param \RetailCrm\Interfaces\AppDataInterface $appData
*/
@ -142,9 +150,9 @@ class Client
/**
* @param \RetailCrm\Interfaces\TopRequestProcessorInterface $processor
*
* @return Client
* @return TopClient
*/
public function setProcessor(TopRequestProcessorInterface $processor): Client
public function setProcessor(TopRequestProcessorInterface $processor): TopClient
{
$this->processor = $processor;
return $this;
@ -153,14 +161,25 @@ class Client
/**
* @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator
*
* @return Client
* @return TopClient
*/
public function setAuthenticator(AuthenticatorInterface $authenticator): Client
public function setAuthenticator(AuthenticatorInterface $authenticator): TopClient
{
$this->authenticator = $authenticator;
return $this;
}
/**
* @param \RetailCrm\Factory\ProductSchemaStorageFactory $productSchemaStorageFactory
*
* @return TopClient
*/
public function setProductSchemaStorageFactory(ProductSchemaStorageFactory $productSchemaStorageFactory): TopClient
{
$this->productSchemaStorageFactory = $productSchemaStorageFactory;
return $this;
}
/**
* @return \RetailCrm\Component\ServiceLocator
*/
@ -172,16 +191,23 @@ class Client
/**
* @param bool $withState
*
* @return string
* @return BuilderInterface
*
* $withState is passed to AuthorizationUriBuilder.
* @see AuthorizationUriBuilder::__construct
* @SuppressWarnings(PHPMD.BooleanArgumentFlag)
*/
public function getAuthorizationUri(bool $withState = false): string
public function getAuthorizationUriBuilder(bool $withState = false): BuilderInterface
{
$builder = new AuthorizationUriBuilder($this->appData->getAppKey(), $this->appData->getAppSecret(), $withState);
return $builder->build();
return new AuthorizationUriBuilder($this->appData->getAppKey(), $this->appData->getAppSecret(), $withState);
}
/**
* @return \RetailCrm\Component\Storage\ProductSchemaStorage
*/
public function getProductSchemaStorage(): ProductSchemaStorage
{
return $this->productSchemaStorageFactory->setClient($this)->create();
}
/**
@ -198,7 +224,7 @@ class Client
public function sendRequest(BaseRequest $request): TopResponseInterface
{
if ('json' !== $request->format) {
throw new TopClientException(sprintf('Client only supports JSON mode, got `%s` mode', $request->format));
throw new TopClientException(sprintf('TopClient only supports JSON mode, got `%s` mode', $request->format));
}
$this->processor->process($request, $this->appData);

View File

@ -14,9 +14,9 @@ namespace RetailCrm\Tests\Builder;
use RetailCrm\Component\AppData;
use RetailCrm\Component\ServiceLocator;
use RetailCrm\Builder\ClientBuilder;
use RetailCrm\Builder\TopClientBuilder;
use RetailCrm\Test\TestCase;
use RetailCrm\TopClient\Client;
use RetailCrm\TopClient\TopClient;
/**
* Class ClientBuilderTest
@ -32,12 +32,12 @@ class ClientBuilderTest extends TestCase
{
public function testCreateClient()
{
$client = ClientBuilder::create()
$client = TopClientBuilder::create()
->setContainer($this->getContainer())
->setAppData(new AppData(AppData::OVERSEAS_ENDPOINT, 'appKey', 'helloworld'))
->build();
self::assertInstanceOf(Client::class, $client);
self::assertInstanceOf(TopClient::class, $client);
self::assertInstanceOf(ServiceLocator::class, $client->getServiceLocator());
}
}

View File

@ -14,7 +14,7 @@ namespace RetailCrm\Tests\TopClient;
use Http\Message\RequestMatcher\CallbackRequestMatcher;
use Psr\Http\Message\RequestInterface;
use RetailCrm\Builder\ClientBuilder;
use RetailCrm\Builder\TopClientBuilder;
use RetailCrm\Model\Entity\CategoryInfo;
use RetailCrm\Model\Enum\FeedOperationTypes;
use RetailCrm\Model\Enum\FeedStatuses;
@ -66,7 +66,7 @@ class ClientTest extends TestCase
return true;
}), $this->responseJson(400, $errorResponse));
$client = ClientBuilder::create()
$client = TopClientBuilder::create()
->setContainer($this->getContainer($mockClient))
->setAppData($this->getEnvAppData())
->build();
@ -78,7 +78,7 @@ class ClientTest extends TestCase
public function testClientRequestXmlUnsupported()
{
$client = ClientBuilder::create()
$client = TopClientBuilder::create()
->setContainer($this->getContainer(self::getMockClient()))
->setAppData($this->getEnvAppData())
->build();
@ -86,7 +86,7 @@ class ClientTest extends TestCase
$request = new HttpDnsGetRequest();
$request->format = 'xml';
$this->expectExceptionMessage('Client only supports JSON mode, got `xml` mode');
$this->expectExceptionMessage('TopClient only supports JSON mode, got `xml` mode');
$client->sendRequest($request);
}
@ -135,7 +135,7 @@ EOF;
]),
$this->responseJson(200, $json)
);
$client = ClientBuilder::create()
$client = TopClientBuilder::create()
->setContainer($this->getContainer($mock))
->setAppData($this->getEnvAppData())
->setAuthenticator($this->getEnvTokenAuthenticator())
@ -200,7 +200,7 @@ EOF;
]),
$this->responseJson(200, $json)
);
$client = ClientBuilder::create()
$client = TopClientBuilder::create()
->setContainer($this->getContainer($mock))
->setAppData($this->getEnvAppData())
->setAuthenticator($this->getEnvTokenAuthenticator())
@ -253,7 +253,7 @@ EOF;
]),
$this->responseJson(200, $json)
);
$client = ClientBuilder::create()
$client = TopClientBuilder::create()
->setContainer($this->getContainer($mock))
->setAppData($this->getEnvAppData())
->setAuthenticator($this->getEnvTokenAuthenticator())
@ -296,7 +296,7 @@ EOF;
]),
$this->responseJson(200, $json)
);
$client = ClientBuilder::create()
$client = TopClientBuilder::create()
->setContainer($this->getContainer($mock))
->setAppData($this->getEnvAppData())
->setAuthenticator($this->getEnvTokenAuthenticator())
@ -347,7 +347,7 @@ EOF;
]),
$this->responseJson(200, $json)
);
$client = ClientBuilder::create()
$client = TopClientBuilder::create()
->setContainer($this->getContainer($mock))
->setAppData($this->getEnvAppData())
->setAuthenticator($this->getEnvTokenAuthenticator())
@ -404,7 +404,7 @@ EOF;
]),
$this->responseJson(200, $json)
);
$client = ClientBuilder::create()
$client = TopClientBuilder::create()
->setContainer($this->getContainer($mock))
->setAppData($this->getEnvAppData())
->setAuthenticator($this->getEnvTokenAuthenticator())
@ -451,7 +451,7 @@ EOF;
]),
$this->responseJson(200, $json)
);
$client = ClientBuilder::create()
$client = TopClientBuilder::create()
->setContainer($this->getContainer($mock))
->setAppData($this->getEnvAppData())
->setAuthenticator($this->getEnvTokenAuthenticator())