<commit_before><?php

namespace Brera\Product;

use Brera\Api\ApiRequestHandler;
use Brera\Http\HttpRequest;
use Brera\Image\UpdateImageCommand;
use Brera\Logger;
use Brera\Queue\Queue;
use Brera\Utils\XPathParser;

class CatalogImportApiV1PutRequestHandler extends ApiRequestHandler
{
    /**
     * @var Queue
     */
    private $commandQueue;

    /**
     * @var string
     */
    private $importDirectoryPath;

    /**
     * @var ProductSourceBuilder
     */
    private $productSourceBuilder;

    /**
     * @var ProductListingMetaInfoSourceBuilder
     */
    private $productListingMetaInfoSourceBuilder;

    /**
     * @var Logger
     */
    private $logger;

    /**
     * @param Queue $commandQueue
     * @param string $importDirectoryPath
     * @param ProductSourceBuilder $productSourceBuilder
     * @param ProductListingMetaInfoSourceBuilder $productListingMetaInfoSourceBuilder
     * @param Logger $logger
     */
    private function __construct(
        Queue $commandQueue,
        $importDirectoryPath,
        ProductSourceBuilder $productSourceBuilder,
        ProductListingMetaInfoSourceBuilder $productListingMetaInfoSourceBuilder,
        Logger $logger
    ) {
        $this->commandQueue = $commandQueue;
        $this->importDirectoryPath = $importDirectoryPath;
        $this->productSourceBuilder = $productSourceBuilder;
        $this->productListingMetaInfoSourceBuilder = $productListingMetaInfoSourceBuilder;
        $this->logger = $logger;
    }

    /**
     * @param Queue $commandQueue
     * @param string $importDirectoryPath
     * @param ProductSourceBuilder $productSourceBuilder
     * @param ProductListingMetaInfoSourceBuilder $productListingMetaInfoSourceBuilder
     * @param Logger $logger
     * @return CatalogImportApiV1PutRequestHandler
     */
    public static function create(
        Queue $commandQueue,
        $importDirectoryPath,
        ProductSourceBuilder $productSourceBuilder,
        ProductListingMetaInfoSourceBuilder $productListingMetaInfoSourceBuilder,
        Logger $logger
    ) {
        if (!is_readable($importDirectoryPath)) {
            throw new CatalogImportDirectoryNotReadableException(sprintf('%s is not readable.', $importDirectoryPath));
        }

        return new self(
            $commandQueue,
            $importDirectoryPath,
            $productSourceBuilder,
            $productListingMetaInfoSourceBuilder,
            $logger
        );
    }

    /**
     * @param HttpRequest $request
     * @return bool
     */
    final public function canProcess(HttpRequest $request)
    {
        return HttpRequest::METHOD_PUT === $request->getMethod();
    }

    /**
     * @param HttpRequest $request
     * @return string
     */
    final protected function getResponseBody(HttpRequest $request)
    {
        return json_encode('OK');
    }

    protected function processRequest(HttpRequest $request)
    {
        $xml = $this->getImportFileContents($request);

        $productNodesXml = (new XPathParser($xml))->getXmlNodesRawXmlArrayByXPath('//catalog/products/product');
        foreach ($productNodesXml as $productXml) {
            try {
                $productSource = $this->productSourceBuilder->createProductSourceFromXml($productXml);
                $this->commandQueue->add(new UpdateProductCommand($productSource));
            } catch (\Exception $exception) {
                $skuString = (new XPathParser($productXml))->getXmlNodesArrayByXPath('//@sku')[0]['value'];
                $sku = SampleSku::fromString($skuString);
                $productId = ProductId::fromSku($sku);
                $loggerMessage = new ProductImportFailedMessage($productId, $exception);
                $this->logger->log($loggerMessage);
            }
        }

        $listingNodesXml = (new XPathParser($xml))->getXmlNodesRawXmlArrayByXPath('//catalog/listings/listing');
        foreach ($listingNodesXml as $listingXml) {
            $productListingMetaInfoSource = $this->productListingMetaInfoSourceBuilder
                ->createProductListingMetaInfoSourceFromXml($listingXml);
            $this->commandQueue->add(new UpdateProductListingCommand($productListingMetaInfoSource));
        }

        $imageNodes = (new XPathParser($xml))->getXmlNodesArrayByXPath(
            '//catalog/products/product/attributes/image/file'
        );
        foreach ($imageNodes as $imageNode) {
            $this->commandQueue->add(new UpdateImageCommand($imageNode['value']));
        }
    }

