AbstractSchemaGenerator.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. <?php
  2. /**
  3. *
  4. * Copyright © Magento, Inc. All rights reserved.
  5. * See COPYING.txt for license details.
  6. */
  7. namespace Magento\Webapi\Model;
  8. use Magento\Framework\App\ObjectManager;
  9. use Magento\Framework\Serialize\Serializer\Json;
  10. use Magento\Framework\Webapi\Authorization;
  11. use Magento\Framework\Webapi\CustomAttribute\ServiceTypeListInterface;
  12. use Magento\Webapi\Model\Cache\Type\Webapi;
  13. /**
  14. * Abstract API schema generator.
  15. */
  16. abstract class AbstractSchemaGenerator
  17. {
  18. /**
  19. * @var Webapi
  20. */
  21. protected $cache;
  22. /**
  23. * @var \Magento\Framework\Reflection\TypeProcessor
  24. */
  25. protected $typeProcessor;
  26. /**
  27. * @var \Magento\Framework\Webapi\CustomAttribute\ServiceTypeListInterface
  28. */
  29. protected $serviceTypeList;
  30. /**
  31. * @var ServiceMetadata
  32. */
  33. protected $serviceMetadata;
  34. /**
  35. * @var Authorization
  36. */
  37. protected $authorization;
  38. /**
  39. * Instance of serializer.
  40. *
  41. * @var Json
  42. */
  43. private $serializer;
  44. /**
  45. * Initialize dependencies.
  46. *
  47. * @param Webapi $cache
  48. * @param \Magento\Framework\Reflection\TypeProcessor $typeProcessor
  49. * @param \Magento\Framework\Webapi\CustomAttribute\ServiceTypeListInterface $serviceTypeList
  50. * @param \Magento\Webapi\Model\ServiceMetadata $serviceMetadata
  51. * @param Authorization $authorization
  52. * @param Json|null $serializer
  53. */
  54. public function __construct(
  55. Webapi $cache,
  56. \Magento\Framework\Reflection\TypeProcessor $typeProcessor,
  57. ServiceTypeListInterface $serviceTypeList,
  58. ServiceMetadata $serviceMetadata,
  59. Authorization $authorization,
  60. Json $serializer = null
  61. ) {
  62. $this->cache = $cache;
  63. $this->typeProcessor = $typeProcessor;
  64. $this->serviceTypeList = $serviceTypeList;
  65. $this->serviceMetadata = $serviceMetadata;
  66. $this->authorization = $authorization;
  67. $this->serializer = $serializer ?: ObjectManager::getInstance()->get(Json::class);
  68. }
  69. /**
  70. * Retrieve a list of all services.
  71. *
  72. * @return string[]
  73. */
  74. public function getListOfServices()
  75. {
  76. return array_keys($this->serviceMetadata->getServicesConfig());
  77. }
  78. /**
  79. * Generate schema based on requested services (uses cache)
  80. *
  81. * @param array $requestedServices
  82. * @param string $requestScheme
  83. * @param string $requestHost
  84. * @param string $endPointUrl
  85. * @return string
  86. */
  87. public function generate($requestedServices, $requestScheme, $requestHost, $endPointUrl)
  88. {
  89. /** Sort requested services by names to prevent caching of the same schema file more than once. */
  90. ksort($requestedServices);
  91. $prefix = get_class($this) . $this->serializer->serialize($requestedServices);
  92. $cacheId = $this->cache->generateCacheIdUsingContext($prefix);
  93. $cachedSchemaContent = $this->cache->load($cacheId);
  94. if ($cachedSchemaContent !== false) {
  95. return $cachedSchemaContent;
  96. }
  97. $allowedServicesMetadata = $this->getAllowedServicesMetadata($requestedServices);
  98. $this->collectCallInfo($allowedServicesMetadata);
  99. $schemaContent = $this->generateSchema($allowedServicesMetadata, $requestScheme, $requestHost, $endPointUrl);
  100. $this->cache->save($schemaContent, $cacheId, [Webapi::CACHE_TAG]);
  101. return $schemaContent;
  102. }
  103. /**
  104. * Generate schema based on requested services' metadata.
  105. *
  106. * @param array $requestedServiceMetadata
  107. * @param string $requestScheme
  108. * @param string $requestHost
  109. * @param string $requestUri
  110. * @return string
  111. */
  112. abstract protected function generateSchema($requestedServiceMetadata, $requestScheme, $requestHost, $requestUri);
  113. /**
  114. * Get service metadata
  115. *
  116. * @param string $serviceName
  117. * @return array
  118. */
  119. abstract protected function getServiceMetadata($serviceName);
  120. /**
  121. * Get name of complexType for message element.
  122. *
  123. * @param string $messageName
  124. * @return string
  125. */
  126. public function getElementComplexTypeName($messageName)
  127. {
  128. return ucfirst($messageName);
  129. }
  130. /**
  131. * Collect data about complex types call info.
  132. *
  133. * Walks through all requested services and checks all methods 'in' and 'out' parameters.
  134. *
  135. * @param array $requestedServiceMetadata
  136. * @return void
  137. */
  138. protected function collectCallInfo($requestedServiceMetadata)
  139. {
  140. foreach ($requestedServiceMetadata as $serviceName => $serviceData) {
  141. foreach ($serviceData['methods'] as $methodName => $methodData) {
  142. $this->typeProcessor->processInterfaceCallInfo($methodData['interface'], $serviceName, $methodName);
  143. }
  144. }
  145. }
  146. /**
  147. * Retrieve information only about those services/methods which are visible to current user.
  148. *
  149. * @param string[] $requestedServices
  150. * @return array
  151. */
  152. protected function getAllowedServicesMetadata($requestedServices)
  153. {
  154. $allowedServicesMetadata = [];
  155. foreach ($requestedServices as $serviceName) {
  156. $serviceMetadata = $this->getServiceMetadata($serviceName);
  157. foreach ($serviceMetadata[ServiceMetadata::KEY_SERVICE_METHODS] as $methodName => $methodData) {
  158. if (!$this->authorization->isAllowed($methodData[ServiceMetadata::KEY_ACL_RESOURCES])) {
  159. unset($serviceMetadata[ServiceMetadata::KEY_SERVICE_METHODS][$methodName]);
  160. }
  161. }
  162. if (!empty($serviceMetadata[ServiceMetadata::KEY_SERVICE_METHODS])) {
  163. $this->removeRestrictedRoutes($serviceMetadata);
  164. $allowedServicesMetadata[$serviceName] = $serviceMetadata;
  165. }
  166. }
  167. return $allowedServicesMetadata;
  168. }
  169. /**
  170. * Remove routes which should not be visible to current user.
  171. *
  172. * @param array &$serviceMetadata
  173. * @return void
  174. */
  175. protected function removeRestrictedRoutes(&$serviceMetadata)
  176. {
  177. $allowedMethodNames = array_keys($serviceMetadata[ServiceMetadata::KEY_SERVICE_METHODS]);
  178. /** Remove routes which reference methods not visible to current user */
  179. if (isset($serviceMetadata[ServiceMetadata::KEY_ROUTES])) {
  180. foreach ($serviceMetadata[ServiceMetadata::KEY_ROUTES] as $path => &$routeGroup) {
  181. foreach ($routeGroup as $httpMethod => &$route) {
  182. if (!in_array($route[ServiceMetadata::KEY_ROUTE_METHOD], $allowedMethodNames)) {
  183. unset($routeGroup[$httpMethod]);
  184. }
  185. }
  186. if (empty($routeGroup)) {
  187. unset($serviceMetadata[ServiceMetadata::KEY_ROUTES][$path]);
  188. }
  189. }
  190. }
  191. }
  192. }