1
0
mirror of synced 2024-11-22 12:56:02 +03:00

WIP: Hacks for inline JSON string inside another JSON or inside XML (some responses actually look like that)

This commit is contained in:
Pavel 2020-09-30 17:50:44 +03:00
parent efd71698ca
commit 9b081e2e7f
26 changed files with 407 additions and 206 deletions

View File

@ -18,14 +18,11 @@ Details about those third-party libraries and why you need to install them can b
use RetailCrm\Component\AppData; use RetailCrm\Component\AppData;
use RetailCrm\Builder\ClientBuilder; use RetailCrm\Builder\ClientBuilder;
use RetailCrm\Builder\ContainerBuilder; use RetailCrm\Builder\ContainerBuilder;
use RetailCrm\Component\Authenticator\TokenAuthenticator;
$authenticator = new TokenAuthenticator('appKey', 'token');
$appData = new AppData(AppData::OVERSEAS_ENDPOINT, 'appKey', 'appSecret'); $appData = new AppData(AppData::OVERSEAS_ENDPOINT, 'appKey', 'appSecret');
$client = ClientBuilder::create() $client = ClientBuilder::create()
->setContainer(ContainerBuilder::create()->build()) ->setContainer(ContainerBuilder::create()->build())
->setAppData($appData) ->setAppData($appData)
->setAuthenticator($authenticator)
->build(); ->build();
``` ```
@ -39,7 +36,6 @@ use RetailCrm\Component\Environment;
use RetailCrm\Component\Logger\StdoutLogger; use RetailCrm\Component\Logger\StdoutLogger;
use RetailCrm\Builder\ClientBuilder; use RetailCrm\Builder\ClientBuilder;
use RetailCrm\Builder\ContainerBuilder; use RetailCrm\Builder\ContainerBuilder;
use RetailCrm\Component\Authenticator\TokenAuthenticator;
$client = new Client(); $client = new Client();
$logger = new StdoutLogger(); $logger = new StdoutLogger();
@ -57,7 +53,8 @@ $container = ContainerBuilder::create()
$client = ClientBuilder::create() $client = ClientBuilder::create()
->setContainer($container) ->setContainer($container)
->setAppData($appData) ->setAppData($appData)
->setAuthenticator($authenticator)
->build(); ->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\Client\ClientInterface` (PSR-18), HTTP objects must be compliant to PSR-7.
You can use your own container - it must be compliant to PSR-11. But 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

@ -15,7 +15,6 @@ namespace RetailCrm\Builder;
use RetailCrm\Component\Constants; use RetailCrm\Component\Constants;
use RetailCrm\Component\ServiceLocator; use RetailCrm\Component\ServiceLocator;
use RetailCrm\Interfaces\AppDataInterface; use RetailCrm\Interfaces\AppDataInterface;
use RetailCrm\Interfaces\AuthenticatorInterface;
use RetailCrm\Interfaces\BuilderInterface; use RetailCrm\Interfaces\BuilderInterface;
use RetailCrm\Interfaces\ContainerAwareInterface; use RetailCrm\Interfaces\ContainerAwareInterface;
use RetailCrm\Interfaces\TopRequestFactoryInterface; use RetailCrm\Interfaces\TopRequestFactoryInterface;
@ -40,9 +39,6 @@ class ClientBuilder implements ContainerAwareInterface, BuilderInterface
/** @var \RetailCrm\Interfaces\AppDataInterface $appData */ /** @var \RetailCrm\Interfaces\AppDataInterface $appData */
private $appData; private $appData;
/*** @var AuthenticatorInterface $authenticator */
protected $authenticator;
/** /**
* @return static * @return static
*/ */
@ -62,24 +58,13 @@ class ClientBuilder implements ContainerAwareInterface, BuilderInterface
return $this; return $this;
} }
/**
* @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator
*
* @return $this
*/
public function setAuthenticator(AuthenticatorInterface $authenticator): self
{
$this->authenticator = $authenticator;
return $this;
}
/** /**
* @return \RetailCrm\TopClient\Client * @return \RetailCrm\TopClient\Client
* @throws \RetailCrm\Component\Exception\ValidationException * @throws \RetailCrm\Component\Exception\ValidationException
*/ */
public function build(): Client public function build(): Client
{ {
$client = new Client($this->appData, $this->authenticator); $client = new Client($this->appData);
$client->setHttpClient($this->container->get(Constants::HTTP_CLIENT)); $client->setHttpClient($this->container->get(Constants::HTTP_CLIENT));
$client->setSerializer($this->container->get(Constants::SERIALIZER)); $client->setSerializer($this->container->get(Constants::SERIALIZER));
$client->setValidator($this->container->get(Constants::VALIDATOR)); $client->setValidator($this->container->get(Constants::VALIDATOR));

View File

@ -1,60 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category TokenAuthenticator
* @package RetailCrm\Component\Authenticator
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT https://mit-license.org
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Component\Authenticator;
use RetailCrm\Interfaces\AuthenticatorInterface;
use RetailCrm\Model\Request\BaseRequest;
/**
* Class TokenAuthenticator
*
* @category TokenAuthenticator
* @package RetailCrm\Component\Authenticator
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT https://mit-license.org
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class TokenAuthenticator implements AuthenticatorInterface
{
/**
* @var string $appKey
*/
private $appKey;
/**
* @var string $token
*/
private $token;
/**
* TokenAuthenticator constructor.
*
* @param string $appKey
* @param string $token
*/
public function __construct(string $appKey, string $token)
{
$this->appKey = $appKey;
$this->token = $token;
}
/**
* @param \RetailCrm\Model\Request\BaseRequest $request
*/
public function authenticate(BaseRequest $request): void
{
$request->appKey = $this->appKey;
$request->session = $this->token;
}
}

View File

@ -42,20 +42,21 @@ class TopApiException extends Exception
* TopApiException constructor. * TopApiException constructor.
* *
* @param \RetailCrm\Model\Response\Body\ErrorResponseBody $responseBody * @param \RetailCrm\Model\Response\Body\ErrorResponseBody $responseBody
* @param string|null $requestId
* @param \Throwable|null $previous * @param \Throwable|null $previous
*/ */
public function __construct(ErrorResponseBody $responseBody, Throwable $previous = null) public function __construct(ErrorResponseBody $responseBody, ?string $requestId, Throwable $previous = null)
{ {
parent::__construct($responseBody->msg, $responseBody->code, $previous); parent::__construct($responseBody->msg, $responseBody->code, $previous);
$this->subCode = $responseBody->subCode; $this->subCode = $responseBody->subCode;
$this->requestId = $responseBody->requestId; $this->requestId = $requestId;
} }
/** /**
* @return string * @return string
*/ */
public function getSubCode(): string public function getSubCode(): ?string
{ {
return $this->subCode; return $this->subCode;
} }
@ -63,7 +64,7 @@ class TopApiException extends Exception
/** /**
* @return string * @return string
*/ */
public function getRequestId(): string public function getRequestId(): ?string
{ {
return $this->requestId; return $this->requestId;
} }

View File

@ -0,0 +1,201 @@
<?php
/**
* PHP version 7.3
*
* @category InlineJsonBodyHandler
* @package RetailCrm\Component\Serializer
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Component\Serializer;
use JMS\Serializer\Context;
use JMS\Serializer\GraphNavigator;
use JMS\Serializer\GraphNavigatorInterface;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\JsonSerializationVisitor;
use JMS\Serializer\Metadata\PropertyMetadata;
use JMS\Serializer\Metadata\StaticPropertyMetadata;
use JMS\Serializer\Visitor\DeserializationVisitorInterface;
use JMS\Serializer\Visitor\SerializationVisitorInterface;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ReflectionClass;
/**
* Class InlineJsonBodyHandler
*
* @category InlineJsonBodyHandler
* @package RetailCrm\Component\Serializer
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
* @todo Doesn't work as expected.
*/
class InlineJsonBodyHandler implements SubscribingHandlerInterface
{
/**
* @param \JMS\Serializer\Visitor\SerializationVisitorInterface $visitor
* @param mixed $item
* @param array $type
* @param \JMS\Serializer\Context $context
*
* @return false|string
* @throws \JsonException
* @throws \ReflectionException
*/
public function serialize(SerializationVisitorInterface $visitor, $item, array $type, Context $context)
{
$typeName = $type['name'];
$classMetadata = $context->getMetadataFactory()->getMetadataForClass($typeName);
$reflection = new ReflectionClass($type['name']);
$visitor->startVisitingObject($classMetadata, $item, ['name' => $typeName]);
foreach ($reflection->getProperties() as $property) {
if ($property->isStatic()) {
continue;
}
if (!$property->isPublic()) {
$property->setAccessible(true);
}
$value = $property->getValue($item);
$metadata = new StaticPropertyMetadata($type['name'], $property->getName(), $value);
$visitor->visitProperty($metadata, $value);
}
return json_encode(
$visitor->endVisitingObject($classMetadata, $item, ['name' => $typeName]),
JSON_THROW_ON_ERROR
);
}
/**
* @param \JMS\Serializer\Visitor\DeserializationVisitorInterface $visitor
* @param string $json
* @param array $type
* @param \JMS\Serializer\Context $context
*
* @return object
* @throws \JsonException
* @throws \ReflectionException
*/
public function deserialize(DeserializationVisitorInterface $visitor, $json, array $type, Context $context)
{
$typeName = $type['name'];
$instance = new $type['name'];
$classMetadata = $context->getMetadataFactory()->getMetadataForClass($typeName);
$reflection = new ReflectionClass($type['name']);
$jsonData = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
$visitor->startVisitingObject($classMetadata, $instance, ['name' => $typeName]);
foreach ($reflection->getProperties() as $property) {
if ($property->isStatic()) {
continue;
}
if (!$property->isPublic()) {
$property->setAccessible(true);
}
$metadata = new PropertyMetadata($type['name'], $property->getName());
$property->setValue($instance, $visitor->visitProperty($metadata, $jsonData));
}
$result = $visitor->endVisitingObject($classMetadata, $instance, ['name' => $typeName]);
return $visitor->getResult($result);
}
/**
* @return array
*/
public static function getSubscribingMethods()
{
$methods = [];
foreach (self::getInlineJsonBodyModels() as $type) {
$methods[] = [
'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
'format' => 'json',
'type' => $type,
'method' => 'serialize',
];
$methods[] = [
'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION,
'format' => 'json',
'type' => $type,
'method' => 'deserialize',
];
$methods[] = [
'direction' => GraphNavigatorInterface::DIRECTION_SERIALIZATION,
'format' => 'xml',
'type' => $type,
'method' => 'serialize',
];
$methods[] = [
'direction' => GraphNavigatorInterface::DIRECTION_DESERIALIZATION,
'format' => 'xml',
'type' => $type,
'method' => 'deserialize',
];
}
return $methods;
}
/**
* @return array
* @todo That's horrifying. Maybe find better solution?
*/
private static function getInlineJsonBodyModels(): array
{
$items = [];
$rootDir = realpath(dirname(__DIR__) . '/..');
$directory = new RecursiveDirectoryIterator(
__DIR__ . '/../../Model/Response/Body/InlineJsonBody',
RecursiveDirectoryIterator::SKIP_DOTS
);
$fileIterator = new RecursiveIteratorIterator($directory, RecursiveIteratorIterator::LEAVES_ONLY);
/** @var \SplFileObject $file */
foreach ($fileIterator as $file) {
if ('php' !== $file->getExtension()) {
continue;
}
$items[] = self::pathToClasspath($rootDir, $file->getPath(), $file->getFilename());
}
return $items;
}
/**
* @param string $root
* @param string $path
* @param string $fileName
*
* @return string
*/
private static function pathToClasspath(string $root, string $path, string $fileName): string
{
return 'RetailCrm\\' .
str_replace(
DIRECTORY_SEPARATOR,
'\\',
str_replace(
$root . DIRECTORY_SEPARATOR,
'',
realpath($path)
)
) . '\\' . trim(substr($fileName, 0, -4));
}
}

View File

@ -19,6 +19,7 @@ use JMS\Serializer\SerializerBuilder;
use JMS\Serializer\SerializerInterface; use JMS\Serializer\SerializerInterface;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use RetailCrm\Component\Constants; use RetailCrm\Component\Constants;
use RetailCrm\Component\Serializer\InlineJsonBodyHandler;
use RetailCrm\Interfaces\FactoryInterface; use RetailCrm\Interfaces\FactoryInterface;
/** /**
@ -73,17 +74,17 @@ class SerializerFactory implements FactoryInterface
$returnSame = function ($visitor, $obj, array $type) { $returnSame = function ($visitor, $obj, array $type) {
return $obj; return $obj;
}; };
$serializeJson = function ($visitor, $obj, array $type) use ($container) {
/** @var SerializerInterface $serializer */
$serializer = $container->get(Constants::SERIALIZER);
return $serializer->serialize($obj, 'json');
};
$registry->registerHandler( $registry->registerHandler(
GraphNavigatorInterface::DIRECTION_SERIALIZATION, GraphNavigatorInterface::DIRECTION_SERIALIZATION,
'RequestDtoInterface', 'RequestDtoInterface',
'json', 'json',
function ($visitor, $obj, array $type) use ($container) { $serializeJson
/** @var SerializerInterface $serializer */
$serializer = $container->get(Constants::SERIALIZER);
return $serializer->serialize($obj, 'json');
}
); );
$registry->registerHandler( $registry->registerHandler(
GraphNavigatorInterface::DIRECTION_DESERIALIZATION, GraphNavigatorInterface::DIRECTION_DESERIALIZATION,
@ -132,6 +133,7 @@ class SerializerFactory implements FactoryInterface
'xml', 'xml',
$returnNull $returnNull
); );
$registry->registerSubscribingHandler(new InlineJsonBodyHandler());
})->addDefaultHandlers() })->addDefaultHandlers()
->setSerializationContextFactory(new SerializationContextFactory()) ->setSerializationContextFactory(new SerializationContextFactory())
->build(); ->build();

View File

@ -20,7 +20,6 @@ use Psr\Http\Message\StreamFactoryInterface;
use Psr\Http\Message\UriFactoryInterface; use Psr\Http\Message\UriFactoryInterface;
use RetailCrm\Component\Exception\FactoryException; use RetailCrm\Component\Exception\FactoryException;
use RetailCrm\Interfaces\AppDataInterface; use RetailCrm\Interfaces\AppDataInterface;
use RetailCrm\Interfaces\AuthenticatorInterface;
use RetailCrm\Interfaces\FileItemInterface; use RetailCrm\Interfaces\FileItemInterface;
use RetailCrm\Interfaces\TopRequestFactoryInterface; use RetailCrm\Interfaces\TopRequestFactoryInterface;
use RetailCrm\Model\Request\BaseRequest; use RetailCrm\Model\Request\BaseRequest;
@ -123,19 +122,19 @@ class TopRequestFactory implements TopRequestFactoryInterface
/** /**
* @param \RetailCrm\Model\Request\BaseRequest $request * @param \RetailCrm\Model\Request\BaseRequest $request
* @param \RetailCrm\Interfaces\AppDataInterface $appData * @param \RetailCrm\Interfaces\AppDataInterface $appData
* @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator
* *
* @return \Psr\Http\Message\RequestInterface * @return \Psr\Http\Message\RequestInterface
* @throws \RetailCrm\Component\Exception\FactoryException * @throws \RetailCrm\Component\Exception\FactoryException
*/ */
public function fromModel( public function fromModel(
BaseRequest $request, BaseRequest $request,
AppDataInterface $appData, AppDataInterface $appData
AuthenticatorInterface $authenticator
): RequestInterface { ): RequestInterface {
$requestData = $this->serializer->toArray($request); $requestData = $this->serializer->toArray($request);
$requestHasBinaryData = $this->filter->hasBinaryFromRequestData($requestData); $requestHasBinaryData = $this->filter->hasBinaryFromRequestData($requestData);
ksort($requestData);
if (empty($requestData)) { if (empty($requestData)) {
throw new FactoryException('Empty request data'); throw new FactoryException('Empty request data');
} }

View File

@ -1,34 +0,0 @@
<?php
/**
* PHP version 7.3
*
* @category AuthenticatorInterface
* @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\Model\Request\BaseRequest;
/**
* Interface AuthenticatorInterface
*
* @category AuthenticatorInterface
* @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 AuthenticatorInterface
{
/**
* @param \RetailCrm\Model\Request\BaseRequest $request
*/
public function authenticate(BaseRequest $request): void;
}

View File

@ -31,7 +31,6 @@ interface TopRequestFactoryInterface
/** /**
* @param \RetailCrm\Model\Request\BaseRequest $request * @param \RetailCrm\Model\Request\BaseRequest $request
* @param \RetailCrm\Interfaces\AppDataInterface $appData * @param \RetailCrm\Interfaces\AppDataInterface $appData
* @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator
* *
* @return RequestInterface * @return RequestInterface
* @throws \RetailCrm\Component\Exception\FactoryException * @throws \RetailCrm\Component\Exception\FactoryException
@ -39,7 +38,6 @@ interface TopRequestFactoryInterface
*/ */
public function fromModel( public function fromModel(
BaseRequest $request, BaseRequest $request,
AppDataInterface $appData, AppDataInterface $appData
AuthenticatorInterface $authenticator
): RequestInterface; ): RequestInterface;
} }

View File

@ -33,7 +33,6 @@ interface TopRequestProcessorInterface
* *
* @param \RetailCrm\Model\Request\BaseRequest $request * @param \RetailCrm\Model\Request\BaseRequest $request
* @param \RetailCrm\Interfaces\AppDataInterface $appData * @param \RetailCrm\Interfaces\AppDataInterface $appData
* @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator
* *
* @return void * @return void
* @throws \RetailCrm\Component\Exception\FactoryException * @throws \RetailCrm\Component\Exception\FactoryException
@ -41,7 +40,6 @@ interface TopRequestProcessorInterface
*/ */
public function process( public function process(
BaseRequest $request, BaseRequest $request,
AppDataInterface $appData, AppDataInterface $appData
AuthenticatorInterface $authenticator
): void; ): void;
} }

View File

@ -53,7 +53,7 @@ abstract class BaseRequest
* *
* @JMS\Type("string") * @JMS\Type("string")
* @JMS\SerializedName("session") * @JMS\SerializedName("session")
* @Assert\NotBlank() * @JMS\SkipWhenEmpty()
*/ */
public $session; public $session;
@ -91,6 +91,7 @@ abstract class BaseRequest
* *
* @JMS\Type("bool") * @JMS\Type("bool")
* @JMS\SerializedName("simplify") * @JMS\SerializedName("simplify")
* @JMS\Accessor(getter="isSimplify")
*/ */
public $simplify = false; public $simplify = false;
@ -131,6 +132,14 @@ abstract class BaseRequest
$this->method = $this->getMethod(); $this->method = $this->getMethod();
} }
/**
* @return bool|null
*/
public function isSimplify(): ?bool
{
return $this->simplify ? true : null;
}
/** /**
* @param string $method * @param string $method
* *

View File

@ -12,7 +12,7 @@
*/ */
namespace RetailCrm\Model\Request; namespace RetailCrm\Model\Request;
use RetailCrm\Model\Response\BaseResponse; use RetailCrm\Model\Response\HttpDnsGetResponse;
/** /**
* Class HttpDnsGetRequest * Class HttpDnsGetRequest
@ -43,7 +43,6 @@ class HttpDnsGetRequest extends BaseRequest
*/ */
public function getExpectedResponse(): string public function getExpectedResponse(): string
{ {
// TODO: Implement getExpectedResponse() method. return HttpDnsGetResponse::class;
return BaseResponse::class;
} }
} }

View File

@ -24,7 +24,7 @@ use JMS\Serializer\Annotation as JMS;
* @link http://retailcrm.ru * @link http://retailcrm.ru
* @see https://help.retailcrm.ru * @see https://help.retailcrm.ru
*/ */
class BaseResponse class BaseResponse implements TopResponseInterface
{ {
/** /**
* @var \RetailCrm\Model\Response\Body\ErrorResponseBody * @var \RetailCrm\Model\Response\Body\ErrorResponseBody
@ -33,4 +33,12 @@ class BaseResponse
* @JMS\SerializedName("error_response") * @JMS\SerializedName("error_response")
*/ */
public $errorResponse; public $errorResponse;
/**
* @var string $requestId
*
* @JMS\Type("string")
* @JMS\SerializedName("request_id")
*/
public $requestId;
} }

View File

@ -49,12 +49,4 @@ class ErrorResponseBody
* @JMS\SerializedName("sub_code") * @JMS\SerializedName("sub_code")
*/ */
public $subCode; public $subCode;
/**
* @var string $requestId
*
* @JMS\Type("string")
* @JMS\SerializedName("request_id")
*/
public $requestId;
} }

View File

@ -0,0 +1,36 @@
<?php
/**
* PHP version 7.3
*
* @category HttpDnsGetResponseResult
* @package RetailCrm\Model\Response\Body\InlineJsonBody
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Model\Response\Body\InlineJsonBody;
use JMS\Serializer\Annotation as JMS;
/**
* Class HttpDnsGetResponseResult
*
* @category HttpDnsGetResponseResult
* @package RetailCrm\Model\Response\Body\InlineJsonBody
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class HttpDnsGetResponseResult
{
/**
* @var array $env
*
* @JMS\Type("int")
* @JMS\SerializedName("env")
*/
public $env;
}

View File

@ -0,0 +1,37 @@
<?php
/**
* PHP version 7.3
*
* @category HttpDnsGetResponseData
* @package RetailCrm\Model\Response\Data
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Model\Response\Data;
use JMS\Serializer\Annotation as JMS;
use RetailCrm\Model\Response\Body\InlineJsonBody\HttpDnsGetResponseResult;
/**
* Class HttpDnsGetResponseData
*
* @category HttpDnsGetResponseData
* @package RetailCrm\Model\Response\Data
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class HttpDnsGetResponseData
{
/**
* @var HttpDnsGetResponseResult $result
*
* @JMS\Type("RetailCrm\Model\Response\Body\InlineJsonBody\HttpDnsGetResponseResult")
* @JMS\SerializedName("result")
*/
public $result;
}

View File

@ -0,0 +1,36 @@
<?php
/**
* PHP version 7.3
*
* @category HttpDnsGetResponse
* @package RetailCrm\Model\Response
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Model\Response;
use JMS\Serializer\Annotation as JMS;
/**
* Class HttpDnsGetResponse
*
* @category HttpDnsGetResponse
* @package RetailCrm\Model\Response
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
class HttpDnsGetResponse extends BaseResponse
{
/**
* @var \RetailCrm\Model\Response\Data\HttpDnsGetResponseData $responseData
*
* @JMS\Type("RetailCrm\Model\Response\Data\HttpDnsGetResponseData")
* @JMS\SerializedName("httpdns_get_response")
*/
public $responseData;
}

View File

@ -0,0 +1,28 @@
<?php
/**
* PHP version 7.3
*
* @category TopResponseInterface
* @package RetailCrm\Model\Response
* @author RetailCRM <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see http://help.retailcrm.ru
*/
namespace RetailCrm\Model\Response;
/**
* Interface TopResponseInterface
*
* @category TopResponseInterface
* @package RetailCrm\Model\Response
* @author RetailDriver LLC <integration@retailcrm.ru>
* @license MIT
* @link http://retailcrm.ru
* @see https://help.retailcrm.ru
*/
interface TopResponseInterface
{
}

View File

@ -70,9 +70,8 @@ class RequestSigner implements RequestSignerInterface
switch ($request->signMethod) { switch ($request->signMethod) {
case Constants::SIGN_TYPE_MD5: case Constants::SIGN_TYPE_MD5:
$request->sign = strtoupper(md5( $stringToBeSigned = $appData->getAppSecret() . $stringToBeSigned . $appData->getAppSecret();
$appData->getAppSecret() . $stringToBeSigned . $appData->getAppSecret() $request->sign = strtoupper(md5($stringToBeSigned));
));
break; break;
case Constants::SIGN_TYPE_HMAC: case Constants::SIGN_TYPE_HMAC:
$request->sign = strtoupper(hash_hmac('md5', $stringToBeSigned, $appData->getAppSecret())); $request->sign = strtoupper(hash_hmac('md5', $stringToBeSigned, $appData->getAppSecret()));
@ -91,7 +90,11 @@ class RequestSigner implements RequestSignerInterface
private function getDataForSigning(BaseRequest $request): array private function getDataForSigning(BaseRequest $request): array
{ {
$params = $this->filter->filterBinaryFromRequestData($this->serializer->toArray($request)); $params = $this->filter->filterBinaryFromRequestData($this->serializer->toArray($request));
unset($params['sign']); $params = array_filter(array_filter($params, static function ($val) {
return !is_array($val);
}));
unset($params['sign'], $params['session']);
ksort($params); ksort($params);
return $params; return $params;

View File

@ -13,7 +13,6 @@
namespace RetailCrm\Service; namespace RetailCrm\Service;
use RetailCrm\Interfaces\AppDataInterface; use RetailCrm\Interfaces\AppDataInterface;
use RetailCrm\Interfaces\AuthenticatorInterface;
use RetailCrm\Interfaces\RequestSignerInterface; use RetailCrm\Interfaces\RequestSignerInterface;
use RetailCrm\Interfaces\RequestTimestampProviderInterface; use RetailCrm\Interfaces\RequestTimestampProviderInterface;
use RetailCrm\Interfaces\TopRequestProcessorInterface; use RetailCrm\Interfaces\TopRequestProcessorInterface;
@ -71,12 +70,12 @@ class TopRequestProcessor implements TopRequestProcessorInterface
*/ */
public function process( public function process(
BaseRequest $request, BaseRequest $request,
AppDataInterface $appData, AppDataInterface $appData
AuthenticatorInterface $authenticator
): void { ): void {
$authenticator->authenticate($request); $request->appKey = $appData->getAppKey();
$this->signer->sign($request, $appData);
$this->timestampProvider->provide($request); $this->timestampProvider->provide($request);
$this->signer->sign($request, $appData);
$this->validate($request); $this->validate($request);
} }
} }

View File

@ -12,6 +12,7 @@
*/ */
namespace RetailCrm\TopClient; namespace RetailCrm\TopClient;
use DateTime;
use JMS\Serializer\SerializerInterface; use JMS\Serializer\SerializerInterface;
use Psr\Http\Client\ClientInterface; use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\StreamInterface; use Psr\Http\Message\StreamInterface;
@ -19,11 +20,11 @@ use RetailCrm\Component\Exception\TopApiException;
use RetailCrm\Component\Exception\TopClientException; use RetailCrm\Component\Exception\TopClientException;
use RetailCrm\Component\ServiceLocator; use RetailCrm\Component\ServiceLocator;
use RetailCrm\Interfaces\AppDataInterface; use RetailCrm\Interfaces\AppDataInterface;
use RetailCrm\Interfaces\AuthenticatorInterface;
use RetailCrm\Interfaces\TopRequestFactoryInterface; use RetailCrm\Interfaces\TopRequestFactoryInterface;
use RetailCrm\Interfaces\TopRequestProcessorInterface; use RetailCrm\Interfaces\TopRequestProcessorInterface;
use RetailCrm\Model\Request\BaseRequest; use RetailCrm\Model\Request\BaseRequest;
use RetailCrm\Model\Response\BaseResponse; use RetailCrm\Model\Response\BaseResponse;
use RetailCrm\Model\Response\TopResponseInterface;
use RetailCrm\Traits\ValidatorAwareTrait; use RetailCrm\Traits\ValidatorAwareTrait;
use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\Validator\Constraints as Assert;
@ -46,12 +47,6 @@ class Client
*/ */
protected $appData; protected $appData;
/**
* @var AuthenticatorInterface $authenticator
* @Assert\NotNull(message="Authenticator should be provided")
*/
protected $authenticator;
/** /**
* @var ClientInterface $httpClient * @var ClientInterface $httpClient
* @Assert\NotNull(message="HTTP client should be provided") * @Assert\NotNull(message="HTTP client should be provided")
@ -89,12 +84,10 @@ class Client
* Client constructor. * Client constructor.
* *
* @param \RetailCrm\Interfaces\AppDataInterface $appData * @param \RetailCrm\Interfaces\AppDataInterface $appData
* @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator
*/ */
public function __construct(AppDataInterface $appData, AuthenticatorInterface $authenticator) public function __construct(AppDataInterface $appData)
{ {
$this->appData = $appData; $this->appData = $appData;
$this->authenticator = $authenticator;
} }
/** /**
@ -159,18 +152,18 @@ class Client
/** /**
* @param \RetailCrm\Model\Request\BaseRequest $request * @param \RetailCrm\Model\Request\BaseRequest $request
* *
* @return \RetailCrm\Model\Response\BaseResponse * @return TopResponseInterface
* @throws \Psr\Http\Client\ClientExceptionInterface * @throws \Psr\Http\Client\ClientExceptionInterface
* @throws \RetailCrm\Component\Exception\ValidationException * @throws \RetailCrm\Component\Exception\ValidationException
* @throws \RetailCrm\Component\Exception\FactoryException * @throws \RetailCrm\Component\Exception\FactoryException
* @throws \RetailCrm\Component\Exception\TopClientException * @throws \RetailCrm\Component\Exception\TopClientException
* @throws \RetailCrm\Component\Exception\TopApiException * @throws \RetailCrm\Component\Exception\TopApiException
*/ */
public function sendRequest(BaseRequest $request) public function sendRequest(BaseRequest $request): TopResponseInterface
{ {
$this->processor->process($request, $this->appData, $this->authenticator); $this->processor->process($request, $this->appData);
$httpRequest = $this->requestFactory->fromModel($request, $this->appData, $this->authenticator); $httpRequest = $this->requestFactory->fromModel($request, $this->appData);
$httpResponse = $this->httpClient->sendRequest($httpRequest); $httpResponse = $this->httpClient->sendRequest($httpRequest);
/** @var BaseResponse $response */ /** @var BaseResponse $response */
@ -185,7 +178,7 @@ class Client
} }
if (null !== $response->errorResponse) { if (null !== $response->errorResponse) {
throw new TopApiException($response->errorResponse); throw new TopApiException($response->errorResponse, $response->requestId);
} }
return $response; return $response;

View File

@ -86,28 +86,6 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase
return new AppData($endpoint, $appKey, $appSecret); return new AppData($endpoint, $appKey, $appSecret);
} }
/**
* @return \RetailCrm\Interfaces\AuthenticatorInterface
*/
protected function getEnvAuthenticator(): AuthenticatorInterface
{
return $this->getAuthenticator(
self::getenv('APP_KEY', 'appKey'),
self::getenv('SESSION', 'helloworld')
);
}
/**
* @param string $appKey
* @param string $token
*
* @return \RetailCrm\Interfaces\AuthenticatorInterface
*/
protected function getAuthenticator(string $appKey = 'appKey', string $token = 'token'): AuthenticatorInterface
{
return new TokenAuthenticator($appKey, $token);
}
/** /**
* @param string $signMethod * @param string $signMethod
* *

View File

@ -13,7 +13,6 @@
namespace RetailCrm\Tests\Builder; namespace RetailCrm\Tests\Builder;
use RetailCrm\Component\AppData; use RetailCrm\Component\AppData;
use RetailCrm\Component\Authenticator\TokenAuthenticator;
use RetailCrm\Component\ServiceLocator; use RetailCrm\Component\ServiceLocator;
use RetailCrm\Builder\ClientBuilder; use RetailCrm\Builder\ClientBuilder;
use RetailCrm\Test\TestCase; use RetailCrm\Test\TestCase;
@ -36,7 +35,6 @@ class ClientBuilderTest extends TestCase
$client = ClientBuilder::create() $client = ClientBuilder::create()
->setContainer($this->getContainer()) ->setContainer($this->getContainer())
->setAppData(new AppData(AppData::OVERSEAS_ENDPOINT, 'appKey', 'helloworld')) ->setAppData(new AppData(AppData::OVERSEAS_ENDPOINT, 'appKey', 'helloworld'))
->setAuthenticator(new TokenAuthenticator('appKey', 'token'))
->build(); ->build();
self::assertInstanceOf(Client::class, $client); self::assertInstanceOf(Client::class, $client);

View File

@ -35,8 +35,7 @@ class TopRequestFactoryTest extends TestCase
$factory = $this->getContainer()->get(TopRequestFactoryInterface::class); $factory = $this->getContainer()->get(TopRequestFactoryInterface::class);
$request = $factory->fromModel( $request = $factory->fromModel(
$this->getTestRequest(Constants::SIGN_TYPE_HMAC), $this->getTestRequest(Constants::SIGN_TYPE_HMAC),
$this->getAppData(), $this->getAppData()
$this->getAuthenticator()
); );
$uri = $request->getUri(); $uri = $request->getUri();
$contents = self::getStreamData($request->getBody()); $contents = self::getStreamData($request->getBody());
@ -52,8 +51,7 @@ class TopRequestFactoryTest extends TestCase
$factory = $this->getContainer()->get(TopRequestFactoryInterface::class); $factory = $this->getContainer()->get(TopRequestFactoryInterface::class);
$request = $factory->fromModel( $request = $factory->fromModel(
$this->getTestRequest(Constants::SIGN_TYPE_HMAC, true, true), $this->getTestRequest(Constants::SIGN_TYPE_HMAC, true, true),
$this->getAppData(), $this->getAppData()
$this->getAuthenticator()
); );
$uri = $request->getUri(); $uri = $request->getUri();
$contents = self::getStreamData($request->getBody()); $contents = self::getStreamData($request->getBody());

View File

@ -16,7 +16,6 @@ use RetailCrm\Component\AppData;
use RetailCrm\Component\Constants; use RetailCrm\Component\Constants;
use RetailCrm\Interfaces\AppDataInterface; use RetailCrm\Interfaces\AppDataInterface;
use RetailCrm\Interfaces\RequestSignerInterface; use RetailCrm\Interfaces\RequestSignerInterface;
use RetailCrm\Service\RequestSigner;
use RetailCrm\Test\TestCase; use RetailCrm\Test\TestCase;
use RetailCrm\Test\TestSignerRequest; use RetailCrm\Test\TestSignerRequest;
@ -56,22 +55,22 @@ class RequestSignerTest extends TestCase
[ [
$this->getTestRequest(Constants::SIGN_TYPE_MD5), $this->getTestRequest(Constants::SIGN_TYPE_MD5),
$appData, $appData,
'4BC79C5FAA1B5E254E95A97E65BACEAB' '468BF7C95925C187D0DFD7D042072EB4'
], ],
[ [
$this->getTestRequest(Constants::SIGN_TYPE_HMAC), $this->getTestRequest(Constants::SIGN_TYPE_HMAC),
$appData, $appData,
'497FA7FCAD98F4F335EFAE2451F8291D' '5EF5C76D5C158BFFA9F35BAAA712A879'
], ],
[ [
$this->getTestRequest(Constants::SIGN_TYPE_MD5, true), $this->getTestRequest(Constants::SIGN_TYPE_MD5, true),
$appData, $appData,
'4BC79C5FAA1B5E254E95A97E65BACEAB' '468BF7C95925C187D0DFD7D042072EB4'
], ],
[ [
$this->getTestRequest(Constants::SIGN_TYPE_HMAC, true), $this->getTestRequest(Constants::SIGN_TYPE_HMAC, true),
$appData, $appData,
'497FA7FCAD98F4F335EFAE2451F8291D' '5EF5C76D5C158BFFA9F35BAAA712A879'
] ]
]; ];
} }

View File

@ -16,6 +16,8 @@ use Psr\Http\Message\RequestInterface;
use RetailCrm\Builder\ClientBuilder; use RetailCrm\Builder\ClientBuilder;
use RetailCrm\Component\AppData; use RetailCrm\Component\AppData;
use RetailCrm\Component\Authenticator\TokenAuthenticator; use RetailCrm\Component\Authenticator\TokenAuthenticator;
use RetailCrm\Component\Constants;
use RetailCrm\Component\Exception\TopApiException;
use RetailCrm\Model\Request\HttpDnsGetRequest; use RetailCrm\Model\Request\HttpDnsGetRequest;
use RetailCrm\Model\Response\BaseResponse; use RetailCrm\Model\Response\BaseResponse;
use RetailCrm\Model\Response\Body\ErrorResponseBody; use RetailCrm\Model\Response\Body\ErrorResponseBody;
@ -52,7 +54,6 @@ class ClientTest extends TestCase
$client = ClientBuilder::create() $client = ClientBuilder::create()
->setContainer($this->getContainer($mockClient)) ->setContainer($this->getContainer($mockClient))
->setAppData(new AppData(AppData::OVERSEAS_ENDPOINT, 'appKey', 'appSecret')) ->setAppData(new AppData(AppData::OVERSEAS_ENDPOINT, 'appKey', 'appSecret'))
->setAuthenticator(new TokenAuthenticator('appKey', 'token'))
->build(); ->build();
$this->expectExceptionMessage($errorBody->msg); $this->expectExceptionMessage($errorBody->msg);