From 9b081e2e7f1e0bd23109601d06de125cadb77fae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=B0=D0=B2=D0=B5=D0=BB?= Date: Wed, 30 Sep 2020 17:50:44 +0300 Subject: [PATCH] WIP: Hacks for inline JSON string inside another JSON or inside XML (some responses actually look like that) --- README.md | 7 +- src/Builder/ClientBuilder.php | 17 +- .../Authenticator/TokenAuthenticator.php | 60 ------ src/Component/Exception/TopApiException.php | 9 +- .../Serializer/InlineJsonBodyHandler.php | 201 ++++++++++++++++++ src/Factory/SerializerFactory.php | 14 +- src/Factory/TopRequestFactory.php | 7 +- src/Interfaces/AuthenticatorInterface.php | 34 --- src/Interfaces/TopRequestFactoryInterface.php | 4 +- .../TopRequestProcessorInterface.php | 4 +- src/Model/Request/BaseRequest.php | 11 +- src/Model/Request/HttpDnsGetRequest.php | 5 +- src/Model/Response/BaseResponse.php | 10 +- src/Model/Response/Body/ErrorResponseBody.php | 8 - .../HttpDnsGetResponseResult.php | 36 ++++ .../Response/Data/HttpDnsGetResponseData.php | 37 ++++ src/Model/Response/HttpDnsGetResponse.php | 36 ++++ src/Model/Response/TopResponseInterface.php | 28 +++ src/Service/RequestSigner.php | 11 +- src/Service/TopRequestProcessor.php | 9 +- src/TopClient/Client.php | 23 +- tests/RetailCrm/Test/TestCase.php | 22 -- .../Tests/Builder/ClientBuilderTest.php | 2 - .../Tests/Factory/TopRequestFactoryTest.php | 6 +- .../Tests/Service/RequestSignerTest.php | 9 +- .../RetailCrm/Tests/TopClient/ClientTest.php | 3 +- 26 files changed, 407 insertions(+), 206 deletions(-) delete mode 100644 src/Component/Authenticator/TokenAuthenticator.php create mode 100644 src/Component/Serializer/InlineJsonBodyHandler.php delete mode 100644 src/Interfaces/AuthenticatorInterface.php create mode 100644 src/Model/Response/Body/InlineJsonBody/HttpDnsGetResponseResult.php create mode 100644 src/Model/Response/Data/HttpDnsGetResponseData.php create mode 100644 src/Model/Response/HttpDnsGetResponse.php create mode 100644 src/Model/Response/TopResponseInterface.php diff --git a/README.md b/README.md index bb634e2..6bcc62a 100644 --- a/README.md +++ b/README.md @@ -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\Builder\ClientBuilder; use RetailCrm\Builder\ContainerBuilder; -use RetailCrm\Component\Authenticator\TokenAuthenticator; -$authenticator = new TokenAuthenticator('appKey', 'token'); $appData = new AppData(AppData::OVERSEAS_ENDPOINT, 'appKey', 'appSecret'); $client = ClientBuilder::create() ->setContainer(ContainerBuilder::create()->build()) ->setAppData($appData) - ->setAuthenticator($authenticator) ->build(); ``` @@ -39,7 +36,6 @@ use RetailCrm\Component\Environment; use RetailCrm\Component\Logger\StdoutLogger; use RetailCrm\Builder\ClientBuilder; use RetailCrm\Builder\ContainerBuilder; -use RetailCrm\Component\Authenticator\TokenAuthenticator; $client = new Client(); $logger = new StdoutLogger(); @@ -57,7 +53,8 @@ $container = ContainerBuilder::create() $client = ClientBuilder::create() ->setContainer($container) ->setAppData($appData) - ->setAuthenticator($authenticator) ->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. + +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. diff --git a/src/Builder/ClientBuilder.php b/src/Builder/ClientBuilder.php index 91060f2..f90be1f 100644 --- a/src/Builder/ClientBuilder.php +++ b/src/Builder/ClientBuilder.php @@ -15,7 +15,6 @@ namespace RetailCrm\Builder; use RetailCrm\Component\Constants; use RetailCrm\Component\ServiceLocator; use RetailCrm\Interfaces\AppDataInterface; -use RetailCrm\Interfaces\AuthenticatorInterface; use RetailCrm\Interfaces\BuilderInterface; use RetailCrm\Interfaces\ContainerAwareInterface; use RetailCrm\Interfaces\TopRequestFactoryInterface; @@ -40,9 +39,6 @@ class ClientBuilder implements ContainerAwareInterface, BuilderInterface /** @var \RetailCrm\Interfaces\AppDataInterface $appData */ private $appData; - /*** @var AuthenticatorInterface $authenticator */ - protected $authenticator; - /** * @return static */ @@ -62,24 +58,13 @@ class ClientBuilder implements ContainerAwareInterface, BuilderInterface return $this; } - /** - * @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator - * - * @return $this - */ - public function setAuthenticator(AuthenticatorInterface $authenticator): self - { - $this->authenticator = $authenticator; - return $this; - } - /** * @return \RetailCrm\TopClient\Client * @throws \RetailCrm\Component\Exception\ValidationException */ 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->setSerializer($this->container->get(Constants::SERIALIZER)); $client->setValidator($this->container->get(Constants::VALIDATOR)); diff --git a/src/Component/Authenticator/TokenAuthenticator.php b/src/Component/Authenticator/TokenAuthenticator.php deleted file mode 100644 index 5a91e7e..0000000 --- a/src/Component/Authenticator/TokenAuthenticator.php +++ /dev/null @@ -1,60 +0,0 @@ - - * @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 - * @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; - } -} diff --git a/src/Component/Exception/TopApiException.php b/src/Component/Exception/TopApiException.php index 463295a..4edf4d8 100644 --- a/src/Component/Exception/TopApiException.php +++ b/src/Component/Exception/TopApiException.php @@ -42,20 +42,21 @@ class TopApiException extends Exception * TopApiException constructor. * * @param \RetailCrm\Model\Response\Body\ErrorResponseBody $responseBody + * @param string|null $requestId * @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); $this->subCode = $responseBody->subCode; - $this->requestId = $responseBody->requestId; + $this->requestId = $requestId; } /** * @return string */ - public function getSubCode(): string + public function getSubCode(): ?string { return $this->subCode; } @@ -63,7 +64,7 @@ class TopApiException extends Exception /** * @return string */ - public function getRequestId(): string + public function getRequestId(): ?string { return $this->requestId; } diff --git a/src/Component/Serializer/InlineJsonBodyHandler.php b/src/Component/Serializer/InlineJsonBodyHandler.php new file mode 100644 index 0000000..3c65728 --- /dev/null +++ b/src/Component/Serializer/InlineJsonBodyHandler.php @@ -0,0 +1,201 @@ + + * @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 + * @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)); + } +} diff --git a/src/Factory/SerializerFactory.php b/src/Factory/SerializerFactory.php index ba2bd7e..af3cab2 100644 --- a/src/Factory/SerializerFactory.php +++ b/src/Factory/SerializerFactory.php @@ -19,6 +19,7 @@ use JMS\Serializer\SerializerBuilder; use JMS\Serializer\SerializerInterface; use Psr\Container\ContainerInterface; use RetailCrm\Component\Constants; +use RetailCrm\Component\Serializer\InlineJsonBodyHandler; use RetailCrm\Interfaces\FactoryInterface; /** @@ -73,17 +74,17 @@ class SerializerFactory implements FactoryInterface $returnSame = function ($visitor, $obj, array $type) { 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( GraphNavigatorInterface::DIRECTION_SERIALIZATION, 'RequestDtoInterface', 'json', - function ($visitor, $obj, array $type) use ($container) { - /** @var SerializerInterface $serializer */ - $serializer = $container->get(Constants::SERIALIZER); - - return $serializer->serialize($obj, 'json'); - } + $serializeJson ); $registry->registerHandler( GraphNavigatorInterface::DIRECTION_DESERIALIZATION, @@ -132,6 +133,7 @@ class SerializerFactory implements FactoryInterface 'xml', $returnNull ); + $registry->registerSubscribingHandler(new InlineJsonBodyHandler()); })->addDefaultHandlers() ->setSerializationContextFactory(new SerializationContextFactory()) ->build(); diff --git a/src/Factory/TopRequestFactory.php b/src/Factory/TopRequestFactory.php index 9ceff8f..e88a344 100644 --- a/src/Factory/TopRequestFactory.php +++ b/src/Factory/TopRequestFactory.php @@ -20,7 +20,6 @@ use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Message\UriFactoryInterface; use RetailCrm\Component\Exception\FactoryException; use RetailCrm\Interfaces\AppDataInterface; -use RetailCrm\Interfaces\AuthenticatorInterface; use RetailCrm\Interfaces\FileItemInterface; use RetailCrm\Interfaces\TopRequestFactoryInterface; use RetailCrm\Model\Request\BaseRequest; @@ -123,19 +122,19 @@ class TopRequestFactory implements TopRequestFactoryInterface /** * @param \RetailCrm\Model\Request\BaseRequest $request * @param \RetailCrm\Interfaces\AppDataInterface $appData - * @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator * * @return \Psr\Http\Message\RequestInterface * @throws \RetailCrm\Component\Exception\FactoryException */ public function fromModel( BaseRequest $request, - AppDataInterface $appData, - AuthenticatorInterface $authenticator + AppDataInterface $appData ): RequestInterface { $requestData = $this->serializer->toArray($request); $requestHasBinaryData = $this->filter->hasBinaryFromRequestData($requestData); + ksort($requestData); + if (empty($requestData)) { throw new FactoryException('Empty request data'); } diff --git a/src/Interfaces/AuthenticatorInterface.php b/src/Interfaces/AuthenticatorInterface.php deleted file mode 100644 index 1f6b8a6..0000000 --- a/src/Interfaces/AuthenticatorInterface.php +++ /dev/null @@ -1,34 +0,0 @@ - - * @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 - * @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; -} diff --git a/src/Interfaces/TopRequestFactoryInterface.php b/src/Interfaces/TopRequestFactoryInterface.php index 36b9954..14b9991 100644 --- a/src/Interfaces/TopRequestFactoryInterface.php +++ b/src/Interfaces/TopRequestFactoryInterface.php @@ -31,7 +31,6 @@ interface TopRequestFactoryInterface /** * @param \RetailCrm\Model\Request\BaseRequest $request * @param \RetailCrm\Interfaces\AppDataInterface $appData - * @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator * * @return RequestInterface * @throws \RetailCrm\Component\Exception\FactoryException @@ -39,7 +38,6 @@ interface TopRequestFactoryInterface */ public function fromModel( BaseRequest $request, - AppDataInterface $appData, - AuthenticatorInterface $authenticator + AppDataInterface $appData ): RequestInterface; } diff --git a/src/Interfaces/TopRequestProcessorInterface.php b/src/Interfaces/TopRequestProcessorInterface.php index d8aff74..a45039c 100644 --- a/src/Interfaces/TopRequestProcessorInterface.php +++ b/src/Interfaces/TopRequestProcessorInterface.php @@ -33,7 +33,6 @@ interface TopRequestProcessorInterface * * @param \RetailCrm\Model\Request\BaseRequest $request * @param \RetailCrm\Interfaces\AppDataInterface $appData - * @param \RetailCrm\Interfaces\AuthenticatorInterface $authenticator * * @return void * @throws \RetailCrm\Component\Exception\FactoryException @@ -41,7 +40,6 @@ interface TopRequestProcessorInterface */ public function process( BaseRequest $request, - AppDataInterface $appData, - AuthenticatorInterface $authenticator + AppDataInterface $appData ): void; } diff --git a/src/Model/Request/BaseRequest.php b/src/Model/Request/BaseRequest.php index fe35d8c..edbfaab 100644 --- a/src/Model/Request/BaseRequest.php +++ b/src/Model/Request/BaseRequest.php @@ -53,7 +53,7 @@ abstract class BaseRequest * * @JMS\Type("string") * @JMS\SerializedName("session") - * @Assert\NotBlank() + * @JMS\SkipWhenEmpty() */ public $session; @@ -91,6 +91,7 @@ abstract class BaseRequest * * @JMS\Type("bool") * @JMS\SerializedName("simplify") + * @JMS\Accessor(getter="isSimplify") */ public $simplify = false; @@ -131,6 +132,14 @@ abstract class BaseRequest $this->method = $this->getMethod(); } + /** + * @return bool|null + */ + public function isSimplify(): ?bool + { + return $this->simplify ? true : null; + } + /** * @param string $method * diff --git a/src/Model/Request/HttpDnsGetRequest.php b/src/Model/Request/HttpDnsGetRequest.php index fe12595..6f60ebb 100644 --- a/src/Model/Request/HttpDnsGetRequest.php +++ b/src/Model/Request/HttpDnsGetRequest.php @@ -12,7 +12,7 @@ */ namespace RetailCrm\Model\Request; -use RetailCrm\Model\Response\BaseResponse; +use RetailCrm\Model\Response\HttpDnsGetResponse; /** * Class HttpDnsGetRequest @@ -43,7 +43,6 @@ class HttpDnsGetRequest extends BaseRequest */ public function getExpectedResponse(): string { - // TODO: Implement getExpectedResponse() method. - return BaseResponse::class; + return HttpDnsGetResponse::class; } } diff --git a/src/Model/Response/BaseResponse.php b/src/Model/Response/BaseResponse.php index 9a96e93..888a5b8 100644 --- a/src/Model/Response/BaseResponse.php +++ b/src/Model/Response/BaseResponse.php @@ -24,7 +24,7 @@ use JMS\Serializer\Annotation as JMS; * @link http://retailcrm.ru * @see https://help.retailcrm.ru */ -class BaseResponse +class BaseResponse implements TopResponseInterface { /** * @var \RetailCrm\Model\Response\Body\ErrorResponseBody @@ -33,4 +33,12 @@ class BaseResponse * @JMS\SerializedName("error_response") */ public $errorResponse; + + /** + * @var string $requestId + * + * @JMS\Type("string") + * @JMS\SerializedName("request_id") + */ + public $requestId; } diff --git a/src/Model/Response/Body/ErrorResponseBody.php b/src/Model/Response/Body/ErrorResponseBody.php index 9547773..f7c07f8 100644 --- a/src/Model/Response/Body/ErrorResponseBody.php +++ b/src/Model/Response/Body/ErrorResponseBody.php @@ -49,12 +49,4 @@ class ErrorResponseBody * @JMS\SerializedName("sub_code") */ public $subCode; - - /** - * @var string $requestId - * - * @JMS\Type("string") - * @JMS\SerializedName("request_id") - */ - public $requestId; } diff --git a/src/Model/Response/Body/InlineJsonBody/HttpDnsGetResponseResult.php b/src/Model/Response/Body/InlineJsonBody/HttpDnsGetResponseResult.php new file mode 100644 index 0000000..f496a04 --- /dev/null +++ b/src/Model/Response/Body/InlineJsonBody/HttpDnsGetResponseResult.php @@ -0,0 +1,36 @@ + + * @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 + * @license MIT + * @link http://retailcrm.ru + * @see https://help.retailcrm.ru + */ +class HttpDnsGetResponseResult +{ + /** + * @var array $env + * + * @JMS\Type("int") + * @JMS\SerializedName("env") + */ + public $env; +} diff --git a/src/Model/Response/Data/HttpDnsGetResponseData.php b/src/Model/Response/Data/HttpDnsGetResponseData.php new file mode 100644 index 0000000..7936078 --- /dev/null +++ b/src/Model/Response/Data/HttpDnsGetResponseData.php @@ -0,0 +1,37 @@ + + * @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 + * @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; +} diff --git a/src/Model/Response/HttpDnsGetResponse.php b/src/Model/Response/HttpDnsGetResponse.php new file mode 100644 index 0000000..546f16d --- /dev/null +++ b/src/Model/Response/HttpDnsGetResponse.php @@ -0,0 +1,36 @@ + + * @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 + * @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; +} diff --git a/src/Model/Response/TopResponseInterface.php b/src/Model/Response/TopResponseInterface.php new file mode 100644 index 0000000..5c9a846 --- /dev/null +++ b/src/Model/Response/TopResponseInterface.php @@ -0,0 +1,28 @@ + + * @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 + * @license MIT + * @link http://retailcrm.ru + * @see https://help.retailcrm.ru + */ +interface TopResponseInterface +{ +} diff --git a/src/Service/RequestSigner.php b/src/Service/RequestSigner.php index 52a4947..086af4a 100644 --- a/src/Service/RequestSigner.php +++ b/src/Service/RequestSigner.php @@ -70,9 +70,8 @@ class RequestSigner implements RequestSignerInterface switch ($request->signMethod) { case Constants::SIGN_TYPE_MD5: - $request->sign = strtoupper(md5( - $appData->getAppSecret() . $stringToBeSigned . $appData->getAppSecret() - )); + $stringToBeSigned = $appData->getAppSecret() . $stringToBeSigned . $appData->getAppSecret(); + $request->sign = strtoupper(md5($stringToBeSigned)); break; case Constants::SIGN_TYPE_HMAC: $request->sign = strtoupper(hash_hmac('md5', $stringToBeSigned, $appData->getAppSecret())); @@ -91,7 +90,11 @@ class RequestSigner implements RequestSignerInterface private function getDataForSigning(BaseRequest $request): array { $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); return $params; diff --git a/src/Service/TopRequestProcessor.php b/src/Service/TopRequestProcessor.php index 4003022..b0d00ed 100644 --- a/src/Service/TopRequestProcessor.php +++ b/src/Service/TopRequestProcessor.php @@ -13,7 +13,6 @@ namespace RetailCrm\Service; use RetailCrm\Interfaces\AppDataInterface; -use RetailCrm\Interfaces\AuthenticatorInterface; use RetailCrm\Interfaces\RequestSignerInterface; use RetailCrm\Interfaces\RequestTimestampProviderInterface; use RetailCrm\Interfaces\TopRequestProcessorInterface; @@ -71,12 +70,12 @@ class TopRequestProcessor implements TopRequestProcessorInterface */ public function process( BaseRequest $request, - AppDataInterface $appData, - AuthenticatorInterface $authenticator + AppDataInterface $appData ): void { - $authenticator->authenticate($request); - $this->signer->sign($request, $appData); + $request->appKey = $appData->getAppKey(); + $this->timestampProvider->provide($request); + $this->signer->sign($request, $appData); $this->validate($request); } } diff --git a/src/TopClient/Client.php b/src/TopClient/Client.php index bc603b9..5deef6f 100644 --- a/src/TopClient/Client.php +++ b/src/TopClient/Client.php @@ -12,6 +12,7 @@ */ namespace RetailCrm\TopClient; +use DateTime; use JMS\Serializer\SerializerInterface; use Psr\Http\Client\ClientInterface; use Psr\Http\Message\StreamInterface; @@ -19,11 +20,11 @@ use RetailCrm\Component\Exception\TopApiException; use RetailCrm\Component\Exception\TopClientException; use RetailCrm\Component\ServiceLocator; use RetailCrm\Interfaces\AppDataInterface; -use RetailCrm\Interfaces\AuthenticatorInterface; use RetailCrm\Interfaces\TopRequestFactoryInterface; use RetailCrm\Interfaces\TopRequestProcessorInterface; use RetailCrm\Model\Request\BaseRequest; use RetailCrm\Model\Response\BaseResponse; +use RetailCrm\Model\Response\TopResponseInterface; use RetailCrm\Traits\ValidatorAwareTrait; use Symfony\Component\Validator\Constraints as Assert; @@ -46,12 +47,6 @@ class Client */ protected $appData; - /** - * @var AuthenticatorInterface $authenticator - * @Assert\NotNull(message="Authenticator should be provided") - */ - protected $authenticator; - /** * @var ClientInterface $httpClient * @Assert\NotNull(message="HTTP client should be provided") @@ -89,12 +84,10 @@ class Client * Client constructor. * * @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->authenticator = $authenticator; } /** @@ -159,18 +152,18 @@ class Client /** * @param \RetailCrm\Model\Request\BaseRequest $request * - * @return \RetailCrm\Model\Response\BaseResponse + * @return TopResponseInterface * @throws \Psr\Http\Client\ClientExceptionInterface * @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) + 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); /** @var BaseResponse $response */ @@ -185,7 +178,7 @@ class Client } if (null !== $response->errorResponse) { - throw new TopApiException($response->errorResponse); + throw new TopApiException($response->errorResponse, $response->requestId); } return $response; diff --git a/tests/RetailCrm/Test/TestCase.php b/tests/RetailCrm/Test/TestCase.php index dba8428..8a524d1 100644 --- a/tests/RetailCrm/Test/TestCase.php +++ b/tests/RetailCrm/Test/TestCase.php @@ -86,28 +86,6 @@ abstract class TestCase extends \PHPUnit\Framework\TestCase 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 * diff --git a/tests/RetailCrm/Tests/Builder/ClientBuilderTest.php b/tests/RetailCrm/Tests/Builder/ClientBuilderTest.php index 921a7f4..3e8f13d 100644 --- a/tests/RetailCrm/Tests/Builder/ClientBuilderTest.php +++ b/tests/RetailCrm/Tests/Builder/ClientBuilderTest.php @@ -13,7 +13,6 @@ namespace RetailCrm\Tests\Builder; use RetailCrm\Component\AppData; -use RetailCrm\Component\Authenticator\TokenAuthenticator; use RetailCrm\Component\ServiceLocator; use RetailCrm\Builder\ClientBuilder; use RetailCrm\Test\TestCase; @@ -36,7 +35,6 @@ class ClientBuilderTest extends TestCase $client = ClientBuilder::create() ->setContainer($this->getContainer()) ->setAppData(new AppData(AppData::OVERSEAS_ENDPOINT, 'appKey', 'helloworld')) - ->setAuthenticator(new TokenAuthenticator('appKey', 'token')) ->build(); self::assertInstanceOf(Client::class, $client); diff --git a/tests/RetailCrm/Tests/Factory/TopRequestFactoryTest.php b/tests/RetailCrm/Tests/Factory/TopRequestFactoryTest.php index 771c68c..0ee5a22 100644 --- a/tests/RetailCrm/Tests/Factory/TopRequestFactoryTest.php +++ b/tests/RetailCrm/Tests/Factory/TopRequestFactoryTest.php @@ -35,8 +35,7 @@ class TopRequestFactoryTest extends TestCase $factory = $this->getContainer()->get(TopRequestFactoryInterface::class); $request = $factory->fromModel( $this->getTestRequest(Constants::SIGN_TYPE_HMAC), - $this->getAppData(), - $this->getAuthenticator() + $this->getAppData() ); $uri = $request->getUri(); $contents = self::getStreamData($request->getBody()); @@ -52,8 +51,7 @@ class TopRequestFactoryTest extends TestCase $factory = $this->getContainer()->get(TopRequestFactoryInterface::class); $request = $factory->fromModel( $this->getTestRequest(Constants::SIGN_TYPE_HMAC, true, true), - $this->getAppData(), - $this->getAuthenticator() + $this->getAppData() ); $uri = $request->getUri(); $contents = self::getStreamData($request->getBody()); diff --git a/tests/RetailCrm/Tests/Service/RequestSignerTest.php b/tests/RetailCrm/Tests/Service/RequestSignerTest.php index 1f8bf62..f4262cb 100644 --- a/tests/RetailCrm/Tests/Service/RequestSignerTest.php +++ b/tests/RetailCrm/Tests/Service/RequestSignerTest.php @@ -16,7 +16,6 @@ use RetailCrm\Component\AppData; use RetailCrm\Component\Constants; use RetailCrm\Interfaces\AppDataInterface; use RetailCrm\Interfaces\RequestSignerInterface; -use RetailCrm\Service\RequestSigner; use RetailCrm\Test\TestCase; use RetailCrm\Test\TestSignerRequest; @@ -56,22 +55,22 @@ class RequestSignerTest extends TestCase [ $this->getTestRequest(Constants::SIGN_TYPE_MD5), $appData, - '4BC79C5FAA1B5E254E95A97E65BACEAB' + '468BF7C95925C187D0DFD7D042072EB4' ], [ $this->getTestRequest(Constants::SIGN_TYPE_HMAC), $appData, - '497FA7FCAD98F4F335EFAE2451F8291D' + '5EF5C76D5C158BFFA9F35BAAA712A879' ], [ $this->getTestRequest(Constants::SIGN_TYPE_MD5, true), $appData, - '4BC79C5FAA1B5E254E95A97E65BACEAB' + '468BF7C95925C187D0DFD7D042072EB4' ], [ $this->getTestRequest(Constants::SIGN_TYPE_HMAC, true), $appData, - '497FA7FCAD98F4F335EFAE2451F8291D' + '5EF5C76D5C158BFFA9F35BAAA712A879' ] ]; } diff --git a/tests/RetailCrm/Tests/TopClient/ClientTest.php b/tests/RetailCrm/Tests/TopClient/ClientTest.php index c60b5b1..df1348a 100644 --- a/tests/RetailCrm/Tests/TopClient/ClientTest.php +++ b/tests/RetailCrm/Tests/TopClient/ClientTest.php @@ -16,6 +16,8 @@ use Psr\Http\Message\RequestInterface; use RetailCrm\Builder\ClientBuilder; use RetailCrm\Component\AppData; use RetailCrm\Component\Authenticator\TokenAuthenticator; +use RetailCrm\Component\Constants; +use RetailCrm\Component\Exception\TopApiException; use RetailCrm\Model\Request\HttpDnsGetRequest; use RetailCrm\Model\Response\BaseResponse; use RetailCrm\Model\Response\Body\ErrorResponseBody; @@ -52,7 +54,6 @@ class ClientTest extends TestCase $client = ClientBuilder::create() ->setContainer($this->getContainer($mockClient)) ->setAppData(new AppData(AppData::OVERSEAS_ENDPOINT, 'appKey', 'appSecret')) - ->setAuthenticator(new TokenAuthenticator('appKey', 'token')) ->build(); $this->expectExceptionMessage($errorBody->msg);