Skip to content

Commit

Permalink
MAGETWO-85287: 8615: REST API unable to make requests with slash (/) …
Browse files Browse the repository at this point in the history
…in SKU #949

 - Merge Pull Request magento-engcom/magento2ce#949 from RomaKis/magento2:8615
 - Merged commits:
   1. 3776684
   2. 8870cf2
  • Loading branch information
Oleksii Korshenko committed Dec 18, 2017
2 parents 3e3e8fc + 8870cf2 commit e982176
Show file tree
Hide file tree
Showing 8 changed files with 318 additions and 18 deletions.
17 changes: 12 additions & 5 deletions app/code/Magento/Webapi/Controller/Rest/InputParamsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@

namespace Magento\Webapi\Controller\Rest;

use Magento\Framework\Webapi\ServiceInputProcessor;
use Magento\Framework\Webapi\Rest\Request as RestRequest;
use Magento\Webapi\Controller\Rest\Router;
use Magento\Framework\Webapi\ServiceInputProcessor;
use Magento\Webapi\Controller\Rest\Router\Route;
use Magento\Webapi\Model\UrlDecoder;

/**
* This class is responsible for retrieving resolved input data
Expand Down Expand Up @@ -47,26 +47,32 @@ class InputParamsResolver
private $requestValidator;

/**
* Initialize dependencies
*
* @var UrlDecoder
*/
private $urlDecoder;

/**
* @param RestRequest $request
* @param ParamsOverrider $paramsOverrider
* @param ServiceInputProcessor $serviceInputProcessor
* @param Router $router
* @param RequestValidator $requestValidator
* @param UrlDecoder $urlDecoder
*/
public function __construct(
RestRequest $request,
ParamsOverrider $paramsOverrider,
ServiceInputProcessor $serviceInputProcessor,
Router $router,
RequestValidator $requestValidator
RequestValidator $requestValidator,
UrlDecoder $urlDecoder = null
) {
$this->request = $request;
$this->paramsOverrider = $paramsOverrider;
$this->serviceInputProcessor = $serviceInputProcessor;
$this->router = $router;
$this->requestValidator = $requestValidator;
$this->urlDecoder = $urlDecoder ?: \Magento\Framework\App\ObjectManager::getInstance()->get(UrlDecoder::class);
}

/**
Expand Down Expand Up @@ -97,6 +103,7 @@ public function resolve()
$inputData = $this->request->getRequestData();
}

$inputData = $this->urlDecoder->decodeParams($inputData);
$inputData = $this->paramsOverrider->override($inputData, $route->getParameters());
$inputParams = $this->serviceInputProcessor->process($serviceClassName, $serviceMethodName, $inputData);
return $inputParams;
Expand Down
14 changes: 11 additions & 3 deletions app/code/Magento/Webapi/Controller/Soap/Request/Handler.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use Magento\Webapi\Model\Soap\Config as SoapConfig;
use Magento\Framework\Reflection\MethodsMap;
use Magento\Webapi\Model\ServiceMetadata;
use Magento\Webapi\Model\UrlDecoder;

/**
* Handler of requests to SOAP server.
Expand Down Expand Up @@ -70,8 +71,11 @@ class Handler
protected $methodsMapProcessor;

/**
* Initialize dependencies.
*
* @var UrlDecoder
*/
private $urlDecoder;