    /**
     * @param HttpRequest $request
     * @return string
     */
    private function getImportFileContents(HttpRequest $request)
    {
        $filePath = $this->importDirectoryPath . '/' . $this->getImportFileNameFromRequest($request);

        if (!is_readable($filePath)) {
            throw new CatalogImportFileNotReadableException(sprintf('%s file is not readable.', $filePath));
        }

        return file_get_contents($filePath);
    }

    /**
     * @param HttpRequest $request
     * @return string
     */
    private function getImportFileNameFromRequest(HttpRequest $request)
    {
        $requestArguments = json_decode($request->getRawBody(), true);

        if (!is_array($requestArguments) || !isset($requestArguments['fileName']) || !$requestArguments['fileName']) {
            throw new CatalogImportFileNameNotFoundInRequestBodyException(
                'Import file name is not found in request body.'
            );
        }

        return $requestArguments['fileName'];
    }
}<commit_msg>Issue #335: Refactor CatalogImportApiV1PutRequestHandler::processRequest<commit_after><?php

namespace Brera\Product;

use Brera\Api\ApiRequestHandler;
use Brera\Http\HttpRequest;
use Brera\Image\UpdateImageCommand;
use Brera\Logger;
use Brera\Queue\Queue;
use Brera\Utils\XPathParser;

class CatalogImportApiV1PutRequestHandler extends ApiRequestHandler
{
    /**
     * @var Queue
     */
    private $commandQueue;

    /**
     * @var string
     */
    private $importDirectoryPath;

    /**
     * @var ProductSourceBuilder
     */
    private $productSourceBuilder;

    /**
     * @var ProductListingMetaInfoSourceBuilder
     */
    private $productListingMetaInfoSourceBuilder;

    /**
     * @var Logger
     */
    private $logger;

    /**
     * @param Queue $commandQueue
     * @param string $importDirectoryPath
     * @param ProductSourceBuilder $productSourceBuilder
     * @param ProductListingMetaInfoSourceBuilder $productListingMetaInfoSourceBuilder
     * @param Logger $logger
     */
    private function __construct(
        Queue $commandQueue,
        $importDirectoryPath,
        ProductSourceBuilder $productSourceBuilder,
        ProductListingMetaInfoSourceBuilder $productListingMetaInfoSourceBuilder,
        Logger $logger
    ) {
        $this->commandQueue = $commandQueue;
        $this->importDirectoryPath = $importDirectoryPath;
        $this->productSourceBuilder = $productSourceBuilder;
        $this->productListingMetaInfoSourceBuilder = $productListingMetaInfoSourceBuilder;
        $this->logger = $logger;
    }

    /**
     * @param Queue $commandQueue
     * @param string $importDirectoryPath
     * @param ProductSourceBuilder $productSourceBuilder
     * @param ProductListingMetaInfoSourceBuilder $productListingMetaInfoSourceBuilder
     * @param Logger $logger
     * @return CatalogImportApiV1PutRequestHandler
     */
    public static function create(
        Queue $commandQueue,
        $importDirectoryPath,
        ProductSourceBuilder $productSourceBuilder,
        ProductListingMetaInfoSourceBuilder $productListingMetaInfoSourceBuilder,
        Logger $logger
    ) {
        if (!is_readable($importDirectoryPath)) {
            throw new CatalogImportDirectoryNotReadableException(sprintf('%s is not readable.', $importDirectoryPath));
        }

        return new self(
            $commandQueue,
            $importDirectoryPath,
            $productSourceBuilder,
            $productListingMetaInfoSourceBuilder,
            $logger
        );
    }

