Skip to content

Commit e9f7398

Browse files
committed
Merge remote-tracking branch 'origin/AC-746' into spartans_pr_07072025_v2
2 parents 84bd3cc + 4f2cbfa commit e9f7398

File tree

2 files changed

+227
-3
lines changed

2 files changed

+227
-3
lines changed
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
<?php
2+
/**
3+
* Copyright 2025 Adobe
4+
* All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
declare(strict_types=1);
8+
9+
namespace Magento\Framework\Webapi;
10+
11+
use Magento\Catalog\Test\Fixture\Product as ProductFixture;
12+
use Magento\Indexer\Test\Fixture\Indexer as IndexerFixture;
13+
use Magento\Quote\Test\Fixture\AddProductToCart as AddProductToCartFixture;
14+
use Magento\Quote\Test\Fixture\GuestCart as GuestCartFixture;
15+
use Magento\TestFramework\Fixture\DataFixture;
16+
use Magento\TestFramework\Fixture\DataFixtureStorage;
17+
use Magento\TestFramework\Fixture\DataFixtureStorageManager;
18+
use Magento\TestFramework\TestCase\WebapiAbstract;
19+
20+
/**
21+
* Test API error processor for malformed requests/bodies.
22+
*/
23+
class ApiErrorProcessorTest extends WebapiAbstract
24+
{
25+
private const RESOURCE_PATH = '/V1/';
26+
27+
/**
28+
* @var DataFixtureStorage
29+
*/
30+
protected $fixtures;
31+
32+
protected function setUp(): void
33+
{
34+
$this->_markTestAsRestOnly();
35+
parent::setUp();
36+
/** @var DataFixtureStorage $fixtures */
37+
$this->fixtures = DataFixtureStorageManager::getStorage();
38+
}
39+
40+
/**
41+
* Test that the API returns a 400 error when the request params are malformed.
42+
*
43+
* @param array $requestData
44+
* @param string $endpoint
45+
* @param int $expectedExceptionCode
46+
*
47+
* @dataProvider malformedRequestParamsDataProvider
48+
*/
49+
public function testMalformedRequestParams(array $requestData, string $endpoint, int $expectedExceptionCode)
50+
{
51+
$this->expectException(\Exception::class);
52+
$this->expectExceptionCode($expectedExceptionCode);
53+
54+
$serviceInfo = [
55+
'rest' => [
56+
'resourcePath' => self::RESOURCE_PATH . $endpoint . '?' . http_build_query($requestData),
57+
'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_GET,
58+
]
59+
];
60+
61+
$this->_webApiCall($serviceInfo, $requestData);
62+
}
63+
64+
/**
65+
* Data provider for testMalformedRequestParams
66+
*
67+
* @return array
68+
*/
69+
public static function malformedRequestParamsDataProvider()
70+
{
71+
return [
72+
'empty_filter_groups_value' => [
73+
'requestData' => [
74+
'searchCriteria' => [
75+
'filterGroups' => [
76+
[
77+
'filters' => [
78+
[
79+
'field' => 'string'
80+
]
81+
]
82+
]
83+
]
84+
]
85+
],
86+
'endpoint' => 'coupons/search',
87+
'expectedExceptionCode' => 400,
88+
],
89+
'empty_filter_groups_value2' => [
90+
'requestData' => [
91+
'searchCriteria' => [
92+
'filterGroups' => [
93+
[
94+
'filters' => [
95+
[
96+
'field' => 'string'
97+
]
98+
]
99+
]
100+
]
101+
]
102+
],
103+
'endpoint' => 'categories/attributes',
104+
'expectedExceptionCode' => 400,
105+
],
106+
'empty_sort_orders' => [
107+
'requestData' => [
108+
'searchCriteria' => [
109+
'sortOrders' => [
110+
[
111+
'field' => 'string'
112+
]
113+
]
114+
]
115+
],
116+
'endpoint' => 'cmsPage/search',
117+
'expectedExceptionCode' => 400,
118+
]
119+
];
120+
}
121+
122+
/**
123+
* Test that the POST API returns a 400 error when the request body is malformed.
124+
*/
125+
#[
126+
DataFixture(ProductFixture::class, as: 'product'),
127+
DataFixture(IndexerFixture::class, as: 'indexer'),
128+
DataFixture(GuestCartFixture::class, as: 'cart'),
129+
DataFixture(AddProductToCartFixture::class, ['cart_id' => '$cart.id$', 'product_id' => '$product.id$']),
130+
]
131+
public function testPOSTWithMalformedBody(): void
132+
{
133+
$this->expectException(\Exception::class);
134+
$this->expectExceptionCode(400);
135+
136+
$cart = $this->fixtures->get('cart');
137+
/** @var \Magento\Quote\Model\QuoteIdMask $quoteIdMask */
138+
$quoteIdMask = \Magento\TestFramework\Helper\Bootstrap::getObjectManager()
139+
->create(\Magento\Quote\Model\QuoteIdMaskFactory::class)
140+
->create();
141+
$quoteIdMask->load($cart->getId(), 'quote_id');
142+
//Use masked cart Id
143+
$cartId = $quoteIdMask->getMaskedId();
144+
145+
$serviceInfo = [
146+
'rest' => [
147+
'resourcePath' => self::RESOURCE_PATH . 'guest-carts/' . $cartId . '/shipping-information',
148+
'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_POST,
149+
],
150+
];
151+
$requestData = [
152+
"addressInformation" => [
153+
"extension_attributes" => [
154+
"discounts" => [
155+
[
156+
"discount_data" => [
157+
"amount" => 0
158+
]
159+
]
160+
]
161+
]
162+
]
163+
];
164+
$this->_webApiCall($serviceInfo, $requestData);
165+
}
166+
167+
/**
168+
* Test that the PUT API returns a 400 error when the request body is malformed.
169+
*/
170+
#[
171+
DataFixture(ProductFixture::class, as: 'product'),
172+
DataFixture(IndexerFixture::class, as: 'indexer'),
173+
]
174+
public function testPUTWithMalformedBody(): void
175+
{
176+
$this->expectException(\Exception::class);
177+
$this->expectExceptionCode(400);
178+
179+
$product = $this->fixtures->get('product');
180+
$sku = $product->getSku();
181+
182+
$serviceInfo = [
183+
'rest' => [
184+
'resourcePath' => self::RESOURCE_PATH . 'products/' . $sku,
185+
'httpMethod' => \Magento\Framework\Webapi\Rest\Request::HTTP_METHOD_PUT,
186+
],
187+
];
188+
$requestData = [
189+
"product" => [
190+
"extension_attributes" => [
191+
"stock_item" => [
192+
"show_default_notification_message" => true
193+
]
194+
]
195+
]
196+
];
197+
$this->_webApiCall($serviceInfo, $requestData);
198+
}
199+
}