/**
* @param SoapRequest $request
* @param \Magento\Framework\ObjectManagerInterface $objectManager
* @param SoapConfig $apiConfig
Expand All @@ -80,6 +84,7 @@ class Handler
* @param ServiceInputProcessor $serviceInputProcessor
* @param DataObjectProcessor $dataObjectProcessor
* @param MethodsMap $methodsMapProcessor
* @param UrlDecoder $urlDecoder
*/
public function __construct(
SoapRequest $request,
Expand All @@ -89,7 +94,8 @@ public function __construct(
SimpleDataObjectConverter $dataObjectConverter,
ServiceInputProcessor $serviceInputProcessor,
DataObjectProcessor $dataObjectProcessor,
MethodsMap $methodsMapProcessor
MethodsMap $methodsMapProcessor,
UrlDecoder $urlDecoder = null
) {
$this->_request = $request;
$this->_objectManager = $objectManager;
Expand All @@ -99,6 +105,7 @@ public function __construct(
$this->serviceInputProcessor = $serviceInputProcessor;
$this->_dataObjectProcessor = $dataObjectProcessor;
$this->methodsMapProcessor = $methodsMapProcessor;
$this->urlDecoder = $urlDecoder ?: \Magento\Framework\App\ObjectManager::getInstance()->get(UrlDecoder::class);
}

/**
Expand Down Expand Up @@ -150,6 +157,7 @@ protected function _prepareRequestData($serviceClass, $serviceMethod, $arguments
/** SoapServer wraps parameters into array. Thus this wrapping should be removed to get access to parameters. */
$arguments = reset($arguments);
$arguments = $this->_dataObjectConverter->convertStdObjectToArray($arguments, true);
$arguments = $this->urlDecoder->decodeParams($arguments);
return $this->serviceInputProcessor->process($serviceClass, $serviceMethod, $arguments);
}

Expand Down
35 changes: 35 additions & 0 deletions app/code/Magento/Webapi/Model/UrlDecoder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/

namespace Magento\Webapi\Model;

/**
* Url decoder.
*/
class UrlDecoder
{
/**
* Decode request params.
*
* @param array $params
*
* @return array
*/
public function decodeParams(array $params)
{
foreach ($params as &$param) {
if (is_array($param)) {
$param = $this->decodeParams($param);
} else {
if ($param !== null && is_string($param)) {
$param = rawurldecode($param);
}
}
}

return $params;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,15 @@ class ProductRepositoryInterfaceTest extends WebapiAbstract
ProductInterface::TYPE_ID => 'simple',
ProductInterface::PRICE => 10
],
[
ProductInterface::SKU => [
'rest' => 'sku%252fwith%252fslashes',
'soap' => 'sku%2fwith%2fslashes'
],
ProductInterface::NAME => 'Simple Product with Sku with Slashes',
ProductInterface::TYPE_ID => 'simple',
ProductInterface::PRICE => 10
],
];

/**
Expand Down Expand Up @@ -135,6 +144,20 @@ public function productCreationProvider()
];
}

/**
* @magentoApiDataFixture Magento/Catalog/_files/product_simple_sku_with_slash.php
*/
public function testGetBySkuWithSlash()
{
$productData = $this->productData[2];
$response = $this->getProduct($productData[ProductInterface::SKU][TESTS_WEB_API_ADAPTER]);
$productData[ProductInterface::SKU] = rawurldecode($productData[ProductInterface::SKU]['soap']);
foreach ([ProductInterface::SKU, ProductInterface::NAME, ProductInterface::PRICE] as $key) {
$this->assertEquals($productData[$key], $response[$key]);
}
$this->assertEquals([1], $response[ProductInterface::EXTENSION_ATTRIBUTES_KEY]["website_ids"]);
}

/**
* Test removing association between product and website 1
* @magentoApiDataFixture Magento/Catalog/_files/product_with_two_websites.php
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,12 @@ public function setUp()

/**
* @param array $result
* @param string $productSku
*
* @return array
*/
protected function getStockItemBySku($result)
protected function getStockItemBySku($result, $productSku)
{
$productSku = 'simple1';
$serviceInfo = [
'rest' => [
'resourcePath' => self::RESOURCE_GET_PATH . "/$productSku",
Expand All @@ -69,22 +70,41 @@ protected function getStockItemBySku($result)
$apiResult = $this->_webApiCall($serviceInfo, $arguments);
$result['item_id'] = $apiResult['item_id'];
$this->assertEquals($result, array_intersect_key($apiResult, $result), 'The stock data does not match.');

return $apiResult;
}

/**
* @param array $newData
* @param array $expectedResult
* @param array $fixtureData
*
* @magentoApiDataFixture Magento/Catalog/_files/multiple_products.php
* @dataProvider saveStockItemBySkuWithWrongInputDataProvider
*/
public function testStockItemPUTWithWrongInput($newData, $expectedResult, $fixtureData)
{
$stockItemOld = $this->getStockItemBySku($fixtureData);
$productSku = 'simple1';
$stockItemOld = $this->getStockItemBySku($fixtureData, $productSku);
$itemId = $stockItemOld['item_id'];

$actualData = $this->updateStockItemBySku($productSku, $itemId, $newData);

$this->assertEquals($stockItemOld['item_id'], $actualData);

/** @var \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory */
$stockItemFactory = $this->objectManager
->get(\Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory::class);
$stockItem = $stockItemFactory->create();
/** @var \Magento\CatalogInventory\Model\ResourceModel\Stock\Item $stockItemResource */
$stockItemResource = $this->objectManager->get(\Magento\CatalogInventory\Model\ResourceModel\Stock\Item::class);
$stockItemResource->loadByProductId($stockItem, $stockItemOld['product_id'], $stockItemOld['stock_id']);
$expectedResult['item_id'] = $stockItem->getItemId();
$this->assertEquals($expectedResult, array_intersect_key($stockItem->getData(), $expectedResult));
}

private function updateStockItemBySku($productSku, $itemId, $newData)
{
$resourcePath = str_replace([':productSku', ':itemId'], [$productSku, $itemId], self::RESOURCE_PUT_PATH);

$serviceInfo = [
Expand Down Expand Up @@ -113,7 +133,124 @@ public function testStockItemPUTWithWrongInput($newData, $expectedResult, $fixtu
$data = $stockItemDetailsDo->getData();
$data['show_default_notification_message'] = false;
$arguments = ['productSku' => $productSku, 'stockItem' => $data];
$this->assertEquals($stockItemOld['item_id'], $this->_webApiCall($serviceInfo, $arguments));

return $this->_webApiCall($serviceInfo, $arguments);
}

/**
* @return array
*/
public function saveStockItemBySkuWithWrongInputDataProvider()
{
return [
[
[
'item_id' => 222,
'product_id' => 222,
'stock_id' => 1,
'qty' => '111.0000',
'min_qty' => '0.0000',
'use_config_min_qty' => 1,
'is_qty_decimal' => 0,
'backorders' => 0,
'use_config_backorders' => 1,
'min_sale_qty' => '1.0000',
'use_config_min_sale_qty' => 1,
'max_sale_qty' => '0.0000',
'use_config_max_sale_qty' => 1,
'is_in_stock' => 1,
'low_stock_date' => '',
'notify_stock_qty' => null,
'use_config_notify_stock_qty' => 1,
'manage_stock' => 0,
'use_config_manage_stock' => 1,
'stock_status_changed_auto' => 0,
'use_config_qty_increments' => 1,
'qty_increments' => '0.0000',
'use_config_enable_qty_inc' => 1,
'enable_qty_increments' => 0,
'is_decimal_divided' => 0,
],
[
'item_id' => '1',
'product_id' => '10',
'stock_id' => '1',
'qty' => '111.0000',
'min_qty' => '0.0000',
'use_config_min_qty' => '1',
'is_qty_decimal' => '0',
'backorders' => '0',
'use_config_backorders' => '1',
'min_sale_qty' => '1.0000',
'use_config_min_sale_qty' => '1',
'max_sale_qty' => '0.0000',
'use_config_max_sale_qty' => '1',
'is_in_stock' => '1',
'low_stock_date' => null,
'notify_stock_qty' => null,
'use_config_notify_stock_qty' => '1',
'manage_stock' => '0',
'use_config_manage_stock' => '1',
'stock_status_changed_auto' => '0',
'use_config_qty_increments' => '1',
'qty_increments' => '0.0000',
'use_config_enable_qty_inc' => '1',
'enable_qty_increments' => '0',
'is_decimal_divided' => '0',
'type_id' => 'simple',
],
[
'item_id' => 1,
'product_id' => 10,
'stock_id' => 1,
'qty' => 100,
'is_in_stock' => 1,
'is_qty_decimal' => '',
'show_default_notification_message' => '',
'use_config_min_qty' => 1,
'min_qty' => 0,
'use_config_min_sale_qty' => 1,
'min_sale_qty' => 1,
'use_config_max_sale_qty' => 1,
'max_sale_qty' => 10000,
'use_config_backorders' => 1,
'backorders' => 0,
'use_config_notify_stock_qty' => 1,
'notify_stock_qty' => 1,
'use_config_qty_increments' => 1,
'qty_increments' => 0,
'use_config_enable_qty_inc' => 1,
'enable_qty_increments' => '',
'use_config_manage_stock' => 1,
'manage_stock' => 1,
'low_stock_date' => '',
'is_decimal_divided' => '',
'stock_status_changed_auto' => 0,
],
],
];
}

/**
* @param array $newData
* @param array $expectedResult
* @param array $fixtureData
*
* @magentoApiDataFixture Magento/Catalog/_files/product_simple_sku_with_slash.php
* @dataProvider testUpdateStockItemBySkuDataProvider
*/
public function testUpdateStockItemBySku($newData, $expectedResult, $fixtureData)
{
$productSku = [
'rest' => 'sku%252fwith%252fslashes',
'soap' => 'sku%2fwith%2fslashes'
];
$stockItemOld = $this->getStockItemBySku($fixtureData, $productSku[TESTS_WEB_API_ADAPTER]);
$itemId = $stockItemOld['item_id'];

$actualData = $this->updateStockItemBySku($productSku[TESTS_WEB_API_ADAPTER], $itemId, $newData);

$this->assertEquals($stockItemOld['item_id'], $actualData);

/** @var \Magento\CatalogInventory\Api\Data\StockItemInterfaceFactory $stockItemFactory */
$stockItemFactory = $this->objectManager
Expand All @@ -126,10 +263,7 @@ public function testStockItemPUTWithWrongInput($newData, $expectedResult, $fixtu
$this->assertEquals($expectedResult, array_intersect_key($stockItem->getData(), $expectedResult));
}

/**
* @return array
*/
public function saveStockItemBySkuWithWrongInputDataProvider()
public function testUpdateStockItemBySkuDataProvider()
{
return [
[
Expand Down Expand Up @@ -162,7 +296,7 @@ public function saveStockItemBySkuWithWrongInputDataProvider()
],
[
'item_id' => '1',
'product_id' => '10',
'product_id' => '1',
'stock_id' => '1',
'qty' => '111.0000',
'min_qty' => '0.0000',
Expand Down Expand Up @@ -190,7 +324,7 @@ public function saveStockItemBySkuWithWrongInputDataProvider()
],
[
'item_id' => 1,
'product_id' => 10,
'product_id' => 1,
'stock_id' => 1,
'qty' => 100,
'is_in_stock' => 1,
Expand Down
Loading

0 comments on commit e982176

Please sign in to comment.