88
99namespace Magento \WebapiAsync \Controller \Rest \Asynchronous ;
1010
11+ use Magento \Framework \Api \SimpleDataObjectConverter ;
1112use Magento \Framework \App \ObjectManager ;
1213use Magento \Framework \Exception \AuthorizationException ;
1314use Magento \Framework \Exception \InputException ;
1415use Magento \Framework \Exception \LocalizedException ;
16+ use Magento \Framework \Reflection \MethodsMap ;
1517use Magento \Framework \Webapi \Exception ;
1618use Magento \Framework \Webapi \Rest \Request as RestRequest ;
1719use Magento \Framework \Webapi \ServiceInputProcessor ;
2426
2527/**
2628 * This class is responsible for retrieving resolved input data
29+ *
30+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2731 */
2832class InputParamsResolver
2933{
@@ -61,6 +65,11 @@ class InputParamsResolver
6165 */
6266 private $ inputArraySizeLimitValue ;
6367
68+ /**
69+ * @var MethodsMap
70+ */
71+ private $ methodsMap ;
72+
6473 /**
6574 * Initialize dependencies.
6675 *
@@ -72,6 +81,7 @@ class InputParamsResolver
7281 * @param WebapiInputParamsResolver $inputParamsResolver
7382 * @param bool $isBulk
7483 * @param InputArraySizeLimitValue|null $inputArraySizeLimitValue
84+ * @param MethodsMap|null $methodsMap
7585 */
7686 public function __construct (
7787 RestRequest $ request ,
@@ -81,7 +91,8 @@ public function __construct(
8191 RequestValidator $ requestValidator ,
8292 WebapiInputParamsResolver $ inputParamsResolver ,
8393 bool $ isBulk = false ,
84- ?InputArraySizeLimitValue $ inputArraySizeLimitValue = null
94+ ?InputArraySizeLimitValue $ inputArraySizeLimitValue = null ,
95+ ?MethodsMap $ methodsMap = null
8596 ) {
8697 $ this ->request = $ request ;
8798 $ this ->paramsOverrider = $ paramsOverrider ;
@@ -92,6 +103,8 @@ public function __construct(
92103 $ this ->isBulk = $ isBulk ;
93104 $ this ->inputArraySizeLimitValue = $ inputArraySizeLimitValue ?? ObjectManager::getInstance ()
94105 ->get (InputArraySizeLimitValue::class);
106+ $ this ->methodsMap = $ methodsMap ?? ObjectManager::getInstance ()
107+ ->get (MethodsMap::class);
95108 }
96109
97110 /**
@@ -119,7 +132,13 @@ public function resolve()
119132 $ routeServiceMethod = $ route ->getServiceMethod ();
120133 $ this ->inputArraySizeLimitValue ->set ($ route ->getInputArraySizeLimit ());
121134
135+ $ this ->validateParameters ($ routeServiceClass , $ routeServiceMethod , array_keys ($ route ->getParameters ()));
136+
122137 foreach ($ inputData as $ key => $ singleEntityParams ) {
138+ if (!is_array ($ singleEntityParams )) {
139+ continue ;
140+ }
141+
123142 $ webapiResolvedParams [$ key ] = $ this ->resolveBulkItemParams (
124143 $ singleEntityParams ,
125144 $ routeServiceClass ,
@@ -143,11 +162,22 @@ public function getInputData()
143162 $ inputData = $ this ->request ->getRequestData ();
144163
145164 $ httpMethod = $ this ->request ->getHttpMethod ();
146- if ($ httpMethod == RestRequest::HTTP_METHOD_DELETE ) {
165+ if ($ httpMethod === RestRequest::HTTP_METHOD_DELETE ) {
147166 $ requestBodyParams = $ this ->request ->getBodyParams ();
148167 $ inputData = array_merge ($ requestBodyParams , $ inputData );
149168 }
150- return $ inputData ;
169+
170+ return array_map (function ($ singleEntityParams ) {
171+ if (is_array ($ singleEntityParams )) {
172+ $ singleEntityParams = $ this ->filterInputData ($ singleEntityParams );
173+ $ singleEntityParams = $ this ->paramsOverrider ->override (
174+ $ singleEntityParams ,
175+ $ this ->getRoute ()->getParameters ()
176+ );
177+ }
178+
179+ return $ singleEntityParams ;
180+ }, $ inputData );
151181 }
152182
153183 /**
@@ -180,4 +210,64 @@ private function resolveBulkItemParams(array $inputData, string $serviceClass, s
180210 {
181211 return $ this ->serviceInputProcessor ->process ($ serviceClass , $ serviceMethod , $ inputData );
182212 }
213+
214+ /**
215+ * Validates InputData
216+ *
217+ * @param array $inputData
218+ * @return array
219+ */
220+ private function filterInputData (array $ inputData ): array
221+ {
222+ $ result = [];
223+
224+ $ data = array_filter ($ inputData , function ($ k ) use (&$ result ) {
225+ $ key = is_string ($ k ) ? strtolower (str_replace ('_ ' , "" , $ k )) : $ k ;
226+ return !isset ($ result [$ key ]) && ($ result [$ key ] = true );
227+ }, ARRAY_FILTER_USE_KEY );
228+
229+ return array_map (function ($ value ) {
230+ return is_array ($ value ) ? $ this ->filterInputData ($ value ) : $ value ;
231+ }, $ data );
232+ }
233+
234+ /**
235+ * Validate that parameters are really used in the current request.
236+ *
237+ * @param string $serviceClassName
238+ * @param string $serviceMethodName
239+ * @param array $paramOverriders
240+ */
241+ private function validateParameters (
242+ string $ serviceClassName ,
243+ string $ serviceMethodName ,
244+ array $ paramOverriders
245+ ): void {
246+ $ methodParams = $ this ->methodsMap ->getMethodParams ($ serviceClassName , $ serviceMethodName );
247+ foreach ($ paramOverriders as $ key => $ param ) {
248+ $ arrayKeys = explode ('. ' , $ param ?? '' );
249+ $ value = array_shift ($ arrayKeys );
250+
251+ foreach ($ methodParams as $ serviceMethodParam ) {
252+ $ serviceMethodParamName = $ serviceMethodParam [MethodsMap::METHOD_META_NAME ];
253+ $ serviceMethodType = $ serviceMethodParam [MethodsMap::METHOD_META_TYPE ];
254+
255+ $ camelCaseValue = SimpleDataObjectConverter::snakeCaseToCamelCase ($ value );
256+ if ($ serviceMethodParamName === $ value || $ serviceMethodParamName === $ camelCaseValue ) {
257+ if (count ($ arrayKeys ) > 0 ) {
258+ $ camelCaseKey = SimpleDataObjectConverter::snakeCaseToCamelCase ('set_ ' . $ arrayKeys [0 ]);
259+ $ this ->validateParameters ($ serviceMethodType , $ camelCaseKey , [implode ('. ' , $ arrayKeys )]);
260+ }
261+ unset($ paramOverriders [$ key ]);
262+ break ;
263+ }
264+ }
265+ }
266+
267+ if (!empty ($ paramOverriders )) {
268+ $ message = 'The current request does not expect the next parameters: '
269+ . implode (', ' , $ paramOverriders );
270+ throw new \UnexpectedValueException (__ ($ message )->__toString ());
271+ }
272+ }
183273}
0 commit comments