Skip to content

Commit 5922ccb

Browse files
committed
Merge remote-tracking branch 'bulk/issue-17-custom-route-for-bulk-api' into bulk-custom-route
2 parents fbed07f + 0a49f6e commit 5922ccb

File tree

12 files changed

+187
-9
lines changed

12 files changed

+187
-9
lines changed

app/code/Magento/WebapiAsync/Model/ServiceConfig.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*/
2121
class ServiceConfig
2222
{
23-
const CACHE_ID = 'webapi_async_config';
23+
const CACHE_ID = 'webapi_async_service_config';
2424

2525
/**
2626
* @var WebapiCache
@@ -71,7 +71,7 @@ public function getServices()
7171
if ($services && is_string($services)) {
7272
$this->services = $this->serializer->unserialize($services);
7373
} else {
74-
$this->services = $this->configReader->read()[Converter::KEY_SERVICES] ?? [];
74+
$this->services = $this->configReader->read();
7575
$this->cache->save($this->serializer->serialize($this->services), self::CACHE_ID);
7676
}
7777
}

app/code/Magento/WebapiAsync/Model/ServiceConfig/Converter.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,16 @@ class Converter implements \Magento\Framework\Config\ConverterInterface
2020
const KEY_METHOD = 'method';
2121
const KEY_METHODS = 'methods';
2222
const KEY_SYNCHRONOUS_INVOCATION_ONLY = 'synchronousInvocationOnly';
23+
const KEY_ROUTES = 'routes';
2324
/**#@-*/
2425

26+
private $allowedRouteMethods = [
27+
\Magento\Webapi\Model\Rest\Config::HTTP_METHOD_GET,
28+
\Magento\Webapi\Model\Rest\Config::HTTP_METHOD_POST,
29+
\Magento\Webapi\Model\Rest\Config::HTTP_METHOD_PUT,
30+
\Magento\Webapi\Model\Rest\Config::HTTP_METHOD_DELETE,
31+
];
32+
2533
/**
2634
* {@inheritdoc}
2735
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
@@ -44,6 +52,7 @@ public function convert($source)
4452
$this->initServiceMethodsKey($result, $serviceClass, $serviceMethod);
4553
$this->mergeSynchronousInvocationMethodsData($service, $result, $serviceClass, $serviceMethod);
4654
}
55+
$result[self::KEY_ROUTES] = $this->convertRouteCustomizations($source);
4756

4857
return $result;
4958
}
@@ -158,4 +167,69 @@ private function isSynchronousInvocationOnlyTrue(\DOMElement $synchronousInvocat
158167

159168
return filter_var($synchronousInvocationOnlyNode->nodeValue, FILTER_VALIDATE_BOOLEAN);
160169
}
170+
171+
/**
172+
* Convert and merge "route" nodes, which represent route customizations
173+
* @param \DOMDocument $source
174+
* @return array
175+
*
176+
*/
177+
private function convertRouteCustomizations($source)
178+
{
179+
$customRoutes = [];
180+
$routes = $source->getElementsByTagName('route');
181+
/** @var \DOMElement $route */
182+
foreach ($routes as $route) {
183+
$routeUrl = $this->getRouteUrl($route);
184+
$routeMethod = $this->getRouteMethod($route);
185+
$routeAlias = $this->getRouteAlias($route);
186+
if ($routeUrl && $routeMethod && $routeAlias) {
187+
if (!isset($customRoutes[$routeAlias])) {
188+
$customRoutes[$routeAlias] = [];
189+
}
190+
$customRoutes[$routeAlias][$routeMethod] = $routeUrl;
191+
}
192+
}
193+
return $customRoutes;
194+
}
195+
196+
/**
197+
* @param \DOMElement $route
198+
* @return null|string
199+
*/
200+
private function getRouteUrl($route)
201+
{
202+
$url = $route->attributes->getNamedItem('url')->nodeValue;
203+
return mb_strlen((string) $url) === 0 ? null : $url;
204+
}
205+
206+
/**
207+
* @param \DOMElement $route
208+
* @return null|string
209+
*/
210+
private function getRouteAlias($route)
211+
{
212+
$alias = $route->attributes->getNamedItem('alias')->nodeValue;
213+
return mb_strlen((string) $alias) === 0 ? null : ltrim($alias, '/');
214+
}
215+
216+
/**
217+
* @param \DOMElement $route
218+
* @return null|string
219+
*/
220+
private function getRouteMethod($route)
221+
{
222+
$method = $route->attributes->getNamedItem('method')->nodeValue;
223+
$method = mb_strlen((string) $method) === 0 ? null : $method;
224+
return ($this->validateRouteMethod($method)) ? $method : null;
225+
}
226+
227+
/**
228+
* @param string $method
229+
* @return bool
230+
*/
231+
private function validateRouteMethod($method)
232+
{
233+
return in_array($method, $this->allowedRouteMethods);
234+
}
161235
}

app/code/Magento/WebapiAsync/Model/ServiceConfig/Reader.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class Reader extends \Magento\Framework\Config\Reader\Filesystem
2020
*/
2121
protected $_idAttributes = [
2222
'/services/service' => ['class', 'method'],
23+
'/services/route' => ['url', 'method']
2324
];
2425

