Skip to content

Commit af42bca

Browse files
committed
Fixed the problem with products create/update REST API requests when request body data is sent as "xml" (exception during preparing response data).
1 parent 6b6f428 commit af42bca

File tree

3 files changed

+214
-1
lines changed

3 files changed

+214
-1
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model\Product\Webapi\Rest;
9+
10+
use Magento\Framework\Webapi\Rest\Request\DeserializerInterface;
11+
use Magento\Framework\Webapi\Rest\Request\DeserializerFactory;
12+
use Magento\Framework\Webapi\Rest\Request;
13+
14+
/**
15+
* Class RequestTypeBasedDeserializer
16+
*
17+
* Used for deserialization rest request body.
18+
* Runs appropriate deserialization class object based on request body content type.
19+
*/
20+
class RequestTypeBasedDeserializer implements DeserializerInterface
21+
{
22+
/**
23+
* @var Request
24+
*/
25+
private $request;
26+
27+
/**
28+
* @var DeserializerFactory
29+
*/
30+
private $deserializeFactory;
31+
32+
/**
33+
* RequestTypeBasedDeserializer constructor.
34+
*
35+
* @param DeserializerFactory $deserializeFactory
36+
* @param Request $request
37+
*/
38+
public function __construct(
39+
DeserializerFactory $deserializeFactory,
40+
Request $request
41+
) {
42+
$this->deserializeFactory = $deserializeFactory;
43+
$this->request = $request;
44+
}
45+
46+
/**
47+
* @inheritdoc
48+
*
49+
* Parse request body into array of params with identifying request body content type
50+
* to use appropriate instance of deserializer class
51+
*
52+
* @param string $body Posted content from request
53+
* @return array|null Return NULL if content is invalid
54+
* @throws \Magento\Framework\Exception\InputException
55+
* @throws \Magento\Framework\Webapi\Exception
56+
*/
57+
public function deserialize($body)
58+
{
59+
$deserializer = $this->deserializeFactory->get($this->request->getContentType());
60+
return $deserializer->deserialize($body);
61+
}
62+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\Catalog\Test\Unit\Model\Product\Webapi\Rest;
8+
9+
use Magento\Catalog\Model\Product\Webapi\Rest\RequestTypeBasedDeserializer;
10+
use Magento\Framework\Webapi\Rest\Request\DeserializerFactory;
11+
use Magento\Framework\Webapi\Rest\Request;
12+
use PHPUnit\Framework\MockObject\MockObject;
13+
use Magento\Framework\Webapi\Rest\Request\DeserializerInterface;
14+
use Magento\Framework\Webapi\Rest\Request\Deserializer\Json as DeserializerJson;
15+
use Magento\Framework\Webapi\Rest\Request\Deserializer\Xml as DeserializerXml;
16+
use Magento\Framework\App\State;
17+
use Magento\Framework\Json\Decoder;
18+
use Magento\Framework\Serialize\Serializer\Json as SerializerJson;
19+
use Magento\Framework\Xml\Parser as ParserXml;
20+
21+
class RequestTypeBasedDeserializerTest extends \PHPUnit\Framework\TestCase
22+
{
23+
/** @var RequestTypeBasedDeserializer */
24+
private $requestTypeBasedDeserializer;
25+
/**
26+
* @var DeserializerFactory|MockObject
27+
*/
28+
private $deserializeFactoryMock;
29+
30+
/**
31+
* @var Request|MockObject
32+
*/
33+
private $requestMock;
34+
35+
public function setUp()
36+
{
37+
/** @var DeserializerFactory|MockObject $deserializeFactoryMock */
38+
$this->deserializeFactoryMock = $this->createMock(DeserializerFactory::class);
39+
/** @var Request|MockObject $requestMock */
40+
$this->requestMock = $this->createMock(Request::class);
41+
/** @var requestTypeBasedDeserializer */
42+
$this->requestTypeBasedDeserializer = new RequestTypeBasedDeserializer(
43+
$this->deserializeFactoryMock,
44+
$this->requestMock
45+
);
46+
}
47+
48+
/**
49+
* Test RequestTypeBasedDeserializer::deserializeMethod()
50+
*
51+
* @dataProvider getDeserializerDataProvider
52+
* @param string $body
53+
* @param string $contentType
54+
* @param DeserializerInterface $deserializer
55+
* @param array $expectedResult
56+
* @throws \Magento\Framework\Exception\InputException
57+
* @throws \Magento\Framework\Webapi\Exception
58+
*/
59+
public function testDeserialize(
60+
$body,
61+
$contentType,
62+
DeserializerInterface $deserializer,
63+
array $expectedResult
64+
) {
65+
$this->requestMock->method('getContentType')
66+
->willReturn($contentType);
67+
$this->deserializeFactoryMock->expects($this->any())
68+
->method('get')
69+
->with($contentType)
70+
->willReturn($deserializer);
71+
$this->assertEquals($expectedResult, $this->requestTypeBasedDeserializer->deserialize($body));
72+
}
73+
74+
public function getDeserializerDataProvider()
75+
{
76+
return [
77+
'request body with xml data' => [
78+
'body' => '<products>
79+
<product>
80+
<sku>testSku1</sku>
81+
<name>testName1</name>
82+
<weight>10</weight>
83+
<attribute_set_id>4</attribute_set_id>
84+
<status>1</status>
85+
</product>
86+
</products>',
87+
'content-type' => 'application/xml',
88+
'deserializer' => $this->prepareXmlDeserializer(),
89+
'expectedResult' => [
90+
'product' => [
91+
'sku' => 'testSku1',
92+
'name' => 'testName1',
93+
'weight' => '10',
94+
'attribute_set_id' => '4',
95+
'status' => '1'
96+
]
97+
]
98+
],
99+
'request body with json data' => [
100+
'body' => '{
101+
"product": {
102+
"sku": "testSku2",
103+
"name": "testName2",
104+
"weight": 5,
105+
"attribute_set_id": 4,
106+
"status": 0
107+
}
108+
}',
109+
'content-type' => 'application/json',
110+
'deserializer' => $this->prepareJsonDeserializer(),
111+
'expectedResult' => [
112+
'product' => [
113+
'sku' => 'testSku2',
114+
'name' => 'testName2',
115+
'weight' => 5,
116+
'attribute_set_id' => 4,
117+
'status' => 0
118+
]
119+
]
120+
]
121+
];
122+
}
123+
124+
/**
125+
* Creates Json Deserializer instance with some mocked parameters
126+
*
127+
* @return DeserializerJson
128+
*/
129+
private function prepareJsonDeserializer()
130+
{
131+
/** @var Decoder|MockObject $decoder */
132+
$decoder = $this->createMock(Decoder::class);
133+
/** @var State|MockObject $appStateMock */
134+
$appStateMock = $this->createMock(State::class);
135+
$serializer = new SerializerJson();
136+
return new DeserializerJson($decoder, $appStateMock, $serializer);
137+
}
138+
139+
/**
140+
* Creates XML Deserializer instance with some mocked parameters
141+
*
142+
* @return DeserializerXml
143+
*/
144+
private function prepareXmlDeserializer()
145+
{
146+
$parserXml = new ParserXml();
147+
/** @var State|MockObject $appStateMock */
148+
$appStateMock = $this->createMock(State::class);
149+
return new DeserializerXml($parserXml, $appStateMock);
150+
}
151+
}

app/code/Magento/Catalog/etc/webapi_rest/di.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
</type>
3838
<type name="Magento\Catalog\Model\Product\Webapi\ProductOutputProcessor">
3939
<arguments>
40-
<argument name="deserializer" xsi:type="object">Magento\Framework\Webapi\Rest\Request\Deserializer\Json</argument>
40+
<argument name="deserializer" xsi:type="object">Magento\Catalog\Model\Product\Webapi\Rest\RequestTypeBasedDeserializer</argument>
4141
</arguments>
4242
</type>
4343
</config>

0 commit comments

Comments
 (0)