lib/internal/Magento/Framework/Webapi/ErrorProcessor.php

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22
/**
3-
* Copyright © Magento, Inc. All rights reserved.
4-
* See COPYING.txt for license details.
3+
* Copyright 2012 Adobe
4+
* All rights reserved.
55
*/
66
declare(strict_types=1);
77

@@ -145,6 +145,8 @@ public function maskException(\Exception $exception)
145145
$stackTrace
146146
);
147147
} else {
148+
// Check if this is a client error based on the exception type
149+
$httpCode = $this->getClientErrorHttpCode($exception);
148150
$message = $exception->getMessage();
149151
$code = $exception->getCode();
150152
//if not in Dev mode, make sure the message and code is masked for unanticipated exceptions
@@ -157,7 +159,7 @@ public function maskException(\Exception $exception)
157159
$maskedException = new WebapiException(
158160
new Phrase($message),
159161
$code,
160-
WebapiException::HTTP_INTERNAL_ERROR,
162+
$httpCode,
161163
[],
162164
'',
163165
null,
@@ -167,6 +169,29 @@ public function maskException(\Exception $exception)
167169
return $maskedException;
168170
}
169171

172+
/**
173+
* Return the HTTP code for a client error based on the exception type
174+
*
175+
* @param \Exception $exception
176+
* @return int
177+
*/
178+
private function getClientErrorHttpCode(\Exception $exception)
179+
{
180+
// Check if this is a client error based on the exception type
181+
if ($exception instanceof \Zend_Db_Exception
182+
|| $exception instanceof \Zend_Db_Adapter_Exception
183+
|| $exception instanceof \Zend_Db_Statement_Exception
184+
|| $exception instanceof \PDOException
185+
|| $exception instanceof \InvalidArgumentException
186+
|| $exception instanceof \BadMethodCallException
187+
|| $exception instanceof \UnexpectedValueException
188+
|| $exception instanceof \Magento\Framework\Search\Request\NonExistingRequestNameException
189+
) {
190+
return WebapiException::HTTP_BAD_REQUEST;
191+
}
192+
return WebapiException::HTTP_INTERNAL_ERROR;
193+
}
194+
170195
/**
171196
* Process API exception.
172197
*

0 commit comments

Comments
 (0)