    /**
     * @param HttpRequest $request
     * @return bool
     */
    final public function canProcess(HttpRequest $request)
    {
        return HttpRequest::METHOD_PUT === $request->getMethod();
    }

    /**
     * @param HttpRequest $request
     * @return string
     */
    final protected function getResponseBody(HttpRequest $request)
    {
        return json_encode('OK');
    }

    protected function processRequest(HttpRequest $request)
    {
        $xml = $this->getImportFileContents($request);

        $this->processProductsXml($xml);
        $this->processListings($xml);
        $this->processImages($xml);
    }

    /**
     * @param HttpRequest $request
     * @return string
     */
    private function getImportFileContents(HttpRequest $request)
    {
        $filePath = $this->importDirectoryPath . '/' . $this->getImportFileNameFromRequest($request);

        if (!is_readable($filePath)) {
            throw new CatalogImportFileNotReadableException(sprintf('%s file is not readable.', $filePath));
        }

        return file_get_contents($filePath);
    }

    /**
     * @param HttpRequest $request
     * @return string
     */
    private function getImportFileNameFromRequest(HttpRequest $request)
    {
        $requestArguments = json_decode($request->getRawBody(), true);

        if (!is_array($requestArguments) || !isset($requestArguments['fileName']) || !$requestArguments['fileName']) {
            throw new CatalogImportFileNameNotFoundInRequestBodyException(
                'Import file name is not found in request body.'
            );
        }

        return $requestArguments['fileName'];
    }

    /**
     * @param string $xml
     */
    private function processProductsXml($xml)
    {
        $productNodesXml = (new XPathParser($xml))->getXmlNodesRawXmlArrayByXPath('//catalog/products/product');
        foreach ($productNodesXml as $productXml) {
            try {
                $productSource = $this->productSourceBuilder->createProductSourceFromXml($productXml);
                $this->commandQueue->add(new UpdateProductCommand($productSource));
            } catch (\Exception $exception) {
                $skuString = (new XPathParser($productXml))->getXmlNodesArrayByXPath('//@sku')[0]['value'];
                $sku = SampleSku::fromString($skuString);
                $productId = ProductId::fromSku($sku);
                $loggerMessage = new ProductImportFailedMessage($productId, $exception);
                $this->logger->log($loggerMessage);
            }
        }
    }

    /**
     * @param string $xml
     */
    private function processListings($xml)
    {
        $listingNodesXml = (new XPathParser($xml))->getXmlNodesRawXmlArrayByXPath('//catalog/listings/listing');
        foreach ($listingNodesXml as $listingXml) {
            $productListingMetaInfoSource = $this->productListingMetaInfoSourceBuilder
                ->createProductListingMetaInfoSourceFromXml($listingXml);
            $this->commandQueue->add(new UpdateProductListingCommand($productListingMetaInfoSource));
        }
    }

    /**
     * @param string $xml
     */
    private function processImages($xml)
    {
        $imageNodes = (new XPathParser($xml))->getXmlNodesArrayByXPath(
            '//catalog/products/product/attributes/image/file'
        );
        foreach ($imageNodes as $imageNode) {
            $this->commandQueue->add(new UpdateImageCommand($imageNode['value']));
        }
    }
}
{'fraction_non_alphanumeric': 0.10544541598130032, 'fraction_numerical': 0.0008657259111765215, 'mean_word_length': 2.7925147734734077, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 58, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 17}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4818187', 'n_tokens_mistral': 3055, 'n_tokens_neox': 2972, 'n_words': 538}