2526
/**
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
declare(strict_types=1);
8+
9+
namespace Magento\WebapiAsync\Plugin;
10+
11+
use Magento\WebapiAsync\Model\ServiceConfig;
12+
use Magento\Webapi\Controller\PathProcessor;
13+
use Magento\Webapi\Controller\Rest;
14+
use Magento\Framework\App\RequestInterface;
15+
use Magento\WebapiAsync\Model\ServiceConfig\Converter;
16+
17+
class ControllerRest
18+
{
19+
/**
20+
* @var ServiceConfig
21+
*/
22+
private $serviceConfig;
23+
24+
/**
25+
* @var PathProcessor
26+
*/
27+
private $pathProcessor;
28+
29+
/**
30+
* ControllerRest constructor.
31+
*
32+
* @param ServiceConfig $serviceConfig
33+
* @param PathProcessor $pathProcessor
34+
*/
35+
public function __construct(
36+
ServiceConfig $serviceConfig,
37+
PathProcessor $pathProcessor
38+
) {
39+
$this->serviceConfig = $serviceConfig;
40+
$this->pathProcessor = $pathProcessor;
41+
}
42+
43+
/**
44+
* Check is current rest api route path in route customization config.
45+
* If that replaces $request route path by related endpoint,
46+
* @param Rest $subject
47+
* @param RequestInterface $request
48+
* @return array
49+
*/
50+
public function beforeDispatch(Rest $subject, RequestInterface $request)
51+
{
52+
$routeCustomizations = $this->serviceConfig->getServices()[Converter::KEY_ROUTES] ?? [];
53+
if ($routeCustomizations) {
54+
$originPath = $request->getPathInfo();
55+
$requestMethod = $request->getMethod();
56+
$routePath = ltrim($this->pathProcessor->process($originPath), '/');
57+
if (array_key_exists($routePath, $routeCustomizations)) {
58+
if (isset($routeCustomizations[$routePath][$requestMethod])) {
59+
$path = ltrim($routeCustomizations[$routePath][$requestMethod], '/');
60+
$request->setPathInfo(str_replace($routePath, $path, $originPath));
61+
}
62+
}
63+
}
64+
return [$request];
65+
}
66+
}

app/code/Magento/WebapiAsync/Plugin/ServiceMetadata.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,8 @@ private function getServiceVersions(string $serviceName)
129129
private function getSynchronousOnlyServiceMethods(\Magento\Webapi\Model\ServiceMetadata $serviceMetadata)
130130
{
131131
$synchronousOnlyServiceMethods = [];
132-
foreach ($this->serviceConfig->getServices() as $service => $serviceData) {
132+
$services = $this->serviceConfig->getServices()[Converter::KEY_SERVICES] ?? [];
133+
foreach ($services as $service => $serviceData) {
133134
if (!isset($serviceData[Converter::KEY_METHODS])) {
134135
continue;
135136
}

app/code/Magento/WebapiAsync/Test/Unit/Model/ServiceConfig/_files/Converter/webapi_async.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,9 @@
2222
],
2323
],
2424
],
25+
'routes' => [
26+
'asyncProducts' => ['POST' => 'async/V1/products'],
27+
'asyncBulkCmsBlocks' => ['POST' => 'async/bulk/V1/cmsBlock'],
28+
'asyncCustomers' => ['POST' => 'async/V1/customers']
29+
]
2530
];

app/code/Magento/WebapiAsync/Test/Unit/Model/ServiceConfig/_files/Converter/webapi_async.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,8 @@
1616
</service>
1717

1818
<service class="Magento\Customer\Api\CustomerRepositoryInterface" method="get" />
19+
20+
<route url="async/V1/products" method="POST" alias="asyncProducts"/>
21+
<route url="async/bulk/V1/cmsBlock" method="POST" alias="asyncBulkCmsBlocks"/>
22+
<route url="async/V1/customers" method="POST" alias="asyncCustomers"/>
1923
</services>

app/code/Magento/WebapiAsync/Test/Unit/Model/ServiceConfig/_files/Reader/webapi_async.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,9 @@
2222
],
2323
],
2424
],
25+
'routes' => [
26+
'asyncProducts' => ['POST' => 'async/bulk/V1/products'],
27+
'asyncBulkCmsPages' => ['POST' => 'async/bulk/V1/cmsPage'],
28+
'asyncCustomers' => ['POST' => 'async/V1/customers']
29+
]
2530
];

app/code/Magento/WebapiAsync/Test/Unit/Model/ServiceConfig/_files/Reader/webapi_async_1.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,7 @@
1515
<synchronousInvocationOnly />
1616
</service>
1717

18+
<route url="async/V1/products" method="POST" alias="asyncProducts"/>
19+
<route url="async/bulk/V1/cmsPage" method="POST" alias="asyncBulkCmsPages"/>
20+
<route url="async/V1/customers" method="POST" alias="/asyncCustomers"/>
1821
</services>

app/code/Magento/WebapiAsync/Test/Unit/Model/ServiceConfig/_files/Reader/webapi_async_2.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
<services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
99
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_WebapiAsync:etc/webapi_async.xsd">
1010
<service class="Magento\Customer\Api\CustomerRepositoryInterface" method="get" />
11-
11+
<route url="async/bulk/V1/products" method="POST" alias="asyncProducts"/>
1212
</services>

0 commit comments

Comments
 (0)