123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- <?php
- /**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
- namespace Magento\Webapi\Controller\Rest;
- use Magento\Framework\Webapi\Rest\Request\ParamOverriderInterface;
- use Magento\Webapi\Model\Config\Converter;
- use Magento\Framework\Reflection\MethodsMap;
- use Magento\Framework\Api\SimpleDataObjectConverter;
- /**
- * Override parameter values
- */
- class ParamsOverrider
- {
- /**
- * @var ParamOverriderInterface[]
- */
- private $paramOverriders;
- /**
- * @var MethodsMap
- */
- private $methodsMap;
- /**
- * Initialize dependencies
- *
- * @param ParamOverriderInterface[] $paramOverriders
- */
- public function __construct(
- array $paramOverriders = []
- ) {
- $this->paramOverriders = $paramOverriders;
- }
- /**
- * Override parameter values based on webapi.xml
- *
- * @param array $inputData Incoming data from request
- * @param array $parameters Contains parameters to replace or default
- * @return array Data in same format as $inputData with appropriate parameters added or changed
- */
- public function override(array $inputData, array $parameters)
- {
- foreach ($parameters as $name => $paramData) {
- $arrayKeys = explode('.', $name);
- if ($paramData[Converter::KEY_FORCE] || !$this->isNestedArrayValueSet($inputData, $arrayKeys)) {
- $paramValue = $paramData[Converter::KEY_VALUE];
- if (isset($this->paramOverriders[$paramValue])) {
- $value = $this->paramOverriders[$paramValue]->getOverriddenValue();
- } else {
- $value = $paramData[Converter::KEY_VALUE];
- }
- $this->setNestedArrayValue($inputData, $arrayKeys, $value);
- }
- }
- return $inputData;
- }
- /**
- * Determine if a nested array value is set.
- *
- * @param array &$nestedArray
- * @param string[] $arrayKeys
- * @return bool true if array value is set
- */
- protected function isNestedArrayValueSet(&$nestedArray, $arrayKeys)
- {
- $currentArray = &$nestedArray;
- foreach ($arrayKeys as $key) {
- if (!isset($currentArray[$key])) {
- return false;
- }
- $currentArray = &$currentArray[$key];
- }
- return true;
- }
- /**
- * Set a nested array value.
- *
- * @param array &$nestedArray
- * @param string[] $arrayKeys
- * @param string $valueToSet
- * @return void
- */
- protected function setNestedArrayValue(&$nestedArray, $arrayKeys, $valueToSet)
- {
- $currentArray = &$nestedArray;
- $lastKey = array_pop($arrayKeys);
- foreach ($arrayKeys as $key) {
- if (!isset($currentArray[$key])) {
- $currentArray[$key] = [];
- }
- $currentArray = &$currentArray[$key];
- }
- $currentArray[$lastKey] = $valueToSet;
- }
- /**
- * Override request body property value with matching url path parameter value
- *
- * This method assumes that webapi.xml url defines the substitution parameter as camelCase to the actual
- * snake case key described as part of the api contract. example: /:parentId/nestedResource/:entityId.
- * Here :entityId value will be used for overriding 'entity_id' property in the body.
- * Since Webapi framework allows both camelCase and snakeCase, either of them will be substituted for now.
- * If the request body is missing url path parameter as property, it will be added to the body.
- * This method works only requests with scalar properties at top level or properties of single object embedded
- * in the request body.
- * Only the last path parameter value will be substituted from the url in case of multiple parameters.
- *
- * @param array $urlPathParams url path parameters as array
- * @param array $requestBodyParams body parameters as array
- * @param string $serviceClassName name of the service class that we are trying to call
- * @param string $serviceMethodName name of the method that we are trying to call
- * @return array
- */
- public function overrideRequestBodyIdWithPathParam(
- array $urlPathParams,
- array $requestBodyParams,
- $serviceClassName,
- $serviceMethodName
- ) {
- if (empty($urlPathParams)) {
- return $requestBodyParams;
- }
- $pathParamValue = end($urlPathParams);
- // Self apis should not be overridden
- if ($pathParamValue === 'me') {
- return $requestBodyParams;
- }
- $pathParamKey = key($urlPathParams);
- // Check if the request data is a top level object of body
- if (count($requestBodyParams) == 1 && is_array(end($requestBodyParams))) {
- $requestDataKey = key($requestBodyParams);
- if ($this->isPropertyDeclaredInDataObject(
- $serviceClassName,
- $serviceMethodName,
- $requestDataKey,
- $pathParamKey
- )
- ) {
- $this->substituteParameters($requestBodyParams[$requestDataKey], $pathParamKey, $pathParamValue);
- } else {
- $this->substituteParameters($requestBodyParams, $pathParamKey, $pathParamValue);
- }
- } else { // Else parameters passed as scalar values in body will be overridden
- $this->substituteParameters($requestBodyParams, $pathParamKey, $pathParamValue);
- }
- return $requestBodyParams;
- }
- /**
- * Check presence for both camelCase and snake_case keys in array and substitute if either is present
- *
- * @param array $requestData
- * @param string $key
- * @param string $value
- * @return void
- */
- private function substituteParameters(array &$requestData, $key, $value)
- {
- $snakeCaseKey = SimpleDataObjectConverter::camelCaseToSnakeCase($key);
- $camelCaseKey = SimpleDataObjectConverter::snakeCaseToCamelCase($key);
- if (isset($requestData[$camelCaseKey])) {
- $requestData[$camelCaseKey] = $value;
- } else {
- $requestData[$snakeCaseKey] = $value;
- }
- }
- /**
- * Verify property in parameter's object
- *
- * @param string $serviceClassName name of the service class that we are trying to call
- * @param string $serviceMethodName name of the method that we are trying to call
- * @param string $serviceMethodParamName
- * @param string $objectProperty
- * @return bool
- */
- private function isPropertyDeclaredInDataObject(
- $serviceClassName,
- $serviceMethodName,
- $serviceMethodParamName,
- $objectProperty
- ) {
- if ($serviceClassName && $serviceMethodName) {
- $methodParams = $this->getMethodsMap()->getMethodParams($serviceClassName, $serviceMethodName);
- $index = array_search($serviceMethodParamName, array_column($methodParams, 'name'));
- if ($index !== false) {
- $paramObjectType = $methodParams[$index][MethodsMap::METHOD_META_TYPE];
- $setter = 'set' . SimpleDataObjectConverter::snakeCaseToUpperCamelCase($objectProperty);
- if (array_key_exists(
- $setter,
- $this->getMethodsMap()->getMethodsMap($paramObjectType)
- )) {
- return true;
- }
- }
- }
- return false;
- }
- /**
- * The getter function to get MethodsMap object
- *
- * @return \Magento\Framework\Reflection\MethodsMap
- *
- * @deprecated 100.1.0
- */
- private function getMethodsMap()
- {
- if ($this->methodsMap === null) {
- $this->methodsMap = \Magento\Framework\App\ObjectManager::getInstance()
- ->get(MethodsMap::class);
- }
- return $this->methodsMap;
- }
- }
|